UNPKG

860 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2020, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
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, manyCopies) {
912 for (var i = arr.length; i >= 0; i--) {
913 if (arr[i] === ele) {
914 arr.splice(i, 1);
915
916 if (!manyCopies) {
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 is deprecated');
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 }
1737 } // End of neighbors update
1738
1739 } // End of main loop
1740 // If we've reached here, then we've not reached our goal
1741
1742
1743 return {
1744 found: false,
1745 distance: undefined,
1746 path: undefined,
1747 steps: steps
1748 };
1749 }
1750}; // elesfn
1751
1752var floydWarshallDefaults = defaults({
1753 weight: function weight(edge) {
1754 return 1;
1755 },
1756 directed: false
1757});
1758var elesfn$4 = {
1759 // Implemented from pseudocode from wikipedia
1760 floydWarshall: function floydWarshall(options) {
1761 var cy = this.cy();
1762
1763 var _floydWarshallDefault = floydWarshallDefaults(options),
1764 weight = _floydWarshallDefault.weight,
1765 directed = _floydWarshallDefault.directed;
1766
1767 var weightFn = weight;
1768
1769 var _this$byGroup = this.byGroup(),
1770 nodes = _this$byGroup.nodes,
1771 edges = _this$byGroup.edges;
1772
1773 var N = nodes.length;
1774 var Nsq = N * N;
1775
1776 var indexOf = function indexOf(node) {
1777 return nodes.indexOf(node);
1778 };
1779
1780 var atIndex = function atIndex(i) {
1781 return nodes[i];
1782 }; // Initialize distance matrix
1783
1784
1785 var dist = new Array(Nsq);
1786
1787 for (var n = 0; n < Nsq; n++) {
1788 var j = n % N;
1789 var i = (n - j) / N;
1790
1791 if (i === j) {
1792 dist[n] = 0;
1793 } else {
1794 dist[n] = Infinity;
1795 }
1796 } // Initialize matrix used for path reconstruction
1797 // Initialize distance matrix
1798
1799
1800 var next = new Array(Nsq);
1801 var edgeNext = new Array(Nsq); // Process edges
1802
1803 for (var _i = 0; _i < edges.length; _i++) {
1804 var edge = edges[_i];
1805 var src = edge.source()[0];
1806 var tgt = edge.target()[0];
1807
1808 if (src === tgt) {
1809 continue;
1810 } // exclude loops
1811
1812
1813 var s = indexOf(src);
1814 var t = indexOf(tgt);
1815 var st = s * N + t; // source to target index
1816
1817 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1818
1819
1820 if (dist[st] > _weight) {
1821 dist[st] = _weight;
1822 next[st] = t;
1823 edgeNext[st] = edge;
1824 } // If undirected graph, process 'reversed' edge
1825
1826
1827 if (!directed) {
1828 var ts = t * N + s; // target to source index
1829
1830 if (!directed && dist[ts] > _weight) {
1831 dist[ts] = _weight;
1832 next[ts] = s;
1833 edgeNext[ts] = edge;
1834 }
1835 }
1836 } // Main loop
1837
1838
1839 for (var k = 0; k < N; k++) {
1840 for (var _i2 = 0; _i2 < N; _i2++) {
1841 var ik = _i2 * N + k;
1842
1843 for (var _j = 0; _j < N; _j++) {
1844 var ij = _i2 * N + _j;
1845 var kj = k * N + _j;
1846
1847 if (dist[ik] + dist[kj] < dist[ij]) {
1848 dist[ij] = dist[ik] + dist[kj];
1849 next[ij] = next[ik];
1850 }
1851 }
1852 }
1853 }
1854
1855 var getArgEle = function getArgEle(ele) {
1856 return (string(ele) ? cy.filter(ele) : ele)[0];
1857 };
1858
1859 var indexOfArgEle = function indexOfArgEle(ele) {
1860 return indexOf(getArgEle(ele));
1861 };
1862
1863 var res = {
1864 distance: function distance(from, to) {
1865 var i = indexOfArgEle(from);
1866 var j = indexOfArgEle(to);
1867 return dist[i * N + j];
1868 },
1869 path: function path(from, to) {
1870 var i = indexOfArgEle(from);
1871 var j = indexOfArgEle(to);
1872 var fromNode = atIndex(i);
1873
1874 if (i === j) {
1875 return fromNode.collection();
1876 }
1877
1878 if (next[i * N + j] == null) {
1879 return cy.collection();
1880 }
1881
1882 var path = cy.collection();
1883 var prev = i;
1884 var edge;
1885 path.merge(fromNode);
1886
1887 while (i !== j) {
1888 prev = i;
1889 i = next[i * N + j];
1890 edge = edgeNext[prev * N + i];
1891 path.merge(edge);
1892 path.merge(atIndex(i));
1893 }
1894
1895 return path;
1896 }
1897 };
1898 return res;
1899 } // floydWarshall
1900
1901}; // elesfn
1902
1903var bellmanFordDefaults = defaults({
1904 weight: function weight(edge) {
1905 return 1;
1906 },
1907 directed: false,
1908 root: null
1909});
1910var elesfn$5 = {
1911 // Implemented from pseudocode from wikipedia
1912 bellmanFord: function bellmanFord(options) {
1913 var _this = this;
1914
1915 var _bellmanFordDefaults = bellmanFordDefaults(options),
1916 weight = _bellmanFordDefaults.weight,
1917 directed = _bellmanFordDefaults.directed,
1918 root = _bellmanFordDefaults.root;
1919
1920 var weightFn = weight;
1921 var eles = this;
1922 var cy = this.cy();
1923
1924 var _this$byGroup = this.byGroup(),
1925 edges = _this$byGroup.edges,
1926 nodes = _this$byGroup.nodes;
1927
1928 var numNodes = nodes.length;
1929 var infoMap = new Map$1();
1930 var hasNegativeWeightCycle = false;
1931 var negativeWeightCycles = [];
1932 root = cy.collection(root)[0]; // in case selector passed
1933
1934 edges.unmergeBy(function (edge) {
1935 return edge.isLoop();
1936 });
1937 var numEdges = edges.length;
1938
1939 var getInfo = function getInfo(node) {
1940 var obj = infoMap.get(node.id());
1941
1942 if (!obj) {
1943 obj = {};
1944 infoMap.set(node.id(), obj);
1945 }
1946
1947 return obj;
1948 };
1949
1950 var getNodeFromTo = function getNodeFromTo(to) {
1951 return (string(to) ? cy.$(to) : to)[0];
1952 };
1953
1954 var distanceTo = function distanceTo(to) {
1955 return getInfo(getNodeFromTo(to)).dist;
1956 };
1957
1958 var pathTo = function pathTo(to) {
1959 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1960 var end = getNodeFromTo(to);
1961 var path = [];
1962 var node = end;
1963
1964 for (;;) {
1965 if (node == null) {
1966 return _this.spawn();
1967 }
1968
1969 var _getInfo = getInfo(node),
1970 edge = _getInfo.edge,
1971 pred = _getInfo.pred;
1972
1973 path.unshift(node[0]);
1974
1975 if (node.same(thisStart) && path.length > 0) {
1976 break;
1977 }
1978
1979 if (edge != null) {
1980 path.unshift(edge);
1981 }
1982
1983 node = pred;
1984 }
1985
1986 return eles.spawn(path);
1987 }; // Initializations { dist, pred, edge }
1988
1989
1990 for (var i = 0; i < numNodes; i++) {
1991 var node = nodes[i];
1992 var info = getInfo(node);
1993
1994 if (node.same(root)) {
1995 info.dist = 0;
1996 } else {
1997 info.dist = Infinity;
1998 }
1999
2000 info.pred = null;
2001 info.edge = null;
2002 } // Edges relaxation
2003
2004
2005 var replacedEdge = false;
2006
2007 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2008 var dist = info1.dist + weight;
2009
2010 if (dist < info2.dist && !edge.same(info1.edge)) {
2011 info2.dist = dist;
2012 info2.pred = node1;
2013 info2.edge = edge;
2014 replacedEdge = true;
2015 }
2016 };
2017
2018 for (var _i = 1; _i < numNodes; _i++) {
2019 replacedEdge = false;
2020
2021 for (var e = 0; e < numEdges; e++) {
2022 var edge = edges[e];
2023 var src = edge.source();
2024 var tgt = edge.target();
2025
2026 var _weight = weightFn(edge);
2027
2028 var srcInfo = getInfo(src);
2029 var tgtInfo = getInfo(tgt);
2030 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2031
2032 if (!directed) {
2033 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2034 }
2035 }
2036
2037 if (!replacedEdge) {
2038 break;
2039 }
2040 }
2041
2042 if (replacedEdge) {
2043 // Check for negative weight cycles
2044 for (var _e = 0; _e < numEdges; _e++) {
2045 var _edge = edges[_e];
2046
2047 var _src = _edge.source();
2048
2049 var _tgt = _edge.target();
2050
2051 var _weight2 = weightFn(_edge);
2052
2053 var srcDist = getInfo(_src).dist;
2054 var tgtDist = getInfo(_tgt).dist;
2055
2056 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2057 warn('Graph contains a negative weight cycle for Bellman-Ford');
2058 hasNegativeWeightCycle = true;
2059 break;
2060 }
2061 }
2062 }
2063
2064 return {
2065 distanceTo: distanceTo,
2066 pathTo: pathTo,
2067 hasNegativeWeightCycle: hasNegativeWeightCycle,
2068 negativeWeightCycles: negativeWeightCycles
2069 };
2070 } // bellmanFord
2071
2072}; // elesfn
2073
2074var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2075// Updates the remaining edge lists
2076// Receives as a paramater the edge which causes the collapse
2077
2078var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2079 if (remainingEdges.length === 0) {
2080 error("Karger-Stein must be run on a connected (sub)graph");
2081 }
2082
2083 var edgeInfo = remainingEdges[edgeIndex];
2084 var sourceIn = edgeInfo[1];
2085 var targetIn = edgeInfo[2];
2086 var partition1 = nodeMap[sourceIn];
2087 var partition2 = nodeMap[targetIn];
2088 var newEdges = remainingEdges; // re-use array
2089 // Delete all edges between partition1 and partition2
2090
2091 for (var i = newEdges.length - 1; i >= 0; i--) {
2092 var edge = newEdges[i];
2093 var src = edge[1];
2094 var tgt = edge[2];
2095
2096 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2097 newEdges.splice(i, 1);
2098 }
2099 } // All edges pointing to partition2 should now point to partition1
2100
2101
2102 for (var _i = 0; _i < newEdges.length; _i++) {
2103 var _edge = newEdges[_i];
2104
2105 if (_edge[1] === partition2) {
2106 // Check source
2107 newEdges[_i] = _edge.slice(); // copy
2108
2109 newEdges[_i][1] = partition1;
2110 } else if (_edge[2] === partition2) {
2111 // Check target
2112 newEdges[_i] = _edge.slice(); // copy
2113
2114 newEdges[_i][2] = partition1;
2115 }
2116 } // Move all nodes from partition2 to partition1
2117
2118
2119 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2120 if (nodeMap[_i2] === partition2) {
2121 nodeMap[_i2] = partition1;
2122 }
2123 }
2124
2125 return newEdges;
2126}; // Contracts a graph until we reach a certain number of meta nodes
2127
2128
2129var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2130 while (size > sizeLimit) {
2131 // Choose an edge randomly
2132 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2133
2134 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2135 size--;
2136 }
2137
2138 return remainingEdges;
2139};
2140
2141var elesfn$6 = {
2142 // Computes the minimum cut of an undirected graph
2143 // Returns the correct answer with high probability
2144 kargerStein: function kargerStein() {
2145 var _this = this;
2146
2147 var _this$byGroup = this.byGroup(),
2148 nodes = _this$byGroup.nodes,
2149 edges = _this$byGroup.edges;
2150
2151 edges.unmergeBy(function (edge) {
2152 return edge.isLoop();
2153 });
2154 var numNodes = nodes.length;
2155 var numEdges = edges.length;
2156 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2157 var stopSize = Math.floor(numNodes / sqrt2);
2158
2159 if (numNodes < 2) {
2160 error('At least 2 nodes are required for Karger-Stein algorithm');
2161 return undefined;
2162 } // Now store edge destination as indexes
2163 // Format for each edge (edge index, source node index, target node index)
2164
2165
2166 var edgeIndexes = [];
2167
2168 for (var i = 0; i < numEdges; i++) {
2169 var e = edges[i];
2170 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2171 } // We will store the best cut found here
2172
2173
2174 var minCutSize = Infinity;
2175 var minCutEdgeIndexes = [];
2176 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2177
2178 var metaNodeMap = new Array(numNodes);
2179 var metaNodeMap2 = new Array(numNodes);
2180
2181 var copyNodesMap = function copyNodesMap(from, to) {
2182 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2183 to[_i3] = from[_i3];
2184 }
2185 }; // Main loop
2186
2187
2188 for (var iter = 0; iter <= numIter; iter++) {
2189 // Reset meta node partition
2190 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2191 metaNodeMap[_i4] = _i4;
2192 } // Contract until stop point (stopSize nodes)
2193
2194
2195 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2196 var edgesState2 = edgesState.slice(); // copy
2197 // Create a copy of the colapsed nodes state
2198
2199 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2200
2201 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2202 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2203
2204 if (res1.length <= res2.length && res1.length < minCutSize) {
2205 minCutSize = res1.length;
2206 minCutEdgeIndexes = res1;
2207 copyNodesMap(metaNodeMap, minCutNodeMap);
2208 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2209 minCutSize = res2.length;
2210 minCutEdgeIndexes = res2;
2211 copyNodesMap(metaNodeMap2, minCutNodeMap);
2212 }
2213 } // end of main loop
2214 // Construct result
2215
2216
2217 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2218 return edges[e[0]];
2219 }));
2220 var partition1 = this.spawn();
2221 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2222
2223 var witnessNodePartition = minCutNodeMap[0];
2224
2225 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2226 var partitionId = minCutNodeMap[_i5];
2227 var node = nodes[_i5];
2228
2229 if (partitionId === witnessNodePartition) {
2230 partition1.merge(node);
2231 } else {
2232 partition2.merge(node);
2233 }
2234 } // construct components corresponding to each disjoint subset of nodes
2235
2236
2237 var constructComponent = function constructComponent(subset) {
2238 var component = _this.spawn();
2239
2240 subset.forEach(function (node) {
2241 component.merge(node);
2242 node.connectedEdges().forEach(function (edge) {
2243 // ensure edge is within calling collection and edge is not in cut
2244 if (_this.contains(edge) && !cut.contains(edge)) {
2245 component.merge(edge);
2246 }
2247 });
2248 });
2249 return component;
2250 };
2251
2252 var components = [constructComponent(partition1), constructComponent(partition2)];
2253 var ret = {
2254 cut: cut,
2255 components: components,
2256 // n.b. partitions are included to be compatible with the old api spec
2257 // (could be removed in a future major version)
2258 partition1: partition1,
2259 partition2: partition2
2260 };
2261 return ret;
2262 }
2263}; // elesfn
2264
2265var copyPosition = function copyPosition(p) {
2266 return {
2267 x: p.x,
2268 y: p.y
2269 };
2270};
2271var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2272 return {
2273 x: p.x * zoom + pan.x,
2274 y: p.y * zoom + pan.y
2275 };
2276};
2277var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2278 return {
2279 x: (p.x - pan.x) / zoom,
2280 y: (p.y - pan.y) / zoom
2281 };
2282};
2283var array2point = function array2point(arr) {
2284 return {
2285 x: arr[0],
2286 y: arr[1]
2287 };
2288};
2289var min = function min(arr) {
2290 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2291 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2292 var min = Infinity;
2293
2294 for (var i = begin; i < end; i++) {
2295 var val = arr[i];
2296
2297 if (isFinite(val)) {
2298 min = Math.min(val, min);
2299 }
2300 }
2301
2302 return min;
2303};
2304var max = function max(arr) {
2305 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2306 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2307 var max = -Infinity;
2308
2309 for (var i = begin; i < end; i++) {
2310 var val = arr[i];
2311
2312 if (isFinite(val)) {
2313 max = Math.max(val, max);
2314 }
2315 }
2316
2317 return max;
2318};
2319var mean = function mean(arr) {
2320 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2321 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2322 var total = 0;
2323 var n = 0;
2324
2325 for (var i = begin; i < end; i++) {
2326 var val = arr[i];
2327
2328 if (isFinite(val)) {
2329 total += val;
2330 n++;
2331 }
2332 }
2333
2334 return total / n;
2335};
2336var median = function median(arr) {
2337 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2338 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2339 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2340 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2341 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2342
2343 if (copy) {
2344 arr = arr.slice(begin, end);
2345 } else {
2346 if (end < arr.length) {
2347 arr.splice(end, arr.length - end);
2348 }
2349
2350 if (begin > 0) {
2351 arr.splice(0, begin);
2352 }
2353 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2354
2355
2356 var off = 0; // offset from non-finite values
2357
2358 for (var i = arr.length - 1; i >= 0; i--) {
2359 var v = arr[i];
2360
2361 if (includeHoles) {
2362 if (!isFinite(v)) {
2363 arr[i] = -Infinity;
2364 off++;
2365 }
2366 } else {
2367 // just remove it if we don't want to consider holes
2368 arr.splice(i, 1);
2369 }
2370 }
2371
2372 if (sort) {
2373 arr.sort(function (a, b) {
2374 return a - b;
2375 }); // requires copy = true if you don't want to change the orig
2376 }
2377
2378 var len = arr.length;
2379 var mid = Math.floor(len / 2);
2380
2381 if (len % 2 !== 0) {
2382 return arr[mid + 1 + off];
2383 } else {
2384 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2385 }
2386};
2387var deg2rad = function deg2rad(deg) {
2388 return Math.PI * deg / 180;
2389};
2390var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2391 return Math.atan2(dispY, dispX) - Math.PI / 2;
2392};
2393var log2 = Math.log2 || function (n) {
2394 return Math.log(n) / Math.log(2);
2395};
2396var signum = function signum(x) {
2397 if (x > 0) {
2398 return 1;
2399 } else if (x < 0) {
2400 return -1;
2401 } else {
2402 return 0;
2403 }
2404};
2405var dist = function dist(p1, p2) {
2406 return Math.sqrt(sqdist(p1, p2));
2407};
2408var sqdist = function sqdist(p1, p2) {
2409 var dx = p2.x - p1.x;
2410 var dy = p2.y - p1.y;
2411 return dx * dx + dy * dy;
2412};
2413var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2414 var length = v.length; // First, get sum of all elements
2415
2416 var total = 0;
2417
2418 for (var i = 0; i < length; i++) {
2419 total += v[i];
2420 } // Now, divide each by the sum of all elements
2421
2422
2423 for (var _i = 0; _i < length; _i++) {
2424 v[_i] = v[_i] / total;
2425 }
2426
2427 return v;
2428};
2429
2430var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2431 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2432};
2433var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2434 return {
2435 x: qbezierAt(p0.x, p1.x, p2.x, t),
2436 y: qbezierAt(p0.y, p1.y, p2.y, t)
2437 };
2438};
2439var lineAt = function lineAt(p0, p1, t, d) {
2440 var vec = {
2441 x: p1.x - p0.x,
2442 y: p1.y - p0.y
2443 };
2444 var vecDist = dist(p0, p1);
2445 var normVec = {
2446 x: vec.x / vecDist,
2447 y: vec.y / vecDist
2448 };
2449 t = t == null ? 0 : t;
2450 d = d != null ? d : t * vecDist;
2451 return {
2452 x: p0.x + normVec.x * d,
2453 y: p0.y + normVec.y * d
2454 };
2455};
2456var bound = function bound(min, val, max) {
2457 return Math.max(min, Math.min(max, val));
2458}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2459
2460var makeBoundingBox = function makeBoundingBox(bb) {
2461 if (bb == null) {
2462 return {
2463 x1: Infinity,
2464 y1: Infinity,
2465 x2: -Infinity,
2466 y2: -Infinity,
2467 w: 0,
2468 h: 0
2469 };
2470 } else if (bb.x1 != null && bb.y1 != null) {
2471 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2472 return {
2473 x1: bb.x1,
2474 y1: bb.y1,
2475 x2: bb.x2,
2476 y2: bb.y2,
2477 w: bb.x2 - bb.x1,
2478 h: bb.y2 - bb.y1
2479 };
2480 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2481 return {
2482 x1: bb.x1,
2483 y1: bb.y1,
2484 x2: bb.x1 + bb.w,
2485 y2: bb.y1 + bb.h,
2486 w: bb.w,
2487 h: bb.h
2488 };
2489 }
2490 }
2491};
2492var copyBoundingBox = function copyBoundingBox(bb) {
2493 return {
2494 x1: bb.x1,
2495 x2: bb.x2,
2496 w: bb.w,
2497 y1: bb.y1,
2498 y2: bb.y2,
2499 h: bb.h
2500 };
2501};
2502var clearBoundingBox = function clearBoundingBox(bb) {
2503 bb.x1 = Infinity;
2504 bb.y1 = Infinity;
2505 bb.x2 = -Infinity;
2506 bb.y2 = -Infinity;
2507 bb.w = 0;
2508 bb.h = 0;
2509};
2510var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2511 // update bb1 with bb2 bounds
2512 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2513 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2514 bb1.w = bb1.x2 - bb1.x1;
2515 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2516 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2517 bb1.h = bb1.y2 - bb1.y1;
2518};
2519var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2520 bb.x1 = Math.min(bb.x1, x);
2521 bb.x2 = Math.max(bb.x2, x);
2522 bb.w = bb.x2 - bb.x1;
2523 bb.y1 = Math.min(bb.y1, y);
2524 bb.y2 = Math.max(bb.y2, y);
2525 bb.h = bb.y2 - bb.y1;
2526};
2527var expandBoundingBox = function expandBoundingBox(bb) {
2528 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2529 bb.x1 -= padding;
2530 bb.x2 += padding;
2531 bb.y1 -= padding;
2532 bb.y2 += padding;
2533 bb.w = bb.x2 - bb.x1;
2534 bb.h = bb.y2 - bb.y1;
2535 return bb;
2536};
2537var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2538 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2539 var top, right, bottom, left;
2540
2541 if (padding.length === 1) {
2542 top = right = bottom = left = padding[0];
2543 } else if (padding.length === 2) {
2544 top = bottom = padding[0];
2545 left = right = padding[1];
2546 } else if (padding.length === 4) {
2547 var _padding = _slicedToArray(padding, 4);
2548
2549 top = _padding[0];
2550 right = _padding[1];
2551 bottom = _padding[2];
2552 left = _padding[3];
2553 }
2554
2555 bb.x1 -= left;
2556 bb.x2 += right;
2557 bb.y1 -= top;
2558 bb.y2 += bottom;
2559 bb.w = bb.x2 - bb.x1;
2560 bb.h = bb.y2 - bb.y1;
2561 return bb;
2562};
2563
2564var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2565 bb1.x1 = bb2.x1;
2566 bb1.y1 = bb2.y1;
2567 bb1.x2 = bb2.x2;
2568 bb1.y2 = bb2.y2;
2569 bb1.w = bb1.x2 - bb1.x1;
2570 bb1.h = bb1.y2 - bb1.y1;
2571};
2572var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
2573 bb.x1 += delta.x;
2574 bb.x2 += delta.x;
2575 bb.y1 += delta.y;
2576 bb.y2 += delta.y;
2577};
2578var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2579 // case: one bb to right of other
2580 if (bb1.x1 > bb2.x2) {
2581 return false;
2582 }
2583
2584 if (bb2.x1 > bb1.x2) {
2585 return false;
2586 } // case: one bb to left of other
2587
2588
2589 if (bb1.x2 < bb2.x1) {
2590 return false;
2591 }
2592
2593 if (bb2.x2 < bb1.x1) {
2594 return false;
2595 } // case: one bb above other
2596
2597
2598 if (bb1.y2 < bb2.y1) {
2599 return false;
2600 }
2601
2602 if (bb2.y2 < bb1.y1) {
2603 return false;
2604 } // case: one bb below other
2605
2606
2607 if (bb1.y1 > bb2.y2) {
2608 return false;
2609 }
2610
2611 if (bb2.y1 > bb1.y2) {
2612 return false;
2613 } // otherwise, must have some overlap
2614
2615
2616 return true;
2617};
2618var inBoundingBox = function inBoundingBox(bb, x, y) {
2619 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2620};
2621var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2622 return inBoundingBox(bb, pt.x, pt.y);
2623};
2624var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2625 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2626};
2627var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2628 var cornerRadius = getRoundRectangleRadius(width, height);
2629 var halfWidth = width / 2;
2630 var halfHeight = height / 2; // Check intersections with straight line segments
2631
2632 var straightLineIntersections; // Top segment, left to right
2633
2634 {
2635 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2636 var topStartY = nodeY - halfHeight - padding;
2637 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2638 var topEndY = topStartY;
2639 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2640
2641 if (straightLineIntersections.length > 0) {
2642 return straightLineIntersections;
2643 }
2644 } // Right segment, top to bottom
2645
2646 {
2647 var rightStartX = nodeX + halfWidth + padding;
2648 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2649 var rightEndX = rightStartX;
2650 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2651 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2652
2653 if (straightLineIntersections.length > 0) {
2654 return straightLineIntersections;
2655 }
2656 } // Bottom segment, left to right
2657
2658 {
2659 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2660 var bottomStartY = nodeY + halfHeight + padding;
2661 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2662 var bottomEndY = bottomStartY;
2663 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2664
2665 if (straightLineIntersections.length > 0) {
2666 return straightLineIntersections;
2667 }
2668 } // Left segment, top to bottom
2669
2670 {
2671 var leftStartX = nodeX - halfWidth - padding;
2672 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2673 var leftEndX = leftStartX;
2674 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2675 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2676
2677 if (straightLineIntersections.length > 0) {
2678 return straightLineIntersections;
2679 }
2680 } // Check intersections with arc segments
2681
2682 var arcIntersections; // Top Left
2683
2684 {
2685 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2686 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2687 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2688
2689 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2690 return [arcIntersections[0], arcIntersections[1]];
2691 }
2692 } // Top Right
2693
2694 {
2695 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2696 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2697 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2698
2699 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2700 return [arcIntersections[0], arcIntersections[1]];
2701 }
2702 } // Bottom Right
2703
2704 {
2705 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2706 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2707 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2708
2709 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2710 return [arcIntersections[0], arcIntersections[1]];
2711 }
2712 } // Bottom Left
2713
2714 {
2715 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2716 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2717 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2718
2719 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2720 return [arcIntersections[0], arcIntersections[1]];
2721 }
2722 }
2723 return []; // if nothing
2724};
2725var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2726 var t = tolerance;
2727 var x1 = Math.min(lx1, lx2);
2728 var x2 = Math.max(lx1, lx2);
2729 var y1 = Math.min(ly1, ly2);
2730 var y2 = Math.max(ly1, ly2);
2731 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2732};
2733var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2734 var bb = {
2735 x1: Math.min(x1, x3, x2) - tolerance,
2736 x2: Math.max(x1, x3, x2) + tolerance,
2737 y1: Math.min(y1, y3, y2) - tolerance,
2738 y2: Math.max(y1, y3, y2) + tolerance
2739 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2740
2741 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2742 // console.log('bezier out of rough bb')
2743 return false;
2744 } else {
2745 // console.log('do more expensive check');
2746 return true;
2747 }
2748};
2749var solveQuadratic = function solveQuadratic(a, b, c, val) {
2750 c -= val;
2751 var r = b * b - 4 * a * c;
2752
2753 if (r < 0) {
2754 return [];
2755 }
2756
2757 var sqrtR = Math.sqrt(r);
2758 var denom = 2 * a;
2759 var root1 = (-b + sqrtR) / denom;
2760 var root2 = (-b - sqrtR) / denom;
2761 return [root1, root2];
2762};
2763var solveCubic = function solveCubic(a, b, c, d, result) {
2764 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2765 // r is the real component, i is the imaginary component
2766 // An implementation of the Cardano method from the year 1545
2767 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2768 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2769
2770 if (a === 0) {
2771 a = epsilon;
2772 }
2773
2774 b /= a;
2775 c /= a;
2776 d /= a;
2777 var discriminant, q, r, dum1, s, t, term1, r13;
2778 q = (3.0 * c - b * b) / 9.0;
2779 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2780 r /= 54.0;
2781 discriminant = q * q * q + r * r;
2782 result[1] = 0;
2783 term1 = b / 3.0;
2784
2785 if (discriminant > 0) {
2786 s = r + Math.sqrt(discriminant);
2787 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2788 t = r - Math.sqrt(discriminant);
2789 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2790 result[0] = -term1 + s + t;
2791 term1 += (s + t) / 2.0;
2792 result[4] = result[2] = -term1;
2793 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2794 result[3] = term1;
2795 result[5] = -term1;
2796 return;
2797 }
2798
2799 result[5] = result[3] = 0;
2800
2801 if (discriminant === 0) {
2802 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2803 result[0] = -term1 + 2.0 * r13;
2804 result[4] = result[2] = -(r13 + term1);
2805 return;
2806 }
2807
2808 q = -q;
2809 dum1 = q * q * q;
2810 dum1 = Math.acos(r / Math.sqrt(dum1));
2811 r13 = 2.0 * Math.sqrt(q);
2812 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2813 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2814 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2815 return;
2816};
2817var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2818 // Find minimum distance by using the minimum of the distance
2819 // function between the given point and the curve
2820 // This gives the coefficients of the resulting cubic equation
2821 // whose roots tell us where a possible minimum is
2822 // (Coefficients are divided by 4)
2823 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;
2824 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;
2825 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;
2826 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);
2827
2828 var roots = []; // Use the cubic solving algorithm
2829
2830 solveCubic(a, b, c, d, roots);
2831 var zeroThreshold = 0.0000001;
2832 var params = [];
2833
2834 for (var index = 0; index < 6; index += 2) {
2835 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2836 params.push(roots[index]);
2837 }
2838 }
2839
2840 params.push(1.0);
2841 params.push(0.0);
2842 var minDistanceSquared = -1;
2843 var curX, curY, distSquared;
2844
2845 for (var i = 0; i < params.length; i++) {
2846 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2847 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2848 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2849
2850 if (minDistanceSquared >= 0) {
2851 if (distSquared < minDistanceSquared) {
2852 minDistanceSquared = distSquared;
2853 }
2854 } else {
2855 minDistanceSquared = distSquared;
2856 }
2857 }
2858
2859 return minDistanceSquared;
2860};
2861var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2862 var offset = [x - x1, y - y1];
2863 var line = [x2 - x1, y2 - y1];
2864 var lineSq = line[0] * line[0] + line[1] * line[1];
2865 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2866 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2867 var adjSq = dotProduct * dotProduct / lineSq;
2868
2869 if (dotProduct < 0) {
2870 return hypSq;
2871 }
2872
2873 if (adjSq > lineSq) {
2874 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2875 }
2876
2877 return hypSq - adjSq;
2878};
2879var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2880 var x1, y1, x2, y2;
2881 var y3; // Intersect with vertical line through (x, y)
2882
2883 var up = 0; // let down = 0;
2884
2885 for (var i = 0; i < points.length / 2; i++) {
2886 x1 = points[i * 2];
2887 y1 = points[i * 2 + 1];
2888
2889 if (i + 1 < points.length / 2) {
2890 x2 = points[(i + 1) * 2];
2891 y2 = points[(i + 1) * 2 + 1];
2892 } else {
2893 x2 = points[(i + 1 - points.length / 2) * 2];
2894 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2895 }
2896
2897 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2898 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2899
2900 if (y3 > y) {
2901 up++;
2902 } // if( y3 < y ){
2903 // down++;
2904 // }
2905
2906 } else {
2907 continue;
2908 }
2909 }
2910
2911 if (up % 2 === 0) {
2912 return false;
2913 } else {
2914 return true;
2915 }
2916};
2917var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2918 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2919
2920 var angle;
2921
2922 if (direction[0] != null) {
2923 angle = Math.atan(direction[1] / direction[0]);
2924
2925 if (direction[0] < 0) {
2926 angle = angle + Math.PI / 2;
2927 } else {
2928 angle = -angle - Math.PI / 2;
2929 }
2930 } else {
2931 angle = direction;
2932 }
2933
2934 var cos = Math.cos(-angle);
2935 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2936
2937 for (var i = 0; i < transformedPoints.length / 2; i++) {
2938 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2939 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2940 transformedPoints[i * 2] += centerX;
2941 transformedPoints[i * 2 + 1] += centerY;
2942 }
2943
2944 var points;
2945
2946 if (padding > 0) {
2947 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2948 points = joinLines(expandedLineSet);
2949 } else {
2950 points = transformedPoints;
2951 }
2952
2953 return pointInsidePolygonPoints(x, y, points);
2954};
2955var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
2956 var cutPolygonPoints = new Array(basePoints.length);
2957 var halfW = width / 2;
2958 var halfH = height / 2;
2959 var cornerRadius = getRoundPolygonRadius(width, height);
2960 var squaredCornerRadius = cornerRadius * cornerRadius;
2961
2962 for (var i = 0; i < basePoints.length / 4; i++) {
2963 var sourceUv = void 0,
2964 destUv = void 0;
2965
2966 if (i === 0) {
2967 sourceUv = basePoints.length - 2;
2968 } else {
2969 sourceUv = i * 4 - 2;
2970 }
2971
2972 destUv = i * 4 + 2;
2973 var px = centerX + halfW * basePoints[i * 4];
2974 var py = centerY + halfH * basePoints[i * 4 + 1];
2975 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
2976 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
2977 var cp0x = px - offset * basePoints[sourceUv];
2978 var cp0y = py - offset * basePoints[sourceUv + 1];
2979 var cp1x = px + offset * basePoints[destUv];
2980 var cp1y = py + offset * basePoints[destUv + 1];
2981 cutPolygonPoints[i * 4] = cp0x;
2982 cutPolygonPoints[i * 4 + 1] = cp0y;
2983 cutPolygonPoints[i * 4 + 2] = cp1x;
2984 cutPolygonPoints[i * 4 + 3] = cp1y;
2985 var orthx = basePoints[sourceUv + 1];
2986 var orthy = -basePoints[sourceUv];
2987 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
2988
2989 if (cosAlpha < 0) {
2990 orthx *= -1;
2991 orthy *= -1;
2992 }
2993
2994 var cx = cp0x + orthx * cornerRadius;
2995 var cy = cp0y + orthy * cornerRadius;
2996 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
2997
2998 if (squaredDistance <= squaredCornerRadius) {
2999 return true;
3000 }
3001 }
3002
3003 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3004};
3005var joinLines = function joinLines(lineSet) {
3006 var vertices = new Array(lineSet.length / 2);
3007 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3008 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3009
3010 for (var i = 0; i < lineSet.length / 4; i++) {
3011 currentLineStartX = lineSet[i * 4];
3012 currentLineStartY = lineSet[i * 4 + 1];
3013 currentLineEndX = lineSet[i * 4 + 2];
3014 currentLineEndY = lineSet[i * 4 + 3];
3015
3016 if (i < lineSet.length / 4 - 1) {
3017 nextLineStartX = lineSet[(i + 1) * 4];
3018 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3019 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3020 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3021 } else {
3022 nextLineStartX = lineSet[0];
3023 nextLineStartY = lineSet[1];
3024 nextLineEndX = lineSet[2];
3025 nextLineEndY = lineSet[3];
3026 }
3027
3028 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3029 vertices[i * 2] = intersection[0];
3030 vertices[i * 2 + 1] = intersection[1];
3031 }
3032
3033 return vertices;
3034};
3035var expandPolygon = function expandPolygon(points, pad) {
3036 var expandedLineSet = new Array(points.length * 2);
3037 var currentPointX, currentPointY, nextPointX, nextPointY;
3038
3039 for (var i = 0; i < points.length / 2; i++) {
3040 currentPointX = points[i * 2];
3041 currentPointY = points[i * 2 + 1];
3042
3043 if (i < points.length / 2 - 1) {
3044 nextPointX = points[(i + 1) * 2];
3045 nextPointY = points[(i + 1) * 2 + 1];
3046 } else {
3047 nextPointX = points[0];
3048 nextPointY = points[1];
3049 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3050 // Assume CCW polygon winding
3051
3052
3053 var offsetX = nextPointY - currentPointY;
3054 var offsetY = -(nextPointX - currentPointX); // Normalize
3055
3056 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3057 var normalizedOffsetX = offsetX / offsetLength;
3058 var normalizedOffsetY = offsetY / offsetLength;
3059 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3060 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3061 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3062 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3063 }
3064
3065 return expandedLineSet;
3066};
3067var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3068 var dispX = centerX - x;
3069 var dispY = centerY - y;
3070 dispX /= ellipseWradius;
3071 dispY /= ellipseHradius;
3072 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3073 var newLength = len - 1;
3074
3075 if (newLength < 0) {
3076 return [];
3077 }
3078
3079 var lenProportion = newLength / len;
3080 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3081};
3082var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3083 x -= centerX;
3084 y -= centerY;
3085 x /= width / 2 + padding;
3086 y /= height / 2 + padding;
3087 return x * x + y * y <= 1;
3088}; // Returns intersections of increasing distance from line's start point
3089
3090var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3091 // Calculate d, direction vector of line
3092 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3093
3094 var f = [x1 - centerX, y1 - centerY];
3095 var a = d[0] * d[0] + d[1] * d[1];
3096 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3097 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3098 var discriminant = b * b - 4 * a * c;
3099
3100 if (discriminant < 0) {
3101 return [];
3102 }
3103
3104 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3105 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3106 var tMin = Math.min(t1, t2);
3107 var tMax = Math.max(t1, t2);
3108 var inRangeParams = [];
3109
3110 if (tMin >= 0 && tMin <= 1) {
3111 inRangeParams.push(tMin);
3112 }
3113
3114 if (tMax >= 0 && tMax <= 1) {
3115 inRangeParams.push(tMax);
3116 }
3117
3118 if (inRangeParams.length === 0) {
3119 return [];
3120 }
3121
3122 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3123 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3124
3125 if (inRangeParams.length > 1) {
3126 if (inRangeParams[0] == inRangeParams[1]) {
3127 return [nearIntersectionX, nearIntersectionY];
3128 } else {
3129 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3130 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3131 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3132 }
3133 } else {
3134 return [nearIntersectionX, nearIntersectionY];
3135 }
3136};
3137var midOfThree = function midOfThree(a, b, c) {
3138 if (b <= a && a <= c || c <= a && a <= b) {
3139 return a;
3140 } else if (a <= b && b <= c || c <= b && b <= a) {
3141 return b;
3142 } else {
3143 return c;
3144 }
3145}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3146
3147var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3148 var dx13 = x1 - x3;
3149 var dx21 = x2 - x1;
3150 var dx43 = x4 - x3;
3151 var dy13 = y1 - y3;
3152 var dy21 = y2 - y1;
3153 var dy43 = y4 - y3;
3154 var ua_t = dx43 * dy13 - dy43 * dx13;
3155 var ub_t = dx21 * dy13 - dy21 * dx13;
3156 var u_b = dy43 * dx21 - dx43 * dy21;
3157
3158 if (u_b !== 0) {
3159 var ua = ua_t / u_b;
3160 var ub = ub_t / u_b;
3161 var flptThreshold = 0.001;
3162
3163 var _min = 0 - flptThreshold;
3164
3165 var _max = 1 + flptThreshold;
3166
3167 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3168 return [x1 + ua * dx21, y1 + ua * dy21];
3169 } else {
3170 if (!infiniteLines) {
3171 return [];
3172 } else {
3173 return [x1 + ua * dx21, y1 + ua * dy21];
3174 }
3175 }
3176 } else {
3177 if (ua_t === 0 || ub_t === 0) {
3178 // Parallel, coincident lines. Check if overlap
3179 // Check endpoint of second line
3180 if (midOfThree(x1, x2, x4) === x4) {
3181 return [x4, y4];
3182 } // Check start point of second line
3183
3184
3185 if (midOfThree(x1, x2, x3) === x3) {
3186 return [x3, y3];
3187 } // Endpoint of first line
3188
3189
3190 if (midOfThree(x3, x4, x2) === x2) {
3191 return [x2, y2];
3192 }
3193
3194 return [];
3195 } else {
3196 // Parallel, non-coincident
3197 return [];
3198 }
3199 }
3200}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3201// intersect a node polygon (pts transformed)
3202//
3203// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3204// intersect the points (no transform)
3205
3206var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3207 var intersections = [];
3208 var intersection;
3209 var transformedPoints = new Array(basePoints.length);
3210 var doTransform = true;
3211
3212 if (width == null) {
3213 doTransform = false;
3214 }
3215
3216 var points;
3217
3218 if (doTransform) {
3219 for (var i = 0; i < transformedPoints.length / 2; i++) {
3220 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3221 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3222 }
3223
3224 if (padding > 0) {
3225 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3226 points = joinLines(expandedLineSet);
3227 } else {
3228 points = transformedPoints;
3229 }
3230 } else {
3231 points = basePoints;
3232 }
3233
3234 var currentX, currentY, nextX, nextY;
3235
3236 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3237 currentX = points[_i2 * 2];
3238 currentY = points[_i2 * 2 + 1];
3239
3240 if (_i2 < points.length / 2 - 1) {
3241 nextX = points[(_i2 + 1) * 2];
3242 nextY = points[(_i2 + 1) * 2 + 1];
3243 } else {
3244 nextX = points[0];
3245 nextY = points[1];
3246 }
3247
3248 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3249
3250 if (intersection.length !== 0) {
3251 intersections.push(intersection[0], intersection[1]);
3252 }
3253 }
3254
3255 return intersections;
3256};
3257var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3258 var intersections = [];
3259 var intersection;
3260 var lines = new Array(basePoints.length);
3261 var halfW = width / 2;
3262 var halfH = height / 2;
3263 var cornerRadius = getRoundPolygonRadius(width, height);
3264
3265 for (var i = 0; i < basePoints.length / 4; i++) {
3266 var sourceUv = void 0,
3267 destUv = void 0;
3268
3269 if (i === 0) {
3270 sourceUv = basePoints.length - 2;
3271 } else {
3272 sourceUv = i * 4 - 2;
3273 }
3274
3275 destUv = i * 4 + 2;
3276 var px = centerX + halfW * basePoints[i * 4];
3277 var py = centerY + halfH * basePoints[i * 4 + 1];
3278 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3279 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3280 var cp0x = px - offset * basePoints[sourceUv];
3281 var cp0y = py - offset * basePoints[sourceUv + 1];
3282 var cp1x = px + offset * basePoints[destUv];
3283 var cp1y = py + offset * basePoints[destUv + 1];
3284
3285 if (i === 0) {
3286 lines[basePoints.length - 2] = cp0x;
3287 lines[basePoints.length - 1] = cp0y;
3288 } else {
3289 lines[i * 4 - 2] = cp0x;
3290 lines[i * 4 - 1] = cp0y;
3291 }
3292
3293 lines[i * 4] = cp1x;
3294 lines[i * 4 + 1] = cp1y;
3295 var orthx = basePoints[sourceUv + 1];
3296 var orthy = -basePoints[sourceUv];
3297 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3298
3299 if (cosAlpha < 0) {
3300 orthx *= -1;
3301 orthy *= -1;
3302 }
3303
3304 var cx = cp0x + orthx * cornerRadius;
3305 var cy = cp0y + orthy * cornerRadius;
3306 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3307
3308 if (intersection.length !== 0) {
3309 intersections.push(intersection[0], intersection[1]);
3310 }
3311 }
3312
3313 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3314 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3315
3316 if (intersection.length !== 0) {
3317 intersections.push(intersection[0], intersection[1]);
3318 }
3319 }
3320
3321 if (intersections.length > 2) {
3322 var lowestIntersection = [intersections[0], intersections[1]];
3323 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3324
3325 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3326 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3327
3328 if (squaredDistance <= lowestSquaredDistance) {
3329 lowestIntersection[0] = intersections[_i4 * 2];
3330 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3331 lowestSquaredDistance = squaredDistance;
3332 }
3333 }
3334
3335 return lowestIntersection;
3336 }
3337
3338 return intersections;
3339};
3340var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3341 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3342 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3343 var lenRatio = (length - amount) / length;
3344
3345 if (lenRatio < 0) {
3346 lenRatio = 0.00001;
3347 }
3348
3349 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3350};
3351var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3352 var points = generateUnitNgonPoints(sides, rotationRadians);
3353 points = fitPolygonToSquare(points);
3354 return points;
3355};
3356var fitPolygonToSquare = function fitPolygonToSquare(points) {
3357 var x, y;
3358 var sides = points.length / 2;
3359 var minX = Infinity,
3360 minY = Infinity,
3361 maxX = -Infinity,
3362 maxY = -Infinity;
3363
3364 for (var i = 0; i < sides; i++) {
3365 x = points[2 * i];
3366 y = points[2 * i + 1];
3367 minX = Math.min(minX, x);
3368 maxX = Math.max(maxX, x);
3369 minY = Math.min(minY, y);
3370 maxY = Math.max(maxY, y);
3371 } // stretch factors
3372
3373
3374 var sx = 2 / (maxX - minX);
3375 var sy = 2 / (maxY - minY);
3376
3377 for (var _i5 = 0; _i5 < sides; _i5++) {
3378 x = points[2 * _i5] = points[2 * _i5] * sx;
3379 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3380 minX = Math.min(minX, x);
3381 maxX = Math.max(maxX, x);
3382 minY = Math.min(minY, y);
3383 maxY = Math.max(maxY, y);
3384 }
3385
3386 if (minY < -1) {
3387 for (var _i6 = 0; _i6 < sides; _i6++) {
3388 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3389 }
3390 }
3391
3392 return points;
3393};
3394var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3395 var increment = 1.0 / sides * 2 * Math.PI;
3396 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3397 startAngle += rotationRadians;
3398 var points = new Array(sides * 2);
3399 var currentAngle;
3400
3401 for (var i = 0; i < sides; i++) {
3402 currentAngle = i * increment + startAngle;
3403 points[2 * i] = Math.cos(currentAngle); // x
3404
3405 points[2 * i + 1] = Math.sin(-currentAngle); // y
3406 }
3407
3408 return points;
3409}; // Set the default radius, unless half of width or height is smaller than default
3410
3411var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3412 return Math.min(width / 4, height / 4, 8);
3413}; // Set the default radius
3414
3415var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3416 return Math.min(width / 10, height / 10, 8);
3417};
3418var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3419 return 8;
3420};
3421var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3422 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3423}; // get curve width, height, and control point position offsets as a percentage of node height / width
3424
3425var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3426 return {
3427 heightOffset: Math.min(15, 0.05 * height),
3428 widthOffset: Math.min(100, 0.25 * width),
3429 ctrlPtOffsetPct: 0.05
3430 };
3431};
3432
3433var pageRankDefaults = defaults({
3434 dampingFactor: 0.8,
3435 precision: 0.000001,
3436 iterations: 200,
3437 weight: function weight(edge) {
3438 return 1;
3439 }
3440});
3441var elesfn$7 = {
3442 pageRank: function pageRank(options) {
3443 var _pageRankDefaults = pageRankDefaults(options),
3444 dampingFactor = _pageRankDefaults.dampingFactor,
3445 precision = _pageRankDefaults.precision,
3446 iterations = _pageRankDefaults.iterations,
3447 weight = _pageRankDefaults.weight;
3448
3449 var cy = this._private.cy;
3450
3451 var _this$byGroup = this.byGroup(),
3452 nodes = _this$byGroup.nodes,
3453 edges = _this$byGroup.edges;
3454
3455 var numNodes = nodes.length;
3456 var numNodesSqd = numNodes * numNodes;
3457 var numEdges = edges.length; // Construct transposed adjacency matrix
3458 // First lets have a zeroed matrix of the right size
3459 // We'll also keep track of the sum of each column
3460
3461 var matrix = new Array(numNodesSqd);
3462 var columnSum = new Array(numNodes);
3463 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3464
3465 for (var i = 0; i < numNodes; i++) {
3466 for (var j = 0; j < numNodes; j++) {
3467 var n = i * numNodes + j;
3468 matrix[n] = 0;
3469 }
3470
3471 columnSum[i] = 0;
3472 } // Now, process edges
3473
3474
3475 for (var _i = 0; _i < numEdges; _i++) {
3476 var edge = edges[_i];
3477 var srcId = edge.data('source');
3478 var tgtId = edge.data('target'); // Don't include loops in the matrix
3479
3480 if (srcId === tgtId) {
3481 continue;
3482 }
3483
3484 var s = nodes.indexOfId(srcId);
3485 var t = nodes.indexOfId(tgtId);
3486 var w = weight(edge);
3487
3488 var _n = t * numNodes + s; // Update matrix
3489
3490
3491 matrix[_n] += w; // Update column sum
3492
3493 columnSum[s] += w;
3494 } // Add additional probability based on damping factor
3495 // Also, take into account columns that have sum = 0
3496
3497
3498 var p = 1.0 / numNodes + additionalProb; // Shorthand
3499 // Traverse matrix, column by column
3500
3501 for (var _j = 0; _j < numNodes; _j++) {
3502 if (columnSum[_j] === 0) {
3503 // No 'links' out from node jth, assume equal probability for each possible node
3504 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3505 var _n2 = _i2 * numNodes + _j;
3506
3507 matrix[_n2] = p;
3508 }
3509 } else {
3510 // Node jth has outgoing link, compute normalized probabilities
3511 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3512 var _n3 = _i3 * numNodes + _j;
3513
3514 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3515 }
3516 }
3517 } // Compute dominant eigenvector using power method
3518
3519
3520 var eigenvector = new Array(numNodes);
3521 var temp = new Array(numNodes);
3522 var previous; // Start with a vector of all 1's
3523 // Also, initialize a null vector which will be used as shorthand
3524
3525 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3526 eigenvector[_i4] = 1;
3527 }
3528
3529 for (var iter = 0; iter < iterations; iter++) {
3530 // Temp array with all 0's
3531 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3532 temp[_i5] = 0;
3533 } // Multiply matrix with previous result
3534
3535
3536 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3537 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3538 var _n4 = _i6 * numNodes + _j2;
3539
3540 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3541 }
3542 }
3543
3544 inPlaceSumNormalize(temp);
3545 previous = eigenvector;
3546 eigenvector = temp;
3547 temp = previous;
3548 var diff = 0; // Compute difference (squared module) of both vectors
3549
3550 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3551 var delta = previous[_i7] - eigenvector[_i7];
3552 diff += delta * delta;
3553 } // If difference is less than the desired threshold, stop iterating
3554
3555
3556 if (diff < precision) {
3557 break;
3558 }
3559 } // Construct result
3560
3561
3562 var res = {
3563 rank: function rank(node) {
3564 node = cy.collection(node)[0];
3565 return eigenvector[nodes.indexOf(node)];
3566 }
3567 };
3568 return res;
3569 } // pageRank
3570
3571}; // elesfn
3572
3573var defaults$1 = defaults({
3574 root: null,
3575 weight: function weight(edge) {
3576 return 1;
3577 },
3578 directed: false,
3579 alpha: 0
3580});
3581var elesfn$8 = {
3582 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3583 options = defaults$1(options);
3584 var cy = this.cy();
3585 var nodes = this.nodes();
3586 var numNodes = nodes.length;
3587
3588 if (!options.directed) {
3589 var degrees = {};
3590 var maxDegree = 0;
3591
3592 for (var i = 0; i < numNodes; i++) {
3593 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3594
3595 options.root = node;
3596 var currDegree = this.degreeCentrality(options);
3597
3598 if (maxDegree < currDegree.degree) {
3599 maxDegree = currDegree.degree;
3600 }
3601
3602 degrees[node.id()] = currDegree.degree;
3603 }
3604
3605 return {
3606 degree: function degree(node) {
3607 if (maxDegree === 0) {
3608 return 0;
3609 }
3610
3611 if (string(node)) {
3612 // from is a selector string
3613 node = cy.filter(node);
3614 }
3615
3616 return degrees[node.id()] / maxDegree;
3617 }
3618 };
3619 } else {
3620 var indegrees = {};
3621 var outdegrees = {};
3622 var maxIndegree = 0;
3623 var maxOutdegree = 0;
3624
3625 for (var _i = 0; _i < numNodes; _i++) {
3626 var _node = nodes[_i];
3627
3628 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3629
3630
3631 options.root = _node;
3632
3633 var _currDegree = this.degreeCentrality(options);
3634
3635 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3636 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3637 indegrees[id] = _currDegree.indegree;
3638 outdegrees[id] = _currDegree.outdegree;
3639 }
3640
3641 return {
3642 indegree: function indegree(node) {
3643 if (maxIndegree == 0) {
3644 return 0;
3645 }
3646
3647 if (string(node)) {
3648 // from is a selector string
3649 node = cy.filter(node);
3650 }
3651
3652 return indegrees[node.id()] / maxIndegree;
3653 },
3654 outdegree: function outdegree(node) {
3655 if (maxOutdegree === 0) {
3656 return 0;
3657 }
3658
3659 if (string(node)) {
3660 // from is a selector string
3661 node = cy.filter(node);
3662 }
3663
3664 return outdegrees[node.id()] / maxOutdegree;
3665 }
3666 };
3667 }
3668 },
3669 // degreeCentralityNormalized
3670 // Implemented from the algorithm in Opsahl's paper
3671 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3672 // check the heading 2 "Degree"
3673 degreeCentrality: function degreeCentrality(options) {
3674 options = defaults$1(options);
3675 var cy = this.cy();
3676 var callingEles = this;
3677 var _options = options,
3678 root = _options.root,
3679 weight = _options.weight,
3680 directed = _options.directed,
3681 alpha = _options.alpha;
3682 root = cy.collection(root)[0];
3683
3684 if (!directed) {
3685 var connEdges = root.connectedEdges().intersection(callingEles);
3686 var k = connEdges.length;
3687 var s = 0; // Now, sum edge weights
3688
3689 for (var i = 0; i < connEdges.length; i++) {
3690 s += weight(connEdges[i]);
3691 }
3692
3693 return {
3694 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3695 };
3696 } else {
3697 var edges = root.connectedEdges();
3698 var incoming = edges.filter(function (edge) {
3699 return edge.target().same(root) && callingEles.has(edge);
3700 });
3701 var outgoing = edges.filter(function (edge) {
3702 return edge.source().same(root) && callingEles.has(edge);
3703 });
3704 var k_in = incoming.length;
3705 var k_out = outgoing.length;
3706 var s_in = 0;
3707 var s_out = 0; // Now, sum incoming edge weights
3708
3709 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3710 s_in += weight(incoming[_i2]);
3711 } // Now, sum outgoing edge weights
3712
3713
3714 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3715 s_out += weight(outgoing[_i3]);
3716 }
3717
3718 return {
3719 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3720 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3721 };
3722 }
3723 } // degreeCentrality
3724
3725}; // elesfn
3726// nice, short mathemathical alias
3727
3728elesfn$8.dc = elesfn$8.degreeCentrality;
3729elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3730
3731var defaults$2 = defaults({
3732 harmonic: true,
3733 weight: function weight() {
3734 return 1;
3735 },
3736 directed: false,
3737 root: null
3738});
3739var elesfn$9 = {
3740 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3741 var _defaults = defaults$2(options),
3742 harmonic = _defaults.harmonic,
3743 weight = _defaults.weight,
3744 directed = _defaults.directed;
3745
3746 var cy = this.cy();
3747 var closenesses = {};
3748 var maxCloseness = 0;
3749 var nodes = this.nodes();
3750 var fw = this.floydWarshall({
3751 weight: weight,
3752 directed: directed
3753 }); // Compute closeness for every node and find the maximum closeness
3754
3755 for (var i = 0; i < nodes.length; i++) {
3756 var currCloseness = 0;
3757 var node_i = nodes[i];
3758
3759 for (var j = 0; j < nodes.length; j++) {
3760 if (i !== j) {
3761 var d = fw.distance(node_i, nodes[j]);
3762
3763 if (harmonic) {
3764 currCloseness += 1 / d;
3765 } else {
3766 currCloseness += d;
3767 }
3768 }
3769 }
3770
3771 if (!harmonic) {
3772 currCloseness = 1 / currCloseness;
3773 }
3774
3775 if (maxCloseness < currCloseness) {
3776 maxCloseness = currCloseness;
3777 }
3778
3779 closenesses[node_i.id()] = currCloseness;
3780 }
3781
3782 return {
3783 closeness: function closeness(node) {
3784 if (maxCloseness == 0) {
3785 return 0;
3786 }
3787
3788 if (string(node)) {
3789 // from is a selector string
3790 node = cy.filter(node)[0].id();
3791 } else {
3792 // from is a node
3793 node = node.id();
3794 }
3795
3796 return closenesses[node] / maxCloseness;
3797 }
3798 };
3799 },
3800 // Implemented from pseudocode from wikipedia
3801 closenessCentrality: function closenessCentrality(options) {
3802 var _defaults2 = defaults$2(options),
3803 root = _defaults2.root,
3804 weight = _defaults2.weight,
3805 directed = _defaults2.directed,
3806 harmonic = _defaults2.harmonic;
3807
3808 root = this.filter(root)[0]; // we need distance from this node to every other node
3809
3810 var dijkstra = this.dijkstra({
3811 root: root,
3812 weight: weight,
3813 directed: directed
3814 });
3815 var totalDistance = 0;
3816 var nodes = this.nodes();
3817
3818 for (var i = 0; i < nodes.length; i++) {
3819 var n = nodes[i];
3820
3821 if (!n.same(root)) {
3822 var d = dijkstra.distanceTo(n);
3823
3824 if (harmonic) {
3825 totalDistance += 1 / d;
3826 } else {
3827 totalDistance += d;
3828 }
3829 }
3830 }
3831
3832 return harmonic ? totalDistance : 1 / totalDistance;
3833 } // closenessCentrality
3834
3835}; // elesfn
3836// nice, short mathemathical alias
3837
3838elesfn$9.cc = elesfn$9.closenessCentrality;
3839elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3840
3841var defaults$3 = defaults({
3842 weight: null,
3843 directed: false
3844});
3845var elesfn$a = {
3846 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3847 betweennessCentrality: function betweennessCentrality(options) {
3848 var _defaults = defaults$3(options),
3849 directed = _defaults.directed,
3850 weight = _defaults.weight;
3851
3852 var weighted = weight != null;
3853 var cy = this.cy(); // starting
3854
3855 var V = this.nodes();
3856 var A = {};
3857 var _C = {};
3858 var max = 0;
3859 var C = {
3860 set: function set(key, val) {
3861 _C[key] = val;
3862
3863 if (val > max) {
3864 max = val;
3865 }
3866 },
3867 get: function get(key) {
3868 return _C[key];
3869 }
3870 }; // A contains the neighborhoods of every node
3871
3872 for (var i = 0; i < V.length; i++) {
3873 var v = V[i];
3874 var vid = v.id();
3875
3876 if (directed) {
3877 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3878 } else {
3879 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3880 }
3881
3882 C.set(vid, 0);
3883 }
3884
3885 var _loop = function _loop(s) {
3886 var sid = V[s].id();
3887 var S = []; // stack
3888
3889 var P = {};
3890 var g = {};
3891 var d = {};
3892 var Q = new Heap(function (a, b) {
3893 return d[a] - d[b];
3894 }); // queue
3895 // init dictionaries
3896
3897 for (var _i = 0; _i < V.length; _i++) {
3898 var _vid = V[_i].id();
3899
3900 P[_vid] = [];
3901 g[_vid] = 0;
3902 d[_vid] = Infinity;
3903 }
3904
3905 g[sid] = 1; // sigma
3906
3907 d[sid] = 0; // distance to s
3908
3909 Q.push(sid);
3910
3911 while (!Q.empty()) {
3912 var _v = Q.pop();
3913
3914 S.push(_v);
3915
3916 if (weighted) {
3917 for (var j = 0; j < A[_v].length; j++) {
3918 var w = A[_v][j];
3919 var vEle = cy.getElementById(_v);
3920 var edge = void 0;
3921
3922 if (vEle.edgesTo(w).length > 0) {
3923 edge = vEle.edgesTo(w)[0];
3924 } else {
3925 edge = w.edgesTo(vEle)[0];
3926 }
3927
3928 var edgeWeight = weight(edge);
3929 w = w.id();
3930
3931 if (d[w] > d[_v] + edgeWeight) {
3932 d[w] = d[_v] + edgeWeight;
3933
3934 if (Q.nodes.indexOf(w) < 0) {
3935 //if w is not in Q
3936 Q.push(w);
3937 } else {
3938 // update position if w is in Q
3939 Q.updateItem(w);
3940 }
3941
3942 g[w] = 0;
3943 P[w] = [];
3944 }
3945
3946 if (d[w] == d[_v] + edgeWeight) {
3947 g[w] = g[w] + g[_v];
3948 P[w].push(_v);
3949 }
3950 }
3951 } else {
3952 for (var _j = 0; _j < A[_v].length; _j++) {
3953 var _w = A[_v][_j].id();
3954
3955 if (d[_w] == Infinity) {
3956 Q.push(_w);
3957 d[_w] = d[_v] + 1;
3958 }
3959
3960 if (d[_w] == d[_v] + 1) {
3961 g[_w] = g[_w] + g[_v];
3962
3963 P[_w].push(_v);
3964 }
3965 }
3966 }
3967 }
3968
3969 var e = {};
3970
3971 for (var _i2 = 0; _i2 < V.length; _i2++) {
3972 e[V[_i2].id()] = 0;
3973 }
3974
3975 while (S.length > 0) {
3976 var _w2 = S.pop();
3977
3978 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
3979 var _v2 = P[_w2][_j2];
3980 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
3981 }
3982
3983 if (_w2 != V[s].id()) {
3984 C.set(_w2, C.get(_w2) + e[_w2]);
3985 }
3986 }
3987 };
3988
3989 for (var s = 0; s < V.length; s++) {
3990 _loop(s);
3991 }
3992
3993 var ret = {
3994 betweenness: function betweenness(node) {
3995 var id = cy.collection(node).id();
3996 return C.get(id);
3997 },
3998 betweennessNormalized: function betweennessNormalized(node) {
3999 if (max == 0) {
4000 return 0;
4001 }
4002
4003 var id = cy.collection(node).id();
4004 return C.get(id) / max;
4005 }
4006 }; // alias
4007
4008 ret.betweennessNormalised = ret.betweennessNormalized;
4009 return ret;
4010 } // betweennessCentrality
4011
4012}; // elesfn
4013// nice, short mathemathical alias
4014
4015elesfn$a.bc = elesfn$a.betweennessCentrality;
4016
4017// Implemented by Zoe Xi @zoexi for GSOC 2016
4018/* eslint-disable no-unused-vars */
4019
4020var defaults$4 = defaults({
4021 expandFactor: 2,
4022 // affects time of computation and cluster granularity to some extent: M * M
4023 inflateFactor: 2,
4024 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4025 multFactor: 1,
4026 // optional self loops for each node. Use a neutral value to improve cluster computations.
4027 maxIterations: 20,
4028 // maximum number of iterations of the MCL algorithm in a single run
4029 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4030 function (edge) {
4031 return 1;
4032 }]
4033});
4034/* eslint-enable */
4035
4036var setOptions = function setOptions(options) {
4037 return defaults$4(options);
4038};
4039/* eslint-enable */
4040
4041
4042var getSimilarity = function getSimilarity(edge, attributes) {
4043 var total = 0;
4044
4045 for (var i = 0; i < attributes.length; i++) {
4046 total += attributes[i](edge);
4047 }
4048
4049 return total;
4050};
4051
4052var addLoops = function addLoops(M, n, val) {
4053 for (var i = 0; i < n; i++) {
4054 M[i * n + i] = val;
4055 }
4056};
4057
4058var normalize = function normalize(M, n) {
4059 var sum;
4060
4061 for (var col = 0; col < n; col++) {
4062 sum = 0;
4063
4064 for (var row = 0; row < n; row++) {
4065 sum += M[row * n + col];
4066 }
4067
4068 for (var _row = 0; _row < n; _row++) {
4069 M[_row * n + col] = M[_row * n + col] / sum;
4070 }
4071 }
4072}; // TODO: blocked matrix multiplication?
4073
4074
4075var mmult = function mmult(A, B, n) {
4076 var C = new Array(n * n);
4077
4078 for (var i = 0; i < n; i++) {
4079 for (var j = 0; j < n; j++) {
4080 C[i * n + j] = 0;
4081 }
4082
4083 for (var k = 0; k < n; k++) {
4084 for (var _j = 0; _j < n; _j++) {
4085 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4086 }
4087 }
4088 }
4089
4090 return C;
4091};
4092
4093var expand = function expand(M, n, expandFactor
4094/** power **/
4095) {
4096 var _M = M.slice(0);
4097
4098 for (var p = 1; p < expandFactor; p++) {
4099 M = mmult(M, _M, n);
4100 }
4101
4102 return M;
4103};
4104
4105var inflate = function inflate(M, n, inflateFactor
4106/** r **/
4107) {
4108 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4109
4110
4111 for (var i = 0; i < n * n; i++) {
4112 _M[i] = Math.pow(M[i], inflateFactor);
4113 }
4114
4115 normalize(_M, n);
4116 return _M;
4117};
4118
4119var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4120 // Check that both matrices have the same elements (i,j)
4121 for (var i = 0; i < n2; i++) {
4122 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4123
4124 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4125
4126 if (v1 !== v2) {
4127 return false;
4128 }
4129 }
4130
4131 return true;
4132};
4133
4134var assign = function assign(M, n, nodes, cy) {
4135 var clusters = [];
4136
4137 for (var i = 0; i < n; i++) {
4138 var cluster = [];
4139
4140 for (var j = 0; j < n; j++) {
4141 // Row-wise attractors and elements that they attract belong in same cluster
4142 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4143 cluster.push(nodes[j]);
4144 }
4145 }
4146
4147 if (cluster.length !== 0) {
4148 clusters.push(cy.collection(cluster));
4149 }
4150 }
4151
4152 return clusters;
4153};
4154
4155var isDuplicate = function isDuplicate(c1, c2) {
4156 for (var i = 0; i < c1.length; i++) {
4157 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4158 return false;
4159 }
4160 }
4161
4162 return true;
4163};
4164
4165var removeDuplicates = function removeDuplicates(clusters) {
4166 for (var i = 0; i < clusters.length; i++) {
4167 for (var j = 0; j < clusters.length; j++) {
4168 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4169 clusters.splice(j, 1);
4170 }
4171 }
4172 }
4173
4174 return clusters;
4175};
4176
4177var markovClustering = function markovClustering(options) {
4178 var nodes = this.nodes();
4179 var edges = this.edges();
4180 var cy = this.cy(); // Set parameters of algorithm:
4181
4182 var opts = setOptions(options); // Map each node to its position in node array
4183
4184 var id2position = {};
4185
4186 for (var i = 0; i < nodes.length; i++) {
4187 id2position[nodes[i].id()] = i;
4188 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4189
4190
4191 var n = nodes.length,
4192 n2 = n * n;
4193
4194 var M = new Array(n2),
4195 _M;
4196
4197 for (var _i = 0; _i < n2; _i++) {
4198 M[_i] = 0;
4199 }
4200
4201 for (var e = 0; e < edges.length; e++) {
4202 var edge = edges[e];
4203 var _i2 = id2position[edge.source().id()];
4204 var j = id2position[edge.target().id()];
4205 var sim = getSimilarity(edge, opts.attributes);
4206 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4207
4208 M[j * n + _i2] += sim;
4209 } // Begin Markov cluster algorithm
4210 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4211
4212
4213 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4214
4215 normalize(M, n);
4216 var isStillMoving = true;
4217 var iterations = 0;
4218
4219 while (isStillMoving && iterations < opts.maxIterations) {
4220 isStillMoving = false; // Step 3:
4221
4222 _M = expand(M, n, opts.expandFactor); // Step 4:
4223
4224 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4225
4226 if (!hasConverged(M, _M, n2, 4)) {
4227 isStillMoving = true;
4228 }
4229
4230 iterations++;
4231 } // Build clusters from matrix
4232
4233
4234 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4235
4236 clusters = removeDuplicates(clusters);
4237 return clusters;
4238};
4239
4240var markovClustering$1 = {
4241 markovClustering: markovClustering,
4242 mcl: markovClustering
4243};
4244
4245// Common distance metrics for clustering algorithms
4246
4247var identity = function identity(x) {
4248 return x;
4249};
4250
4251var absDiff = function absDiff(p, q) {
4252 return Math.abs(q - p);
4253};
4254
4255var addAbsDiff = function addAbsDiff(total, p, q) {
4256 return total + absDiff(p, q);
4257};
4258
4259var addSquaredDiff = function addSquaredDiff(total, p, q) {
4260 return total + Math.pow(q - p, 2);
4261};
4262
4263var sqrt = function sqrt(x) {
4264 return Math.sqrt(x);
4265};
4266
4267var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4268 return Math.max(currentMax, absDiff(p, q));
4269};
4270
4271var getDistance = function getDistance(length, getP, getQ, init, visit) {
4272 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4273 var ret = init;
4274 var p, q;
4275
4276 for (var dim = 0; dim < length; dim++) {
4277 p = getP(dim);
4278 q = getQ(dim);
4279 ret = visit(ret, p, q);
4280 }
4281
4282 return post(ret);
4283};
4284
4285var distances = {
4286 euclidean: function euclidean(length, getP, getQ) {
4287 if (length >= 2) {
4288 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4289 } else {
4290 // for single attr case, more efficient to avoid sqrt
4291 return getDistance(length, getP, getQ, 0, addAbsDiff);
4292 }
4293 },
4294 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4295 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4296 },
4297 manhattan: function manhattan(length, getP, getQ) {
4298 return getDistance(length, getP, getQ, 0, addAbsDiff);
4299 },
4300 max: function max(length, getP, getQ) {
4301 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4302 }
4303}; // in case the user accidentally doesn't use camel case
4304
4305distances['squared-euclidean'] = distances['squaredEuclidean'];
4306distances['squaredeuclidean'] = distances['squaredEuclidean'];
4307function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4308 var impl;
4309
4310 if (fn(method)) {
4311 impl = method;
4312 } else {
4313 impl = distances[method] || distances.euclidean;
4314 }
4315
4316 if (length === 0 && fn(method)) {
4317 return impl(nodeP, nodeQ);
4318 } else {
4319 return impl(length, getP, getQ, nodeP, nodeQ);
4320 }
4321}
4322
4323var defaults$5 = defaults({
4324 k: 2,
4325 m: 2,
4326 sensitivityThreshold: 0.0001,
4327 distance: 'euclidean',
4328 maxIterations: 10,
4329 attributes: [],
4330 testMode: false,
4331 testCentroids: null
4332});
4333
4334var setOptions$1 = function setOptions(options) {
4335 return defaults$5(options);
4336};
4337/* eslint-enable */
4338
4339
4340var getDist = function getDist(type, node, centroid, attributes, mode) {
4341 var noNodeP = mode !== 'kMedoids';
4342 var getP = noNodeP ? function (i) {
4343 return centroid[i];
4344 } : function (i) {
4345 return attributes[i](centroid);
4346 };
4347
4348 var getQ = function getQ(i) {
4349 return attributes[i](node);
4350 };
4351
4352 var nodeP = centroid;
4353 var nodeQ = node;
4354 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4355};
4356
4357var randomCentroids = function randomCentroids(nodes, k, attributes) {
4358 var ndim = attributes.length;
4359 var min = new Array(ndim);
4360 var max = new Array(ndim);
4361 var centroids = new Array(k);
4362 var centroid = null; // Find min, max values for each attribute dimension
4363
4364 for (var i = 0; i < ndim; i++) {
4365 min[i] = nodes.min(attributes[i]).value;
4366 max[i] = nodes.max(attributes[i]).value;
4367 } // Build k centroids, each represented as an n-dim feature vector
4368
4369
4370 for (var c = 0; c < k; c++) {
4371 centroid = [];
4372
4373 for (var _i = 0; _i < ndim; _i++) {
4374 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4375 }
4376
4377 centroids[c] = centroid;
4378 }
4379
4380 return centroids;
4381};
4382
4383var classify = function classify(node, centroids, distance, attributes, type) {
4384 var min = Infinity;
4385 var index = 0;
4386
4387 for (var i = 0; i < centroids.length; i++) {
4388 var dist = getDist(distance, node, centroids[i], attributes, type);
4389
4390 if (dist < min) {
4391 min = dist;
4392 index = i;
4393 }
4394 }
4395
4396 return index;
4397};
4398
4399var buildCluster = function buildCluster(centroid, nodes, assignment) {
4400 var cluster = [];
4401 var node = null;
4402
4403 for (var n = 0; n < nodes.length; n++) {
4404 node = nodes[n];
4405
4406 if (assignment[node.id()] === centroid) {
4407 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4408 cluster.push(node);
4409 }
4410 }
4411
4412 return cluster;
4413};
4414
4415var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4416 return Math.abs(v2 - v1) <= sensitivityThreshold;
4417};
4418
4419var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4420 for (var i = 0; i < v1.length; i++) {
4421 for (var j = 0; j < v1[i].length; j++) {
4422 var diff = Math.abs(v1[i][j] - v2[i][j]);
4423
4424 if (diff > sensitivityThreshold) {
4425 return false;
4426 }
4427 }
4428 }
4429
4430 return true;
4431};
4432
4433var seenBefore = function seenBefore(node, medoids, n) {
4434 for (var i = 0; i < n; i++) {
4435 if (node === medoids[i]) return true;
4436 }
4437
4438 return false;
4439};
4440
4441var randomMedoids = function randomMedoids(nodes, k) {
4442 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4443 // so we need to check to see if we've already seen or chose this node before.
4444
4445 if (nodes.length < 50) {
4446 // Randomly select k medoids from the n nodes
4447 for (var i = 0; i < k; i++) {
4448 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).
4449 // Instead choose a different random node.
4450
4451 while (seenBefore(node, medoids, i)) {
4452 node = nodes[Math.floor(Math.random() * nodes.length)];
4453 }
4454
4455 medoids[i] = node;
4456 }
4457 } else {
4458 // Relatively large data set, so pretty safe to not check and just select random nodes
4459 for (var _i2 = 0; _i2 < k; _i2++) {
4460 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4461 }
4462 }
4463
4464 return medoids;
4465};
4466
4467var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4468 var cost = 0;
4469
4470 for (var n = 0; n < cluster.length; n++) {
4471 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4472 }
4473
4474 return cost;
4475};
4476
4477var kMeans = function kMeans(options) {
4478 var cy = this.cy();
4479 var nodes = this.nodes();
4480 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4481
4482 var opts = setOptions$1(options); // Begin k-means algorithm
4483
4484 var clusters = new Array(opts.k);
4485 var assignment = {};
4486 var centroids; // Step 1: Initialize centroid positions
4487
4488 if (opts.testMode) {
4489 if (typeof opts.testCentroids === 'number') {
4490 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4491 } else if (_typeof(opts.testCentroids) === 'object') {
4492 centroids = opts.testCentroids;
4493 } else {
4494 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4495 }
4496 } else {
4497 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4498 }
4499
4500 var isStillMoving = true;
4501 var iterations = 0;
4502
4503 while (isStillMoving && iterations < opts.maxIterations) {
4504 // Step 2: Assign nodes to the nearest centroid
4505 for (var n = 0; n < nodes.length; n++) {
4506 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4507
4508 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4509 } // Step 3: For each of the k clusters, update its centroid
4510
4511
4512 isStillMoving = false;
4513
4514 for (var c = 0; c < opts.k; c++) {
4515 // Get all nodes that belong to this cluster
4516 var cluster = buildCluster(c, nodes, assignment);
4517
4518 if (cluster.length === 0) {
4519 // If cluster is empty, break out early & move to next cluster
4520 continue;
4521 } // Update centroids by calculating avg of all nodes within the cluster.
4522
4523
4524 var ndim = opts.attributes.length;
4525 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4526
4527 var newCentroid = new Array(ndim);
4528 var sum = new Array(ndim);
4529
4530 for (var d = 0; d < ndim; d++) {
4531 sum[d] = 0.0;
4532
4533 for (var i = 0; i < cluster.length; i++) {
4534 node = cluster[i];
4535 sum[d] += opts.attributes[d](node);
4536 }
4537
4538 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4539
4540 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4541 isStillMoving = true;
4542 }
4543 }
4544
4545 centroids[c] = newCentroid;
4546 clusters[c] = cy.collection(cluster);
4547 }
4548
4549 iterations++;
4550 }
4551
4552 return clusters;
4553};
4554
4555var kMedoids = function kMedoids(options) {
4556 var cy = this.cy();
4557 var nodes = this.nodes();
4558 var node = null;
4559 var opts = setOptions$1(options); // Begin k-medoids algorithm
4560
4561 var clusters = new Array(opts.k);
4562 var medoids;
4563 var assignment = {};
4564 var curCost;
4565 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4566 // Step 1: Initialize k medoids
4567
4568 if (opts.testMode) {
4569 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4570 medoids = opts.testCentroids;
4571 } else {
4572 medoids = randomMedoids(nodes, opts.k);
4573 }
4574 } else {
4575 medoids = randomMedoids(nodes, opts.k);
4576 }
4577
4578 var isStillMoving = true;
4579 var iterations = 0;
4580
4581 while (isStillMoving && iterations < opts.maxIterations) {
4582 // Step 2: Assign nodes to the nearest medoid
4583 for (var n = 0; n < nodes.length; n++) {
4584 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4585
4586 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4587 }
4588
4589 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4590 // select the node with the lowest configuration cost as new medoid.
4591
4592 for (var m = 0; m < medoids.length; m++) {
4593 // Get all nodes that belong to this medoid
4594 var cluster = buildCluster(m, nodes, assignment);
4595
4596 if (cluster.length === 0) {
4597 // If cluster is empty, break out early & move to next cluster
4598 continue;
4599 }
4600
4601 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4602 // Select different medoid if its configuration has the lowest cost
4603
4604 for (var _n = 0; _n < cluster.length; _n++) {
4605 curCost = findCost(cluster[_n], cluster, opts.attributes);
4606
4607 if (curCost < minCosts[m]) {
4608 minCosts[m] = curCost;
4609 medoids[m] = cluster[_n];
4610 isStillMoving = true;
4611 }
4612 }
4613
4614 clusters[m] = cy.collection(cluster);
4615 }
4616
4617 iterations++;
4618 }
4619
4620 return clusters;
4621};
4622
4623var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4624 var numerator, denominator;
4625
4626 for (var n = 0; n < nodes.length; n++) {
4627 for (var c = 0; c < centroids.length; c++) {
4628 weight[n][c] = Math.pow(U[n][c], opts.m);
4629 }
4630 }
4631
4632 for (var _c = 0; _c < centroids.length; _c++) {
4633 for (var dim = 0; dim < opts.attributes.length; dim++) {
4634 numerator = 0;
4635 denominator = 0;
4636
4637 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4638 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4639 denominator += weight[_n2][_c];
4640 }
4641
4642 centroids[_c][dim] = numerator / denominator;
4643 }
4644 }
4645};
4646
4647var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4648 // Save previous step
4649 for (var i = 0; i < U.length; i++) {
4650 _U[i] = U[i].slice();
4651 }
4652
4653 var sum, numerator, denominator;
4654 var pow = 2 / (opts.m - 1);
4655
4656 for (var c = 0; c < centroids.length; c++) {
4657 for (var n = 0; n < nodes.length; n++) {
4658 sum = 0;
4659
4660 for (var k = 0; k < centroids.length; k++) {
4661 // against all other centroids
4662 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4663 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4664 sum += Math.pow(numerator / denominator, pow);
4665 }
4666
4667 U[n][c] = 1 / sum;
4668 }
4669 }
4670};
4671
4672var assign$1 = function assign(nodes, U, opts, cy) {
4673 var clusters = new Array(opts.k);
4674
4675 for (var c = 0; c < clusters.length; c++) {
4676 clusters[c] = [];
4677 }
4678
4679 var max;
4680 var index;
4681
4682 for (var n = 0; n < U.length; n++) {
4683 // for each node (U is N x C matrix)
4684 max = -Infinity;
4685 index = -1; // Determine which cluster the node is most likely to belong in
4686
4687 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4688 if (U[n][_c2] > max) {
4689 max = U[n][_c2];
4690 index = _c2;
4691 }
4692 }
4693
4694 clusters[index].push(nodes[n]);
4695 } // Turn every array into a collection of nodes
4696
4697
4698 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4699 clusters[_c3] = cy.collection(clusters[_c3]);
4700 }
4701
4702 return clusters;
4703};
4704
4705var fuzzyCMeans = function fuzzyCMeans(options) {
4706 var cy = this.cy();
4707 var nodes = this.nodes();
4708 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4709
4710 var clusters;
4711 var centroids;
4712 var U;
4713
4714 var _U;
4715
4716 var weight; // Step 1: Initialize letiables.
4717
4718 _U = new Array(nodes.length);
4719
4720 for (var i = 0; i < nodes.length; i++) {
4721 // N x C matrix
4722 _U[i] = new Array(opts.k);
4723 }
4724
4725 U = new Array(nodes.length);
4726
4727 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4728 // N x C matrix
4729 U[_i3] = new Array(opts.k);
4730 }
4731
4732 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4733 var total = 0;
4734
4735 for (var j = 0; j < opts.k; j++) {
4736 U[_i4][j] = Math.random();
4737 total += U[_i4][j];
4738 }
4739
4740 for (var _j = 0; _j < opts.k; _j++) {
4741 U[_i4][_j] = U[_i4][_j] / total;
4742 }
4743 }
4744
4745 centroids = new Array(opts.k);
4746
4747 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4748 centroids[_i5] = new Array(opts.attributes.length);
4749 }
4750
4751 weight = new Array(nodes.length);
4752
4753 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4754 // N x C matrix
4755 weight[_i6] = new Array(opts.k);
4756 } // end init FCM
4757
4758
4759 var isStillMoving = true;
4760 var iterations = 0;
4761
4762 while (isStillMoving && iterations < opts.maxIterations) {
4763 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4764
4765 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4766
4767 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4768
4769 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4770 isStillMoving = true;
4771 }
4772
4773 iterations++;
4774 } // Assign nodes to clusters with highest probability.
4775
4776
4777 clusters = assign$1(nodes, U, opts, cy);
4778 return {
4779 clusters: clusters,
4780 degreeOfMembership: U
4781 };
4782};
4783
4784var kClustering = {
4785 kMeans: kMeans,
4786 kMedoids: kMedoids,
4787 fuzzyCMeans: fuzzyCMeans,
4788 fcm: fuzzyCMeans
4789};
4790
4791// Implemented by Zoe Xi @zoexi for GSOC 2016
4792var defaults$6 = defaults({
4793 distance: 'euclidean',
4794 // distance metric to compare nodes
4795 linkage: 'min',
4796 // linkage criterion : how to determine the distance between clusters of nodes
4797 mode: 'threshold',
4798 // mode:'threshold' => clusters must be threshold distance apart
4799 threshold: Infinity,
4800 // the distance threshold
4801 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4802 addDendrogram: false,
4803 // whether to add the dendrogram to the graph for viz
4804 dendrogramDepth: 0,
4805 // depth at which dendrogram branches are merged into the returned clusters
4806 attributes: [] // array of attr functions
4807
4808});
4809var linkageAliases = {
4810 'single': 'min',
4811 'complete': 'max'
4812};
4813
4814var setOptions$2 = function setOptions(options) {
4815 var opts = defaults$6(options);
4816 var preferredAlias = linkageAliases[opts.linkage];
4817
4818 if (preferredAlias != null) {
4819 opts.linkage = preferredAlias;
4820 }
4821
4822 return opts;
4823};
4824
4825var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4826 // Find two closest clusters from cached mins
4827 var minKey = 0;
4828 var min = Infinity;
4829 var dist;
4830 var attrs = opts.attributes;
4831
4832 var getDist = function getDist(n1, n2) {
4833 return clusteringDistance(opts.distance, attrs.length, function (i) {
4834 return attrs[i](n1);
4835 }, function (i) {
4836 return attrs[i](n2);
4837 }, n1, n2);
4838 };
4839
4840 for (var i = 0; i < clusters.length; i++) {
4841 var key = clusters[i].key;
4842 var _dist = dists[key][mins[key]];
4843
4844 if (_dist < min) {
4845 minKey = key;
4846 min = _dist;
4847 }
4848 }
4849
4850 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4851 return false;
4852 }
4853
4854 var c1 = index[minKey];
4855 var c2 = index[mins[minKey]];
4856 var merged; // Merge two closest clusters
4857
4858 if (opts.mode === 'dendrogram') {
4859 merged = {
4860 left: c1,
4861 right: c2,
4862 key: c1.key
4863 };
4864 } else {
4865 merged = {
4866 value: c1.value.concat(c2.value),
4867 key: c1.key
4868 };
4869 }
4870
4871 clusters[c1.index] = merged;
4872 clusters.splice(c2.index, 1);
4873 index[c1.key] = merged; // Update distances with new merged cluster
4874
4875 for (var _i = 0; _i < clusters.length; _i++) {
4876 var cur = clusters[_i];
4877
4878 if (c1.key === cur.key) {
4879 dist = Infinity;
4880 } else if (opts.linkage === 'min') {
4881 dist = dists[c1.key][cur.key];
4882
4883 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4884 dist = dists[c2.key][cur.key];
4885 }
4886 } else if (opts.linkage === 'max') {
4887 dist = dists[c1.key][cur.key];
4888
4889 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4890 dist = dists[c2.key][cur.key];
4891 }
4892 } else if (opts.linkage === 'mean') {
4893 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4894 } else {
4895 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4896 }
4897
4898 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4899 } // Update cached mins
4900
4901
4902 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4903 var key1 = clusters[_i2].key;
4904
4905 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4906 var _min = key1;
4907
4908 for (var j = 0; j < clusters.length; j++) {
4909 var key2 = clusters[j].key;
4910
4911 if (dists[key1][key2] < dists[key1][_min]) {
4912 _min = key2;
4913 }
4914 }
4915
4916 mins[key1] = _min;
4917 }
4918
4919 clusters[_i2].index = _i2;
4920 } // Clean up meta data used for clustering
4921
4922
4923 c1.key = c2.key = c1.index = c2.index = null;
4924 return true;
4925};
4926
4927var getAllChildren = function getAllChildren(root, arr, cy) {
4928 if (!root) return;
4929
4930 if (root.value) {
4931 arr.push(root.value);
4932 } else {
4933 if (root.left) getAllChildren(root.left, arr);
4934 if (root.right) getAllChildren(root.right, arr);
4935 }
4936};
4937
4938var buildDendrogram = function buildDendrogram(root, cy) {
4939 if (!root) return '';
4940
4941 if (root.left && root.right) {
4942 var leftStr = buildDendrogram(root.left, cy);
4943 var rightStr = buildDendrogram(root.right, cy);
4944 var node = cy.add({
4945 group: 'nodes',
4946 data: {
4947 id: leftStr + ',' + rightStr
4948 }
4949 });
4950 cy.add({
4951 group: 'edges',
4952 data: {
4953 source: leftStr,
4954 target: node.id()
4955 }
4956 });
4957 cy.add({
4958 group: 'edges',
4959 data: {
4960 source: rightStr,
4961 target: node.id()
4962 }
4963 });
4964 return node.id();
4965 } else if (root.value) {
4966 return root.value.id();
4967 }
4968};
4969
4970var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
4971 if (!root) return [];
4972 var left = [],
4973 right = [],
4974 leaves = [];
4975
4976 if (k === 0) {
4977 // don't cut tree, simply return all nodes as 1 single cluster
4978 if (root.left) getAllChildren(root.left, left);
4979 if (root.right) getAllChildren(root.right, right);
4980 leaves = left.concat(right);
4981 return [cy.collection(leaves)];
4982 } else if (k === 1) {
4983 // cut at root
4984 if (root.value) {
4985 // leaf node
4986 return [cy.collection(root.value)];
4987 } else {
4988 if (root.left) getAllChildren(root.left, left);
4989 if (root.right) getAllChildren(root.right, right);
4990 return [cy.collection(left), cy.collection(right)];
4991 }
4992 } else {
4993 if (root.value) {
4994 return [cy.collection(root.value)];
4995 } else {
4996 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
4997 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
4998 return left.concat(right);
4999 }
5000 }
5001};
5002/* eslint-enable */
5003
5004
5005var hierarchicalClustering = function hierarchicalClustering(options) {
5006 var cy = this.cy();
5007 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5008
5009 var opts = setOptions$2(options);
5010 var attrs = opts.attributes;
5011
5012 var getDist = function getDist(n1, n2) {
5013 return clusteringDistance(opts.distance, attrs.length, function (i) {
5014 return attrs[i](n1);
5015 }, function (i) {
5016 return attrs[i](n2);
5017 }, n1, n2);
5018 }; // Begin hierarchical algorithm
5019
5020
5021 var clusters = [];
5022 var dists = []; // distances between each pair of clusters
5023
5024 var mins = []; // closest cluster for each cluster
5025
5026 var index = []; // hash of all clusters by key
5027 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5028
5029 for (var n = 0; n < nodes.length; n++) {
5030 var cluster = {
5031 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5032 key: n,
5033 index: n
5034 };
5035 clusters[n] = cluster;
5036 index[n] = cluster;
5037 dists[n] = [];
5038 mins[n] = 0;
5039 } // Calculate the distance between each pair of clusters
5040
5041
5042 for (var i = 0; i < clusters.length; i++) {
5043 for (var j = 0; j <= i; j++) {
5044 var dist = void 0;
5045
5046 if (opts.mode === 'dendrogram') {
5047 // modes store cluster values differently
5048 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5049 } else {
5050 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5051 }
5052
5053 dists[i][j] = dist;
5054 dists[j][i] = dist;
5055
5056 if (dist < dists[i][mins[i]]) {
5057 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5058 }
5059 }
5060 } // Find the closest pair of clusters and merge them into a single cluster.
5061 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5062
5063
5064 var merged = mergeClosest(clusters, index, dists, mins, opts);
5065
5066 while (merged) {
5067 merged = mergeClosest(clusters, index, dists, mins, opts);
5068 }
5069
5070 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5071 // in addition to returning the clusters.
5072
5073 if (opts.mode === 'dendrogram') {
5074 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5075 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5076 } else {
5077 // Regular mode simply returns the clusters
5078 retClusters = new Array(clusters.length);
5079 clusters.forEach(function (cluster, i) {
5080 // Clean up meta data used for clustering
5081 cluster.key = cluster.index = null;
5082 retClusters[i] = cy.collection(cluster.value);
5083 });
5084 }
5085
5086 return retClusters;
5087};
5088
5089var hierarchicalClustering$1 = {
5090 hierarchicalClustering: hierarchicalClustering,
5091 hca: hierarchicalClustering
5092};
5093
5094// Implemented by Zoe Xi @zoexi for GSOC 2016
5095var defaults$7 = defaults({
5096 distance: 'euclidean',
5097 // distance metric to compare attributes between two nodes
5098 preference: 'median',
5099 // suitability of a data point to serve as an exemplar
5100 damping: 0.8,
5101 // damping factor between [0.5, 1)
5102 maxIterations: 1000,
5103 // max number of iterations to run
5104 minIterations: 100,
5105 // min number of iterations to run in order for clustering to stop
5106 attributes: [// functions to quantify the similarity between any two points
5107 // e.g. node => node.data('weight')
5108 ]
5109});
5110
5111var setOptions$3 = function setOptions(options) {
5112 var dmp = options.damping;
5113 var pref = options.preference;
5114
5115 if (!(0.5 <= dmp && dmp < 1)) {
5116 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5117 }
5118
5119 var validPrefs = ['median', 'mean', 'min', 'max'];
5120
5121 if (!(validPrefs.some(function (v) {
5122 return v === pref;
5123 }) || number(pref))) {
5124 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5125 return "'".concat(p, "'");
5126 }).join(', '), "] or a number. Got: ").concat(pref));
5127 }
5128
5129 return defaults$7(options);
5130};
5131/* eslint-enable */
5132
5133
5134var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5135 var attr = function attr(n, i) {
5136 return attributes[i](n);
5137 }; // nb negative because similarity should have an inverse relationship to distance
5138
5139
5140 return -clusteringDistance(type, attributes.length, function (i) {
5141 return attr(n1, i);
5142 }, function (i) {
5143 return attr(n2, i);
5144 }, n1, n2);
5145};
5146
5147var getPreference = function getPreference(S, preference) {
5148 // larger preference = greater # of clusters
5149 var p = null;
5150
5151 if (preference === 'median') {
5152 p = median(S);
5153 } else if (preference === 'mean') {
5154 p = mean(S);
5155 } else if (preference === 'min') {
5156 p = min(S);
5157 } else if (preference === 'max') {
5158 p = max(S);
5159 } else {
5160 // Custom preference number, as set by user
5161 p = preference;
5162 }
5163
5164 return p;
5165};
5166
5167var findExemplars = function findExemplars(n, R, A) {
5168 var indices = [];
5169
5170 for (var i = 0; i < n; i++) {
5171 if (R[i * n + i] + A[i * n + i] > 0) {
5172 indices.push(i);
5173 }
5174 }
5175
5176 return indices;
5177};
5178
5179var assignClusters = function assignClusters(n, S, exemplars) {
5180 var clusters = [];
5181
5182 for (var i = 0; i < n; i++) {
5183 var index = -1;
5184 var max = -Infinity;
5185
5186 for (var ei = 0; ei < exemplars.length; ei++) {
5187 var e = exemplars[ei];
5188
5189 if (S[i * n + e] > max) {
5190 index = e;
5191 max = S[i * n + e];
5192 }
5193 }
5194
5195 if (index > 0) {
5196 clusters.push(index);
5197 }
5198 }
5199
5200 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5201 clusters[exemplars[_ei]] = exemplars[_ei];
5202 }
5203
5204 return clusters;
5205};
5206
5207var assign$2 = function assign(n, S, exemplars) {
5208 var clusters = assignClusters(n, S, exemplars);
5209
5210 for (var ei = 0; ei < exemplars.length; ei++) {
5211 var ii = [];
5212
5213 for (var c = 0; c < clusters.length; c++) {
5214 if (clusters[c] === exemplars[ei]) {
5215 ii.push(c);
5216 }
5217 }
5218
5219 var maxI = -1;
5220 var maxSum = -Infinity;
5221
5222 for (var i = 0; i < ii.length; i++) {
5223 var sum = 0;
5224
5225 for (var j = 0; j < ii.length; j++) {
5226 sum += S[ii[j] * n + ii[i]];
5227 }
5228
5229 if (sum > maxSum) {
5230 maxI = i;
5231 maxSum = sum;
5232 }
5233 }
5234
5235 exemplars[ei] = ii[maxI];
5236 }
5237
5238 clusters = assignClusters(n, S, exemplars);
5239 return clusters;
5240};
5241
5242var affinityPropagation = function affinityPropagation(options) {
5243 var cy = this.cy();
5244 var nodes = this.nodes();
5245 var opts = setOptions$3(options); // Map each node to its position in node array
5246
5247 var id2position = {};
5248
5249 for (var i = 0; i < nodes.length; i++) {
5250 id2position[nodes[i].id()] = i;
5251 } // Begin affinity propagation algorithm
5252
5253
5254 var n; // number of data points
5255
5256 var n2; // size of matrices
5257
5258 var S; // similarity matrix (1D array)
5259
5260 var p; // preference/suitability of a data point to serve as an exemplar
5261
5262 var R; // responsibility matrix (1D array)
5263
5264 var A; // availability matrix (1D array)
5265
5266 n = nodes.length;
5267 n2 = n * n; // Initialize and build S similarity matrix
5268
5269 S = new Array(n2);
5270
5271 for (var _i = 0; _i < n2; _i++) {
5272 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5273 }
5274
5275 for (var _i2 = 0; _i2 < n; _i2++) {
5276 for (var j = 0; j < n; j++) {
5277 if (_i2 !== j) {
5278 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5279 }
5280 }
5281 } // Place preferences on the diagonal of S
5282
5283
5284 p = getPreference(S, opts.preference);
5285
5286 for (var _i3 = 0; _i3 < n; _i3++) {
5287 S[_i3 * n + _i3] = p;
5288 } // Initialize R responsibility matrix
5289
5290
5291 R = new Array(n2);
5292
5293 for (var _i4 = 0; _i4 < n2; _i4++) {
5294 R[_i4] = 0.0;
5295 } // Initialize A availability matrix
5296
5297
5298 A = new Array(n2);
5299
5300 for (var _i5 = 0; _i5 < n2; _i5++) {
5301 A[_i5] = 0.0;
5302 }
5303
5304 var old = new Array(n);
5305 var Rp = new Array(n);
5306 var se = new Array(n);
5307
5308 for (var _i6 = 0; _i6 < n; _i6++) {
5309 old[_i6] = 0.0;
5310 Rp[_i6] = 0.0;
5311 se[_i6] = 0;
5312 }
5313
5314 var e = new Array(n * opts.minIterations);
5315
5316 for (var _i7 = 0; _i7 < e.length; _i7++) {
5317 e[_i7] = 0;
5318 }
5319
5320 var iter;
5321
5322 for (iter = 0; iter < opts.maxIterations; iter++) {
5323 // main algorithmic loop
5324 // Update R responsibility matrix
5325 for (var _i8 = 0; _i8 < n; _i8++) {
5326 var max = -Infinity,
5327 max2 = -Infinity,
5328 maxI = -1,
5329 AS = 0.0;
5330
5331 for (var _j = 0; _j < n; _j++) {
5332 old[_j] = R[_i8 * n + _j];
5333 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5334
5335 if (AS >= max) {
5336 max2 = max;
5337 max = AS;
5338 maxI = _j;
5339 } else if (AS > max2) {
5340 max2 = AS;
5341 }
5342 }
5343
5344 for (var _j2 = 0; _j2 < n; _j2++) {
5345 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5346 }
5347
5348 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5349 } // Update A availability matrix
5350
5351
5352 for (var _i9 = 0; _i9 < n; _i9++) {
5353 var sum = 0;
5354
5355 for (var _j3 = 0; _j3 < n; _j3++) {
5356 old[_j3] = A[_j3 * n + _i9];
5357 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5358 sum += Rp[_j3];
5359 }
5360
5361 sum -= Rp[_i9];
5362 Rp[_i9] = R[_i9 * n + _i9];
5363 sum += Rp[_i9];
5364
5365 for (var _j4 = 0; _j4 < n; _j4++) {
5366 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5367 }
5368
5369 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5370 } // Check for convergence
5371
5372
5373 var K = 0;
5374
5375 for (var _i10 = 0; _i10 < n; _i10++) {
5376 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5377 e[iter % opts.minIterations * n + _i10] = E;
5378 K += E;
5379 }
5380
5381 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5382 var _sum = 0;
5383
5384 for (var _i11 = 0; _i11 < n; _i11++) {
5385 se[_i11] = 0;
5386
5387 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5388 se[_i11] += e[_j5 * n + _i11];
5389 }
5390
5391 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5392 _sum++;
5393 }
5394 }
5395
5396 if (_sum === n) {
5397 // then we have convergence
5398 break;
5399 }
5400 }
5401 } // Identify exemplars (cluster centers)
5402
5403
5404 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5405
5406 var clusterIndices = assign$2(n, S, exemplarsIndices);
5407 var clusters = {};
5408
5409 for (var c = 0; c < exemplarsIndices.length; c++) {
5410 clusters[exemplarsIndices[c]] = [];
5411 }
5412
5413 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5414 var pos = id2position[nodes[_i12].id()];
5415
5416 var clusterIndex = clusterIndices[pos];
5417
5418 if (clusterIndex != null) {
5419 // the node may have not been assigned a cluster if no valid attributes were specified
5420 clusters[clusterIndex].push(nodes[_i12]);
5421 }
5422 }
5423
5424 var retClusters = new Array(exemplarsIndices.length);
5425
5426 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5427 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5428 }
5429
5430 return retClusters;
5431};
5432
5433var affinityPropagation$1 = {
5434 affinityPropagation: affinityPropagation,
5435 ap: affinityPropagation
5436};
5437
5438var hierholzerDefaults = defaults({
5439 root: undefined,
5440 directed: false
5441});
5442var elesfn$b = {
5443 hierholzer: function hierholzer(options) {
5444 if (!plainObject(options)) {
5445 var args = arguments;
5446 options = {
5447 root: args[0],
5448 directed: args[1]
5449 };
5450 }
5451
5452 var _hierholzerDefaults = hierholzerDefaults(options),
5453 root = _hierholzerDefaults.root,
5454 directed = _hierholzerDefaults.directed;
5455
5456 var eles = this;
5457 var dflag = false;
5458 var oddIn;
5459 var oddOut;
5460 var startVertex;
5461 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5462 var nodes = {};
5463 var edges = {};
5464
5465 if (directed) {
5466 eles.forEach(function (ele) {
5467 var id = ele.id();
5468
5469 if (ele.isNode()) {
5470 var ind = ele.indegree(true);
5471 var outd = ele.outdegree(true);
5472 var d1 = ind - outd;
5473 var d2 = outd - ind;
5474
5475 if (d1 == 1) {
5476 if (oddIn) dflag = true;else oddIn = id;
5477 } else if (d2 == 1) {
5478 if (oddOut) dflag = true;else oddOut = id;
5479 } else if (d2 > 1 || d1 > 1) {
5480 dflag = true;
5481 }
5482
5483 nodes[id] = [];
5484 ele.outgoers().forEach(function (e) {
5485 if (e.isEdge()) nodes[id].push(e.id());
5486 });
5487 } else {
5488 edges[id] = [undefined, ele.target().id()];
5489 }
5490 });
5491 } else {
5492 eles.forEach(function (ele) {
5493 var id = ele.id();
5494
5495 if (ele.isNode()) {
5496 var d = ele.degree(true);
5497
5498 if (d % 2) {
5499 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5500 }
5501
5502 nodes[id] = [];
5503 ele.connectedEdges().forEach(function (e) {
5504 return nodes[id].push(e.id());
5505 });
5506 } else {
5507 edges[id] = [ele.source().id(), ele.target().id()];
5508 }
5509 });
5510 }
5511
5512 var result = {
5513 found: false,
5514 trail: undefined
5515 };
5516 if (dflag) return result;else if (oddOut && oddIn) {
5517 if (directed) {
5518 if (startVertex && oddOut != startVertex) {
5519 return result;
5520 }
5521
5522 startVertex = oddOut;
5523 } else {
5524 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5525 return result;
5526 } else if (!startVertex) {
5527 startVertex = oddOut;
5528 }
5529 }
5530 } else {
5531 if (!startVertex) startVertex = eles[0].id();
5532 }
5533
5534 var walk = function walk(v) {
5535 var currentNode = v;
5536 var subtour = [v];
5537 var adj, adjTail, adjHead;
5538
5539 while (nodes[currentNode].length) {
5540 adj = nodes[currentNode].shift();
5541 adjTail = edges[adj][0];
5542 adjHead = edges[adj][1];
5543
5544 if (currentNode != adjHead) {
5545 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5546 return e != adj;
5547 });
5548 currentNode = adjHead;
5549 } else if (!directed && currentNode != adjTail) {
5550 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5551 return e != adj;
5552 });
5553 currentNode = adjTail;
5554 }
5555
5556 subtour.unshift(adj);
5557 subtour.unshift(currentNode);
5558 }
5559
5560 return subtour;
5561 };
5562
5563 var trail = [];
5564 var subtour = [];
5565 subtour = walk(startVertex);
5566
5567 while (subtour.length != 1) {
5568 if (nodes[subtour[0]].length == 0) {
5569 trail.unshift(eles.getElementById(subtour.shift()));
5570 trail.unshift(eles.getElementById(subtour.shift()));
5571 } else {
5572 subtour = walk(subtour.shift()).concat(subtour);
5573 }
5574 }
5575
5576 trail.unshift(eles.getElementById(subtour.shift())); // final node
5577
5578 for (var d in nodes) {
5579 if (nodes[d].length) {
5580 return result;
5581 }
5582 }
5583
5584 result.found = true;
5585 result.trail = this.spawn(trail, true);
5586 return result;
5587 }
5588};
5589
5590var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5591 var eles = this;
5592 var nodes = {};
5593 var id = 0;
5594 var edgeCount = 0;
5595 var components = [];
5596 var stack = [];
5597 var visitedEdges = {};
5598
5599 var buildComponent = function buildComponent(x, y) {
5600 var i = stack.length - 1;
5601 var cutset = [];
5602 var component = eles.spawn();
5603
5604 while (stack[i].x != x || stack[i].y != y) {
5605 cutset.push(stack.pop().edge);
5606 i--;
5607 }
5608
5609 cutset.push(stack.pop().edge);
5610 cutset.forEach(function (edge) {
5611 var connectedNodes = edge.connectedNodes().intersection(eles);
5612 component.merge(edge);
5613 connectedNodes.forEach(function (node) {
5614 var nodeId = node.id();
5615 var connectedEdges = node.connectedEdges().intersection(eles);
5616 component.merge(node);
5617
5618 if (!nodes[nodeId].cutVertex) {
5619 component.merge(connectedEdges);
5620 } else {
5621 component.merge(connectedEdges.filter(function (edge) {
5622 return edge.isLoop();
5623 }));
5624 }
5625 });
5626 });
5627 components.push(component);
5628 };
5629
5630 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5631 if (root === parent) edgeCount += 1;
5632 nodes[currentNode] = {
5633 id: id,
5634 low: id++,
5635 cutVertex: false
5636 };
5637 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5638
5639 if (edges.size() === 0) {
5640 components.push(eles.spawn(eles.getElementById(currentNode)));
5641 } else {
5642 var sourceId, targetId, otherNodeId, edgeId;
5643 edges.forEach(function (edge) {
5644 sourceId = edge.source().id();
5645 targetId = edge.target().id();
5646 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5647
5648 if (otherNodeId !== parent) {
5649 edgeId = edge.id();
5650
5651 if (!visitedEdges[edgeId]) {
5652 visitedEdges[edgeId] = true;
5653 stack.push({
5654 x: currentNode,
5655 y: otherNodeId,
5656 edge: edge
5657 });
5658 }
5659
5660 if (!(otherNodeId in nodes)) {
5661 biconnectedSearch(root, otherNodeId, currentNode);
5662 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5663
5664 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5665 nodes[currentNode].cutVertex = true;
5666 buildComponent(currentNode, otherNodeId);
5667 }
5668 } else {
5669 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5670 }
5671 }
5672 });
5673 }
5674 };
5675
5676 eles.forEach(function (ele) {
5677 if (ele.isNode()) {
5678 var nodeId = ele.id();
5679
5680 if (!(nodeId in nodes)) {
5681 edgeCount = 0;
5682 biconnectedSearch(nodeId, nodeId);
5683 nodes[nodeId].cutVertex = edgeCount > 1;
5684 }
5685 }
5686 });
5687 var cutVertices = Object.keys(nodes).filter(function (id) {
5688 return nodes[id].cutVertex;
5689 }).map(function (id) {
5690 return eles.getElementById(id);
5691 });
5692 return {
5693 cut: eles.spawn(cutVertices),
5694 components: components
5695 };
5696};
5697
5698var hopcroftTarjanBiconnected$1 = {
5699 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5700 htbc: hopcroftTarjanBiconnected,
5701 htb: hopcroftTarjanBiconnected,
5702 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5703};
5704
5705var tarjanStronglyConnected = function tarjanStronglyConnected() {
5706 var eles = this;
5707 var nodes = {};
5708 var index = 0;
5709 var components = [];
5710 var stack = [];
5711 var cut = eles.spawn(eles);
5712
5713 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5714 stack.push(sourceNodeId);
5715 nodes[sourceNodeId] = {
5716 index: index,
5717 low: index++,
5718 explored: false
5719 };
5720 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5721 connectedEdges.forEach(function (edge) {
5722 var targetNodeId = edge.target().id();
5723
5724 if (targetNodeId !== sourceNodeId) {
5725 if (!(targetNodeId in nodes)) {
5726 stronglyConnectedSearch(targetNodeId);
5727 }
5728
5729 if (!nodes[targetNodeId].explored) {
5730 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5731 }
5732 }
5733 });
5734
5735 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5736 var componentNodes = eles.spawn();
5737
5738 for (;;) {
5739 var nodeId = stack.pop();
5740 componentNodes.merge(eles.getElementById(nodeId));
5741 nodes[nodeId].low = nodes[sourceNodeId].index;
5742 nodes[nodeId].explored = true;
5743
5744 if (nodeId === sourceNodeId) {
5745 break;
5746 }
5747 }
5748
5749 var componentEdges = componentNodes.edgesWith(componentNodes);
5750 var component = componentNodes.merge(componentEdges);
5751 components.push(component);
5752 cut = cut.difference(component);
5753 }
5754 };
5755
5756 eles.forEach(function (ele) {
5757 if (ele.isNode()) {
5758 var nodeId = ele.id();
5759
5760 if (!(nodeId in nodes)) {
5761 stronglyConnectedSearch(nodeId);
5762 }
5763 }
5764 });
5765 return {
5766 cut: cut,
5767 components: components
5768 };
5769};
5770
5771var tarjanStronglyConnected$1 = {
5772 tarjanStronglyConnected: tarjanStronglyConnected,
5773 tsc: tarjanStronglyConnected,
5774 tscc: tarjanStronglyConnected,
5775 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5776};
5777
5778var elesfn$c = {};
5779[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) {
5780 extend(elesfn$c, props);
5781});
5782
5783/*!
5784Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5785Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5786Licensed under The MIT License (http://opensource.org/licenses/MIT)
5787*/
5788
5789/* promise states [Promises/A+ 2.1] */
5790var STATE_PENDING = 0;
5791/* [Promises/A+ 2.1.1] */
5792
5793var STATE_FULFILLED = 1;
5794/* [Promises/A+ 2.1.2] */
5795
5796var STATE_REJECTED = 2;
5797/* [Promises/A+ 2.1.3] */
5798
5799/* promise object constructor */
5800
5801var api = function api(executor) {
5802 /* optionally support non-constructor/plain-function call */
5803 if (!(this instanceof api)) return new api(executor);
5804 /* initialize object */
5805
5806 this.id = 'Thenable/1.0.7';
5807 this.state = STATE_PENDING;
5808 /* initial state */
5809
5810 this.fulfillValue = undefined;
5811 /* initial value */
5812
5813 /* [Promises/A+ 1.3, 2.1.2.2] */
5814
5815 this.rejectReason = undefined;
5816 /* initial reason */
5817
5818 /* [Promises/A+ 1.5, 2.1.3.2] */
5819
5820 this.onFulfilled = [];
5821 /* initial handlers */
5822
5823 this.onRejected = [];
5824 /* initial handlers */
5825
5826 /* provide optional information-hiding proxy */
5827
5828 this.proxy = {
5829 then: this.then.bind(this)
5830 };
5831 /* support optional executor function */
5832
5833 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5834};
5835/* promise API methods */
5836
5837
5838api.prototype = {
5839 /* promise resolving methods */
5840 fulfill: function fulfill(value) {
5841 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5842 },
5843 reject: function reject(value) {
5844 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5845 },
5846
5847 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5848 then: function then(onFulfilled, onRejected) {
5849 var curr = this;
5850 var next = new api();
5851 /* [Promises/A+ 2.2.7] */
5852
5853 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5854 /* [Promises/A+ 2.2.2/2.2.6] */
5855
5856 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5857 /* [Promises/A+ 2.2.3/2.2.6] */
5858
5859 execute(curr);
5860 return next.proxy;
5861 /* [Promises/A+ 2.2.7, 3.3] */
5862 }
5863};
5864/* deliver an action */
5865
5866var deliver = function deliver(curr, state, name, value) {
5867 if (curr.state === STATE_PENDING) {
5868 curr.state = state;
5869 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5870
5871 curr[name] = value;
5872 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5873
5874 execute(curr);
5875 }
5876
5877 return curr;
5878};
5879/* execute all handlers */
5880
5881
5882var execute = function execute(curr) {
5883 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5884};
5885/* execute particular set of handlers */
5886
5887
5888var execute_handlers = function execute_handlers(curr, name, value) {
5889 /* global setImmediate: true */
5890
5891 /* global setTimeout: true */
5892
5893 /* short-circuit processing */
5894 if (curr[name].length === 0) return;
5895 /* iterate over all handlers, exactly once */
5896
5897 var handlers = curr[name];
5898 curr[name] = [];
5899 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5900
5901 var func = function func() {
5902 for (var i = 0; i < handlers.length; i++) {
5903 handlers[i](value);
5904 }
5905 /* [Promises/A+ 2.2.5] */
5906
5907 };
5908 /* execute procedure asynchronously */
5909
5910 /* [Promises/A+ 2.2.4, 3.1] */
5911
5912
5913 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5914};
5915/* generate a resolver function */
5916
5917
5918var resolver = function resolver(cb, next, method) {
5919 return function (value) {
5920 if (typeof cb !== 'function')
5921 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5922 next[method].call(next, value);
5923 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5924 else {
5925 var result;
5926
5927 try {
5928 result = cb(value);
5929 }
5930 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5931 catch (e) {
5932 next.reject(e);
5933 /* [Promises/A+ 2.2.7.2] */
5934
5935 return;
5936 }
5937
5938 resolve(next, result);
5939 /* [Promises/A+ 2.2.7.1] */
5940 }
5941 };
5942};
5943/* "Promise Resolution Procedure" */
5944
5945/* [Promises/A+ 2.3] */
5946
5947
5948var resolve = function resolve(promise, x) {
5949 /* sanity check arguments */
5950
5951 /* [Promises/A+ 2.3.1] */
5952 if (promise === x || promise.proxy === x) {
5953 promise.reject(new TypeError('cannot resolve promise with itself'));
5954 return;
5955 }
5956 /* surgically check for a "then" method
5957 (mainly to just call the "getter" of "then" only once) */
5958
5959
5960 var then;
5961
5962 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
5963 try {
5964 then = x.then;
5965 }
5966 /* [Promises/A+ 2.3.3.1, 3.5] */
5967 catch (e) {
5968 promise.reject(e);
5969 /* [Promises/A+ 2.3.3.2] */
5970
5971 return;
5972 }
5973 }
5974 /* handle own Thenables [Promises/A+ 2.3.2]
5975 and similar "thenables" [Promises/A+ 2.3.3] */
5976
5977
5978 if (typeof then === 'function') {
5979 var resolved = false;
5980
5981 try {
5982 /* call retrieved "then" method */
5983
5984 /* [Promises/A+ 2.3.3.3] */
5985 then.call(x,
5986 /* resolvePromise */
5987
5988 /* [Promises/A+ 2.3.3.3.1] */
5989 function (y) {
5990 if (resolved) return;
5991 resolved = true;
5992 /* [Promises/A+ 2.3.3.3.3] */
5993
5994 if (y === x)
5995 /* [Promises/A+ 3.6] */
5996 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
5997 },
5998 /* rejectPromise */
5999
6000 /* [Promises/A+ 2.3.3.3.2] */
6001 function (r) {
6002 if (resolved) return;
6003 resolved = true;
6004 /* [Promises/A+ 2.3.3.3.3] */
6005
6006 promise.reject(r);
6007 });
6008 } catch (e) {
6009 if (!resolved)
6010 /* [Promises/A+ 2.3.3.3.3] */
6011 promise.reject(e);
6012 /* [Promises/A+ 2.3.3.3.4] */
6013 }
6014
6015 return;
6016 }
6017 /* handle other values */
6018
6019
6020 promise.fulfill(x);
6021 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6022}; // so we always have Promise.all()
6023
6024
6025api.all = function (ps) {
6026 return new api(function (resolveAll, rejectAll) {
6027 var vals = new Array(ps.length);
6028 var doneCount = 0;
6029
6030 var fulfill = function fulfill(i, val) {
6031 vals[i] = val;
6032 doneCount++;
6033
6034 if (doneCount === ps.length) {
6035 resolveAll(vals);
6036 }
6037 };
6038
6039 for (var i = 0; i < ps.length; i++) {
6040 (function (i) {
6041 var p = ps[i];
6042 var isPromise = p != null && p.then != null;
6043
6044 if (isPromise) {
6045 p.then(function (val) {
6046 fulfill(i, val);
6047 }, function (err) {
6048 rejectAll(err);
6049 });
6050 } else {
6051 var val = p;
6052 fulfill(i, val);
6053 }
6054 })(i);
6055 }
6056 });
6057};
6058
6059api.resolve = function (val) {
6060 return new api(function (resolve, reject) {
6061 resolve(val);
6062 });
6063};
6064
6065api.reject = function (val) {
6066 return new api(function (resolve, reject) {
6067 reject(val);
6068 });
6069};
6070
6071var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6072
6073var Animation = function Animation(target, opts, opts2) {
6074 var isCore = core(target);
6075 var isEle = !isCore;
6076
6077 var _p = this._private = extend({
6078 duration: 1000
6079 }, opts, opts2);
6080
6081 _p.target = target;
6082 _p.style = _p.style || _p.css;
6083 _p.started = false;
6084 _p.playing = false;
6085 _p.hooked = false;
6086 _p.applying = false;
6087 _p.progress = 0;
6088 _p.completes = [];
6089 _p.frames = [];
6090
6091 if (_p.complete && fn(_p.complete)) {
6092 _p.completes.push(_p.complete);
6093 }
6094
6095 if (isEle) {
6096 var pos = target.position();
6097 _p.startPosition = _p.startPosition || {
6098 x: pos.x,
6099 y: pos.y
6100 };
6101 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6102 }
6103
6104 if (isCore) {
6105 var pan = target.pan();
6106 _p.startPan = {
6107 x: pan.x,
6108 y: pan.y
6109 };
6110 _p.startZoom = target.zoom();
6111 } // for future timeline/animations impl
6112
6113
6114 this.length = 1;
6115 this[0] = this;
6116};
6117
6118var anifn = Animation.prototype;
6119extend(anifn, {
6120 instanceString: function instanceString() {
6121 return 'animation';
6122 },
6123 hook: function hook() {
6124 var _p = this._private;
6125
6126 if (!_p.hooked) {
6127 // add to target's animation queue
6128 var q;
6129 var tAni = _p.target._private.animation;
6130
6131 if (_p.queue) {
6132 q = tAni.queue;
6133 } else {
6134 q = tAni.current;
6135 }
6136
6137 q.push(this); // add to the animation loop pool
6138
6139 if (elementOrCollection(_p.target)) {
6140 _p.target.cy().addToAnimationPool(_p.target);
6141 }
6142
6143 _p.hooked = true;
6144 }
6145
6146 return this;
6147 },
6148 play: function play() {
6149 var _p = this._private; // autorewind
6150
6151 if (_p.progress === 1) {
6152 _p.progress = 0;
6153 }
6154
6155 _p.playing = true;
6156 _p.started = false; // needs to be started by animation loop
6157
6158 _p.stopped = false;
6159 this.hook(); // the animation loop will start the animation...
6160
6161 return this;
6162 },
6163 playing: function playing() {
6164 return this._private.playing;
6165 },
6166 apply: function apply() {
6167 var _p = this._private;
6168 _p.applying = true;
6169 _p.started = false; // needs to be started by animation loop
6170
6171 _p.stopped = false;
6172 this.hook(); // the animation loop will apply the animation at this progress
6173
6174 return this;
6175 },
6176 applying: function applying() {
6177 return this._private.applying;
6178 },
6179 pause: function pause() {
6180 var _p = this._private;
6181 _p.playing = false;
6182 _p.started = false;
6183 return this;
6184 },
6185 stop: function stop() {
6186 var _p = this._private;
6187 _p.playing = false;
6188 _p.started = false;
6189 _p.stopped = true; // to be removed from animation queues
6190
6191 return this;
6192 },
6193 rewind: function rewind() {
6194 return this.progress(0);
6195 },
6196 fastforward: function fastforward() {
6197 return this.progress(1);
6198 },
6199 time: function time(t) {
6200 var _p = this._private;
6201
6202 if (t === undefined) {
6203 return _p.progress * _p.duration;
6204 } else {
6205 return this.progress(t / _p.duration);
6206 }
6207 },
6208 progress: function progress(p) {
6209 var _p = this._private;
6210 var wasPlaying = _p.playing;
6211
6212 if (p === undefined) {
6213 return _p.progress;
6214 } else {
6215 if (wasPlaying) {
6216 this.pause();
6217 }
6218
6219 _p.progress = p;
6220 _p.started = false;
6221
6222 if (wasPlaying) {
6223 this.play();
6224 }
6225 }
6226
6227 return this;
6228 },
6229 completed: function completed() {
6230 return this._private.progress === 1;
6231 },
6232 reverse: function reverse() {
6233 var _p = this._private;
6234 var wasPlaying = _p.playing;
6235
6236 if (wasPlaying) {
6237 this.pause();
6238 }
6239
6240 _p.progress = 1 - _p.progress;
6241 _p.started = false;
6242
6243 var swap = function swap(a, b) {
6244 var _pa = _p[a];
6245
6246 if (_pa == null) {
6247 return;
6248 }
6249
6250 _p[a] = _p[b];
6251 _p[b] = _pa;
6252 };
6253
6254 swap('zoom', 'startZoom');
6255 swap('pan', 'startPan');
6256 swap('position', 'startPosition'); // swap styles
6257
6258 if (_p.style) {
6259 for (var i = 0; i < _p.style.length; i++) {
6260 var prop = _p.style[i];
6261 var name = prop.name;
6262 var startStyleProp = _p.startStyle[name];
6263 _p.startStyle[name] = prop;
6264 _p.style[i] = startStyleProp;
6265 }
6266 }
6267
6268 if (wasPlaying) {
6269 this.play();
6270 }
6271
6272 return this;
6273 },
6274 promise: function promise(type) {
6275 var _p = this._private;
6276 var arr;
6277
6278 switch (type) {
6279 case 'frame':
6280 arr = _p.frames;
6281 break;
6282
6283 default:
6284 case 'complete':
6285 case 'completed':
6286 arr = _p.completes;
6287 }
6288
6289 return new Promise$1(function (resolve, reject) {
6290 arr.push(function () {
6291 resolve();
6292 });
6293 });
6294 }
6295});
6296anifn.complete = anifn.completed;
6297anifn.run = anifn.play;
6298anifn.running = anifn.playing;
6299
6300var define = {
6301 animated: function animated() {
6302 return function animatedImpl() {
6303 var self = this;
6304 var selfIsArrayLike = self.length !== undefined;
6305 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6306
6307 var cy = this._private.cy || this;
6308
6309 if (!cy.styleEnabled()) {
6310 return false;
6311 }
6312
6313 var ele = all[0];
6314
6315 if (ele) {
6316 return ele._private.animation.current.length > 0;
6317 }
6318 };
6319 },
6320 // animated
6321 clearQueue: function clearQueue() {
6322 return function clearQueueImpl() {
6323 var self = this;
6324 var selfIsArrayLike = self.length !== undefined;
6325 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6326
6327 var cy = this._private.cy || this;
6328
6329 if (!cy.styleEnabled()) {
6330 return this;
6331 }
6332
6333 for (var i = 0; i < all.length; i++) {
6334 var ele = all[i];
6335 ele._private.animation.queue = [];
6336 }
6337
6338 return this;
6339 };
6340 },
6341 // clearQueue
6342 delay: function delay() {
6343 return function delayImpl(time, complete) {
6344 var cy = this._private.cy || this;
6345
6346 if (!cy.styleEnabled()) {
6347 return this;
6348 }
6349
6350 return this.animate({
6351 delay: time,
6352 duration: time,
6353 complete: complete
6354 });
6355 };
6356 },
6357 // delay
6358 delayAnimation: function delayAnimation() {
6359 return function delayAnimationImpl(time, complete) {
6360 var cy = this._private.cy || this;
6361
6362 if (!cy.styleEnabled()) {
6363 return this;
6364 }
6365
6366 return this.animation({
6367 delay: time,
6368 duration: time,
6369 complete: complete
6370 });
6371 };
6372 },
6373 // delay
6374 animation: function animation() {
6375 return function animationImpl(properties, params) {
6376 var self = this;
6377 var selfIsArrayLike = self.length !== undefined;
6378 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6379
6380 var cy = this._private.cy || this;
6381 var isCore = !selfIsArrayLike;
6382 var isEles = !isCore;
6383
6384 if (!cy.styleEnabled()) {
6385 return this;
6386 }
6387
6388 var style = cy.style();
6389 properties = extend({}, properties, params);
6390 var propertiesEmpty = Object.keys(properties).length === 0;
6391
6392 if (propertiesEmpty) {
6393 return new Animation(all[0], properties); // nothing to animate
6394 }
6395
6396 if (properties.duration === undefined) {
6397 properties.duration = 400;
6398 }
6399
6400 switch (properties.duration) {
6401 case 'slow':
6402 properties.duration = 600;
6403 break;
6404
6405 case 'fast':
6406 properties.duration = 200;
6407 break;
6408 }
6409
6410 if (isEles) {
6411 properties.style = style.getPropsList(properties.style || properties.css);
6412 properties.css = undefined;
6413 }
6414
6415 if (isEles && properties.renderedPosition != null) {
6416 var rpos = properties.renderedPosition;
6417 var pan = cy.pan();
6418 var zoom = cy.zoom();
6419 properties.position = renderedToModelPosition(rpos, zoom, pan);
6420 } // override pan w/ panBy if set
6421
6422
6423 if (isCore && properties.panBy != null) {
6424 var panBy = properties.panBy;
6425 var cyPan = cy.pan();
6426 properties.pan = {
6427 x: cyPan.x + panBy.x,
6428 y: cyPan.y + panBy.y
6429 };
6430 } // override pan w/ center if set
6431
6432
6433 var center = properties.center || properties.centre;
6434
6435 if (isCore && center != null) {
6436 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6437
6438 if (centerPan != null) {
6439 properties.pan = centerPan;
6440 }
6441 } // override pan & zoom w/ fit if set
6442
6443
6444 if (isCore && properties.fit != null) {
6445 var fit = properties.fit;
6446 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6447
6448 if (fitVp != null) {
6449 properties.pan = fitVp.pan;
6450 properties.zoom = fitVp.zoom;
6451 }
6452 } // override zoom (& potentially pan) w/ zoom obj if set
6453
6454
6455 if (isCore && plainObject(properties.zoom)) {
6456 var vp = cy.getZoomedViewport(properties.zoom);
6457
6458 if (vp != null) {
6459 if (vp.zoomed) {
6460 properties.zoom = vp.zoom;
6461 }
6462
6463 if (vp.panned) {
6464 properties.pan = vp.pan;
6465 }
6466 } else {
6467 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6468 }
6469 }
6470
6471 return new Animation(all[0], properties);
6472 };
6473 },
6474 // animate
6475 animate: function animate() {
6476 return function animateImpl(properties, params) {
6477 var self = this;
6478 var selfIsArrayLike = self.length !== undefined;
6479 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6480
6481 var cy = this._private.cy || this;
6482
6483 if (!cy.styleEnabled()) {
6484 return this;
6485 }
6486
6487 if (params) {
6488 properties = extend({}, properties, params);
6489 } // manually hook and run the animation
6490
6491
6492 for (var i = 0; i < all.length; i++) {
6493 var ele = all[i];
6494 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6495 var ani = ele.animation(properties, queue ? {
6496 queue: true
6497 } : undefined);
6498 ani.play();
6499 }
6500
6501 return this; // chaining
6502 };
6503 },
6504 // animate
6505 stop: function stop() {
6506 return function stopImpl(clearQueue, jumpToEnd) {
6507 var self = this;
6508 var selfIsArrayLike = self.length !== undefined;
6509 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6510
6511 var cy = this._private.cy || this;
6512
6513 if (!cy.styleEnabled()) {
6514 return this;
6515 }
6516
6517 for (var i = 0; i < all.length; i++) {
6518 var ele = all[i];
6519 var _p = ele._private;
6520 var anis = _p.animation.current;
6521
6522 for (var j = 0; j < anis.length; j++) {
6523 var ani = anis[j];
6524 var ani_p = ani._private;
6525
6526 if (jumpToEnd) {
6527 // next iteration of the animation loop, the animation
6528 // will go straight to the end and be removed
6529 ani_p.duration = 0;
6530 }
6531 } // clear the queue of future animations
6532
6533
6534 if (clearQueue) {
6535 _p.animation.queue = [];
6536 }
6537
6538 if (!jumpToEnd) {
6539 _p.animation.current = [];
6540 }
6541 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6542
6543
6544 cy.notify('draw');
6545 return this;
6546 };
6547 } // stop
6548
6549}; // define
6550
6551var define$1 = {
6552 // access data field
6553 data: function data(params) {
6554 var defaults = {
6555 field: 'data',
6556 bindingEvent: 'data',
6557 allowBinding: false,
6558 allowSetting: false,
6559 allowGetting: false,
6560 settingEvent: 'data',
6561 settingTriggersEvent: false,
6562 triggerFnName: 'trigger',
6563 immutableKeys: {},
6564 // key => true if immutable
6565 updateStyle: false,
6566 beforeGet: function beforeGet(self) {},
6567 beforeSet: function beforeSet(self, obj) {},
6568 onSet: function onSet(self) {},
6569 canSet: function canSet(self) {
6570 return true;
6571 }
6572 };
6573 params = extend({}, defaults, params);
6574 return function dataImpl(name, value) {
6575 var p = params;
6576 var self = this;
6577 var selfIsArrayLike = self.length !== undefined;
6578 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6579
6580 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6581
6582 if (string(name)) {
6583 // set or get property
6584 // .data('foo')
6585 if (p.allowGetting && value === undefined) {
6586 // get
6587 var ret;
6588
6589 if (single) {
6590 p.beforeGet(single);
6591 ret = single._private[p.field][name];
6592 }
6593
6594 return ret; // .data('foo', 'bar')
6595 } else if (p.allowSetting && value !== undefined) {
6596 // set
6597 var valid = !p.immutableKeys[name];
6598
6599 if (valid) {
6600 var change = _defineProperty({}, name, value);
6601
6602 p.beforeSet(self, change);
6603
6604 for (var i = 0, l = all.length; i < l; i++) {
6605 var ele = all[i];
6606
6607 if (p.canSet(ele)) {
6608 ele._private[p.field][name] = value;
6609 }
6610 } // update mappers if asked
6611
6612
6613 if (p.updateStyle) {
6614 self.updateStyle();
6615 } // call onSet callback
6616
6617
6618 p.onSet(self);
6619
6620 if (p.settingTriggersEvent) {
6621 self[p.triggerFnName](p.settingEvent);
6622 }
6623 }
6624 } // .data({ 'foo': 'bar' })
6625
6626 } else if (p.allowSetting && plainObject(name)) {
6627 // extend
6628 var obj = name;
6629 var k, v;
6630 var keys = Object.keys(obj);
6631 p.beforeSet(self, obj);
6632
6633 for (var _i = 0; _i < keys.length; _i++) {
6634 k = keys[_i];
6635 v = obj[k];
6636
6637 var _valid = !p.immutableKeys[k];
6638
6639 if (_valid) {
6640 for (var j = 0; j < all.length; j++) {
6641 var _ele = all[j];
6642
6643 if (p.canSet(_ele)) {
6644 _ele._private[p.field][k] = v;
6645 }
6646 }
6647 }
6648 } // update mappers if asked
6649
6650
6651 if (p.updateStyle) {
6652 self.updateStyle();
6653 } // call onSet callback
6654
6655
6656 p.onSet(self);
6657
6658 if (p.settingTriggersEvent) {
6659 self[p.triggerFnName](p.settingEvent);
6660 } // .data(function(){ ... })
6661
6662 } else if (p.allowBinding && fn(name)) {
6663 // bind to event
6664 var fn$1 = name;
6665 self.on(p.bindingEvent, fn$1); // .data()
6666 } else if (p.allowGetting && name === undefined) {
6667 // get whole object
6668 var _ret;
6669
6670 if (single) {
6671 p.beforeGet(single);
6672 _ret = single._private[p.field];
6673 }
6674
6675 return _ret;
6676 }
6677
6678 return self; // maintain chainability
6679 }; // function
6680 },
6681 // data
6682 // remove data field
6683 removeData: function removeData(params) {
6684 var defaults = {
6685 field: 'data',
6686 event: 'data',
6687 triggerFnName: 'trigger',
6688 triggerEvent: false,
6689 immutableKeys: {} // key => true if immutable
6690
6691 };
6692 params = extend({}, defaults, params);
6693 return function removeDataImpl(names) {
6694 var p = params;
6695 var self = this;
6696 var selfIsArrayLike = self.length !== undefined;
6697 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6698 // .removeData('foo bar')
6699
6700 if (string(names)) {
6701 // then get the list of keys, and delete them
6702 var keys = names.split(/\s+/);
6703 var l = keys.length;
6704
6705 for (var i = 0; i < l; i++) {
6706 // delete each non-empty key
6707 var key = keys[i];
6708
6709 if (emptyString(key)) {
6710 continue;
6711 }
6712
6713 var valid = !p.immutableKeys[key]; // not valid if immutable
6714
6715 if (valid) {
6716 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6717 all[i_a]._private[p.field][key] = undefined;
6718 }
6719 }
6720 }
6721
6722 if (p.triggerEvent) {
6723 self[p.triggerFnName](p.event);
6724 } // .removeData()
6725
6726 } else if (names === undefined) {
6727 // then delete all keys
6728 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6729 var _privateFields = all[_i_a]._private[p.field];
6730
6731 var _keys = Object.keys(_privateFields);
6732
6733 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6734 var _key = _keys[_i2];
6735 var validKeyToDelete = !p.immutableKeys[_key];
6736
6737 if (validKeyToDelete) {
6738 _privateFields[_key] = undefined;
6739 }
6740 }
6741 }
6742
6743 if (p.triggerEvent) {
6744 self[p.triggerFnName](p.event);
6745 }
6746 }
6747
6748 return self; // maintain chaining
6749 }; // function
6750 } // removeData
6751
6752}; // define
6753
6754var define$2 = {
6755 eventAliasesOn: function eventAliasesOn(proto) {
6756 var p = proto;
6757 p.addListener = p.listen = p.bind = p.on;
6758 p.unlisten = p.unbind = p.off = p.removeListener;
6759 p.trigger = p.emit; // this is just a wrapper alias of .on()
6760
6761 p.pon = p.promiseOn = function (events, selector) {
6762 var self = this;
6763 var args = Array.prototype.slice.call(arguments, 0);
6764 return new Promise$1(function (resolve, reject) {
6765 var callback = function callback(e) {
6766 self.off.apply(self, offArgs);
6767 resolve(e);
6768 };
6769
6770 var onArgs = args.concat([callback]);
6771 var offArgs = onArgs.concat([]);
6772 self.on.apply(self, onArgs);
6773 });
6774 };
6775 }
6776}; // define
6777
6778// use this module to cherry pick functions into your prototype
6779var define$3 = {};
6780[define, define$1, define$2].forEach(function (m) {
6781 extend(define$3, m);
6782});
6783
6784var elesfn$d = {
6785 animate: define$3.animate(),
6786 animation: define$3.animation(),
6787 animated: define$3.animated(),
6788 clearQueue: define$3.clearQueue(),
6789 delay: define$3.delay(),
6790 delayAnimation: define$3.delayAnimation(),
6791 stop: define$3.stop()
6792};
6793
6794var elesfn$e = {
6795 classes: function classes(_classes) {
6796 var self = this;
6797
6798 if (_classes === undefined) {
6799 var ret = [];
6800
6801 self[0]._private.classes.forEach(function (cls) {
6802 return ret.push(cls);
6803 });
6804
6805 return ret;
6806 } else if (!array(_classes)) {
6807 // extract classes from string
6808 _classes = (_classes || '').match(/\S+/g) || [];
6809 }
6810
6811 var changed = [];
6812 var classesSet = new Set$1(_classes); // check and update each ele
6813
6814 for (var j = 0; j < self.length; j++) {
6815 var ele = self[j];
6816 var _p = ele._private;
6817 var eleClasses = _p.classes;
6818 var changedEle = false; // check if ele has all of the passed classes
6819
6820 for (var i = 0; i < _classes.length; i++) {
6821 var cls = _classes[i];
6822 var eleHasClass = eleClasses.has(cls);
6823
6824 if (!eleHasClass) {
6825 changedEle = true;
6826 break;
6827 }
6828 } // check if ele has classes outside of those passed
6829
6830
6831 if (!changedEle) {
6832 changedEle = eleClasses.size !== _classes.length;
6833 }
6834
6835 if (changedEle) {
6836 _p.classes = classesSet;
6837 changed.push(ele);
6838 }
6839 } // trigger update style on those eles that had class changes
6840
6841
6842 if (changed.length > 0) {
6843 this.spawn(changed).updateStyle().emit('class');
6844 }
6845
6846 return self;
6847 },
6848 addClass: function addClass(classes) {
6849 return this.toggleClass(classes, true);
6850 },
6851 hasClass: function hasClass(className) {
6852 var ele = this[0];
6853 return ele != null && ele._private.classes.has(className);
6854 },
6855 toggleClass: function toggleClass(classes, toggle) {
6856 if (!array(classes)) {
6857 // extract classes from string
6858 classes = classes.match(/\S+/g) || [];
6859 }
6860
6861 var self = this;
6862 var toggleUndefd = toggle === undefined;
6863 var changed = []; // eles who had classes changed
6864
6865 for (var i = 0, il = self.length; i < il; i++) {
6866 var ele = self[i];
6867 var eleClasses = ele._private.classes;
6868 var changedEle = false;
6869
6870 for (var j = 0; j < classes.length; j++) {
6871 var cls = classes[j];
6872 var hasClass = eleClasses.has(cls);
6873 var changedNow = false;
6874
6875 if (toggle || toggleUndefd && !hasClass) {
6876 eleClasses.add(cls);
6877 changedNow = true;
6878 } else if (!toggle || toggleUndefd && hasClass) {
6879 eleClasses["delete"](cls);
6880 changedNow = true;
6881 }
6882
6883 if (!changedEle && changedNow) {
6884 changed.push(ele);
6885 changedEle = true;
6886 }
6887 } // for j classes
6888
6889 } // for i eles
6890 // trigger update style on those eles that had class changes
6891
6892
6893 if (changed.length > 0) {
6894 this.spawn(changed).updateStyle().emit('class');
6895 }
6896
6897 return self;
6898 },
6899 removeClass: function removeClass(classes) {
6900 return this.toggleClass(classes, false);
6901 },
6902 flashClass: function flashClass(classes, duration) {
6903 var self = this;
6904
6905 if (duration == null) {
6906 duration = 250;
6907 } else if (duration === 0) {
6908 return self; // nothing to do really
6909 }
6910
6911 self.addClass(classes);
6912 setTimeout(function () {
6913 self.removeClass(classes);
6914 }, duration);
6915 return self;
6916 }
6917};
6918elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6919
6920var tokens = {
6921 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6922 // chars we need to escape in let names, etc
6923 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6924 // binary comparison op (used in data selectors)
6925 boolOp: '\\?|\\!|\\^',
6926 // boolean (unary) operators (used in data selectors)
6927 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6928 // string literals (used in data selectors) -- doublequotes | singlequotes
6929 number: number$1,
6930 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6931 meta: 'degree|indegree|outdegree',
6932 // allowed metadata fields (i.e. allowed functions to use from Collection)
6933 separator: '\\s*,\\s*',
6934 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6935 descendant: '\\s+',
6936 child: '\\s+>\\s+',
6937 subject: '\\$',
6938 group: 'node|edge|\\*',
6939 directedEdge: '\\s+->\\s+',
6940 undirectedEdge: '\\s+<->\\s+'
6941};
6942tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
6943
6944tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
6945
6946tokens.className = tokens.variable; // a class name (follows variable conventions)
6947
6948tokens.id = tokens.variable; // an element id (follows variable conventions)
6949
6950(function () {
6951 var ops, op, i; // add @ variants to comparatorOp
6952
6953 ops = tokens.comparatorOp.split('|');
6954
6955 for (i = 0; i < ops.length; i++) {
6956 op = ops[i];
6957 tokens.comparatorOp += '|@' + op;
6958 } // add ! variants to comparatorOp
6959
6960
6961 ops = tokens.comparatorOp.split('|');
6962
6963 for (i = 0; i < ops.length; i++) {
6964 op = ops[i];
6965
6966 if (op.indexOf('!') >= 0) {
6967 continue;
6968 } // skip ops that explicitly contain !
6969
6970
6971 if (op === '=') {
6972 continue;
6973 } // skip = b/c != is explicitly defined
6974
6975
6976 tokens.comparatorOp += '|\\!' + op;
6977 }
6978})();
6979
6980/**
6981 * Make a new query object
6982 *
6983 * @prop type {Type} The type enum (int) of the query
6984 * @prop checks List of checks to make against an ele to test for a match
6985 */
6986var newQuery = function newQuery() {
6987 return {
6988 checks: []
6989 };
6990};
6991
6992/**
6993 * A check type enum-like object. Uses integer values for fast match() lookup.
6994 * The ordering does not matter as long as the ints are unique.
6995 */
6996var Type = {
6997 /** E.g. node */
6998 GROUP: 0,
6999
7000 /** A collection of elements */
7001 COLLECTION: 1,
7002
7003 /** A filter(ele) function */
7004 FILTER: 2,
7005
7006 /** E.g. [foo > 1] */
7007 DATA_COMPARE: 3,
7008
7009 /** E.g. [foo] */
7010 DATA_EXIST: 4,
7011
7012 /** E.g. [?foo] */
7013 DATA_BOOL: 5,
7014
7015 /** E.g. [[degree > 2]] */
7016 META_COMPARE: 6,
7017
7018 /** E.g. :selected */
7019 STATE: 7,
7020
7021 /** E.g. #foo */
7022 ID: 8,
7023
7024 /** E.g. .foo */
7025 CLASS: 9,
7026
7027 /** E.g. #foo <-> #bar */
7028 UNDIRECTED_EDGE: 10,
7029
7030 /** E.g. #foo -> #bar */
7031 DIRECTED_EDGE: 11,
7032
7033 /** E.g. $#foo -> #bar */
7034 NODE_SOURCE: 12,
7035
7036 /** E.g. #foo -> $#bar */
7037 NODE_TARGET: 13,
7038
7039 /** E.g. $#foo <-> #bar */
7040 NODE_NEIGHBOR: 14,
7041
7042 /** E.g. #foo > #bar */
7043 CHILD: 15,
7044
7045 /** E.g. #foo #bar */
7046 DESCENDANT: 16,
7047
7048 /** E.g. $#foo > #bar */
7049 PARENT: 17,
7050
7051 /** E.g. $#foo #bar */
7052 ANCESTOR: 18,
7053
7054 /** E.g. #foo > $bar > #baz */
7055 COMPOUND_SPLIT: 19,
7056
7057 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7058 TRUE: 20
7059};
7060
7061var stateSelectors = [{
7062 selector: ':selected',
7063 matches: function matches(ele) {
7064 return ele.selected();
7065 }
7066}, {
7067 selector: ':unselected',
7068 matches: function matches(ele) {
7069 return !ele.selected();
7070 }
7071}, {
7072 selector: ':selectable',
7073 matches: function matches(ele) {
7074 return ele.selectable();
7075 }
7076}, {
7077 selector: ':unselectable',
7078 matches: function matches(ele) {
7079 return !ele.selectable();
7080 }
7081}, {
7082 selector: ':locked',
7083 matches: function matches(ele) {
7084 return ele.locked();
7085 }
7086}, {
7087 selector: ':unlocked',
7088 matches: function matches(ele) {
7089 return !ele.locked();
7090 }
7091}, {
7092 selector: ':visible',
7093 matches: function matches(ele) {
7094 return ele.visible();
7095 }
7096}, {
7097 selector: ':hidden',
7098 matches: function matches(ele) {
7099 return !ele.visible();
7100 }
7101}, {
7102 selector: ':transparent',
7103 matches: function matches(ele) {
7104 return ele.transparent();
7105 }
7106}, {
7107 selector: ':grabbed',
7108 matches: function matches(ele) {
7109 return ele.grabbed();
7110 }
7111}, {
7112 selector: ':free',
7113 matches: function matches(ele) {
7114 return !ele.grabbed();
7115 }
7116}, {
7117 selector: ':removed',
7118 matches: function matches(ele) {
7119 return ele.removed();
7120 }
7121}, {
7122 selector: ':inside',
7123 matches: function matches(ele) {
7124 return !ele.removed();
7125 }
7126}, {
7127 selector: ':grabbable',
7128 matches: function matches(ele) {
7129 return ele.grabbable();
7130 }
7131}, {
7132 selector: ':ungrabbable',
7133 matches: function matches(ele) {
7134 return !ele.grabbable();
7135 }
7136}, {
7137 selector: ':animated',
7138 matches: function matches(ele) {
7139 return ele.animated();
7140 }
7141}, {
7142 selector: ':unanimated',
7143 matches: function matches(ele) {
7144 return !ele.animated();
7145 }
7146}, {
7147 selector: ':parent',
7148 matches: function matches(ele) {
7149 return ele.isParent();
7150 }
7151}, {
7152 selector: ':childless',
7153 matches: function matches(ele) {
7154 return ele.isChildless();
7155 }
7156}, {
7157 selector: ':child',
7158 matches: function matches(ele) {
7159 return ele.isChild();
7160 }
7161}, {
7162 selector: ':orphan',
7163 matches: function matches(ele) {
7164 return ele.isOrphan();
7165 }
7166}, {
7167 selector: ':nonorphan',
7168 matches: function matches(ele) {
7169 return ele.isChild();
7170 }
7171}, {
7172 selector: ':compound',
7173 matches: function matches(ele) {
7174 if (ele.isNode()) {
7175 return ele.isParent();
7176 } else {
7177 return ele.source().isParent() || ele.target().isParent();
7178 }
7179 }
7180}, {
7181 selector: ':loop',
7182 matches: function matches(ele) {
7183 return ele.isLoop();
7184 }
7185}, {
7186 selector: ':simple',
7187 matches: function matches(ele) {
7188 return ele.isSimple();
7189 }
7190}, {
7191 selector: ':active',
7192 matches: function matches(ele) {
7193 return ele.active();
7194 }
7195}, {
7196 selector: ':inactive',
7197 matches: function matches(ele) {
7198 return !ele.active();
7199 }
7200}, {
7201 selector: ':backgrounding',
7202 matches: function matches(ele) {
7203 return ele.backgrounding();
7204 }
7205}, {
7206 selector: ':nonbackgrounding',
7207 matches: function matches(ele) {
7208 return !ele.backgrounding();
7209 }
7210}].sort(function (a, b) {
7211 // n.b. selectors that are starting substrings of others must have the longer ones first
7212 return descending(a.selector, b.selector);
7213});
7214
7215var lookup = function () {
7216 var selToFn = {};
7217 var s;
7218
7219 for (var i = 0; i < stateSelectors.length; i++) {
7220 s = stateSelectors[i];
7221 selToFn[s.selector] = s.matches;
7222 }
7223
7224 return selToFn;
7225}();
7226
7227var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7228 return lookup[sel](ele);
7229};
7230var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7231 return s.selector;
7232}).join('|') + ')';
7233
7234// so that values get compared properly in Selector.filter()
7235
7236var cleanMetaChars = function cleanMetaChars(str) {
7237 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7238 return $1;
7239 });
7240};
7241
7242var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7243 selector[selector.length - 1] = replacementQuery;
7244}; // NOTE: add new expression syntax here to have it recognised by the parser;
7245// - a query contains all adjacent (i.e. no separator in between) expressions;
7246// - the current query is stored in selector[i]
7247// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7248
7249
7250var exprs = [{
7251 name: 'group',
7252 // just used for identifying when debugging
7253 query: true,
7254 regex: '(' + tokens.group + ')',
7255 populate: function populate(selector, query, _ref) {
7256 var _ref2 = _slicedToArray(_ref, 1),
7257 group = _ref2[0];
7258
7259 query.checks.push({
7260 type: Type.GROUP,
7261 value: group === '*' ? group : group + 's'
7262 });
7263 }
7264}, {
7265 name: 'state',
7266 query: true,
7267 regex: stateSelectorRegex,
7268 populate: function populate(selector, query, _ref3) {
7269 var _ref4 = _slicedToArray(_ref3, 1),
7270 state = _ref4[0];
7271
7272 query.checks.push({
7273 type: Type.STATE,
7274 value: state
7275 });
7276 }
7277}, {
7278 name: 'id',
7279 query: true,
7280 regex: '\\#(' + tokens.id + ')',
7281 populate: function populate(selector, query, _ref5) {
7282 var _ref6 = _slicedToArray(_ref5, 1),
7283 id = _ref6[0];
7284
7285 query.checks.push({
7286 type: Type.ID,
7287 value: cleanMetaChars(id)
7288 });
7289 }
7290}, {
7291 name: 'className',
7292 query: true,
7293 regex: '\\.(' + tokens.className + ')',
7294 populate: function populate(selector, query, _ref7) {
7295 var _ref8 = _slicedToArray(_ref7, 1),
7296 className = _ref8[0];
7297
7298 query.checks.push({
7299 type: Type.CLASS,
7300 value: cleanMetaChars(className)
7301 });
7302 }
7303}, {
7304 name: 'dataExists',
7305 query: true,
7306 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7307 populate: function populate(selector, query, _ref9) {
7308 var _ref10 = _slicedToArray(_ref9, 1),
7309 variable = _ref10[0];
7310
7311 query.checks.push({
7312 type: Type.DATA_EXIST,
7313 field: cleanMetaChars(variable)
7314 });
7315 }
7316}, {
7317 name: 'dataCompare',
7318 query: true,
7319 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7320 populate: function populate(selector, query, _ref11) {
7321 var _ref12 = _slicedToArray(_ref11, 3),
7322 variable = _ref12[0],
7323 comparatorOp = _ref12[1],
7324 value = _ref12[2];
7325
7326 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7327
7328 if (valueIsString) {
7329 value = value.substring(1, value.length - 1);
7330 } else {
7331 value = parseFloat(value);
7332 }
7333
7334 query.checks.push({
7335 type: Type.DATA_COMPARE,
7336 field: cleanMetaChars(variable),
7337 operator: comparatorOp,
7338 value: value
7339 });
7340 }
7341}, {
7342 name: 'dataBool',
7343 query: true,
7344 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7345 populate: function populate(selector, query, _ref13) {
7346 var _ref14 = _slicedToArray(_ref13, 2),
7347 boolOp = _ref14[0],
7348 variable = _ref14[1];
7349
7350 query.checks.push({
7351 type: Type.DATA_BOOL,
7352 field: cleanMetaChars(variable),
7353 operator: boolOp
7354 });
7355 }
7356}, {
7357 name: 'metaCompare',
7358 query: true,
7359 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7360 populate: function populate(selector, query, _ref15) {
7361 var _ref16 = _slicedToArray(_ref15, 3),
7362 meta = _ref16[0],
7363 comparatorOp = _ref16[1],
7364 number = _ref16[2];
7365
7366 query.checks.push({
7367 type: Type.META_COMPARE,
7368 field: cleanMetaChars(meta),
7369 operator: comparatorOp,
7370 value: parseFloat(number)
7371 });
7372 }
7373}, {
7374 name: 'nextQuery',
7375 separator: true,
7376 regex: tokens.separator,
7377 populate: function populate(selector, query) {
7378 var currentSubject = selector.currentSubject;
7379 var edgeCount = selector.edgeCount;
7380 var compoundCount = selector.compoundCount;
7381 var lastQ = selector[selector.length - 1];
7382
7383 if (currentSubject != null) {
7384 lastQ.subject = currentSubject;
7385 selector.currentSubject = null;
7386 }
7387
7388 lastQ.edgeCount = edgeCount;
7389 lastQ.compoundCount = compoundCount;
7390 selector.edgeCount = 0;
7391 selector.compoundCount = 0; // go on to next query
7392
7393 var nextQuery = selector[selector.length++] = newQuery();
7394 return nextQuery; // this is the new query to be filled by the following exprs
7395 }
7396}, {
7397 name: 'directedEdge',
7398 separator: true,
7399 regex: tokens.directedEdge,
7400 populate: function populate(selector, query) {
7401 if (selector.currentSubject == null) {
7402 // undirected edge
7403 var edgeQuery = newQuery();
7404 var source = query;
7405 var target = newQuery();
7406 edgeQuery.checks.push({
7407 type: Type.DIRECTED_EDGE,
7408 source: source,
7409 target: target
7410 }); // the query in the selector should be the edge rather than the source
7411
7412 replaceLastQuery(selector, query, edgeQuery);
7413 selector.edgeCount++; // we're now populating the target query with expressions that follow
7414
7415 return target;
7416 } else {
7417 // source/target
7418 var srcTgtQ = newQuery();
7419 var _source = query;
7420
7421 var _target = newQuery();
7422
7423 srcTgtQ.checks.push({
7424 type: Type.NODE_SOURCE,
7425 source: _source,
7426 target: _target
7427 }); // the query in the selector should be the neighbourhood rather than the node
7428
7429 replaceLastQuery(selector, query, srcTgtQ);
7430 selector.edgeCount++;
7431 return _target; // now populating the target with the following expressions
7432 }
7433 }
7434}, {
7435 name: 'undirectedEdge',
7436 separator: true,
7437 regex: tokens.undirectedEdge,
7438 populate: function populate(selector, query) {
7439 if (selector.currentSubject == null) {
7440 // undirected edge
7441 var edgeQuery = newQuery();
7442 var source = query;
7443 var target = newQuery();
7444 edgeQuery.checks.push({
7445 type: Type.UNDIRECTED_EDGE,
7446 nodes: [source, target]
7447 }); // the query in the selector should be the edge rather than the source
7448
7449 replaceLastQuery(selector, query, edgeQuery);
7450 selector.edgeCount++; // we're now populating the target query with expressions that follow
7451
7452 return target;
7453 } else {
7454 // neighbourhood
7455 var nhoodQ = newQuery();
7456 var node = query;
7457 var neighbor = newQuery();
7458 nhoodQ.checks.push({
7459 type: Type.NODE_NEIGHBOR,
7460 node: node,
7461 neighbor: neighbor
7462 }); // the query in the selector should be the neighbourhood rather than the node
7463
7464 replaceLastQuery(selector, query, nhoodQ);
7465 return neighbor; // now populating the neighbor with following expressions
7466 }
7467 }
7468}, {
7469 name: 'child',
7470 separator: true,
7471 regex: tokens.child,
7472 populate: function populate(selector, query) {
7473 if (selector.currentSubject == null) {
7474 // default: child query
7475 var parentChildQuery = newQuery();
7476 var child = newQuery();
7477 var parent = selector[selector.length - 1];
7478 parentChildQuery.checks.push({
7479 type: Type.CHILD,
7480 parent: parent,
7481 child: child
7482 }); // the query in the selector should be the '>' itself
7483
7484 replaceLastQuery(selector, query, parentChildQuery);
7485 selector.compoundCount++; // we're now populating the child query with expressions that follow
7486
7487 return child;
7488 } else if (selector.currentSubject === query) {
7489 // compound split query
7490 var compound = newQuery();
7491 var left = selector[selector.length - 1];
7492 var right = newQuery();
7493 var subject = newQuery();
7494
7495 var _child = newQuery();
7496
7497 var _parent = newQuery(); // set up the root compound q
7498
7499
7500 compound.checks.push({
7501 type: Type.COMPOUND_SPLIT,
7502 left: left,
7503 right: right,
7504 subject: subject
7505 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7506
7507 subject.checks = query.checks; // take the checks from the left
7508
7509 query.checks = [{
7510 type: Type.TRUE
7511 }]; // checks under left refs the subject implicitly
7512 // set up the right q
7513
7514 _parent.checks.push({
7515 type: Type.TRUE
7516 }); // parent implicitly refs the subject
7517
7518
7519 right.checks.push({
7520 type: Type.PARENT,
7521 // type is swapped on right side queries
7522 parent: _parent,
7523 child: _child // empty for now
7524
7525 });
7526 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7527
7528 selector.currentSubject = subject;
7529 selector.compoundCount++;
7530 return _child; // now populating the right side's child
7531 } else {
7532 // parent query
7533 // info for parent query
7534 var _parent2 = newQuery();
7535
7536 var _child2 = newQuery();
7537
7538 var pcQChecks = [{
7539 type: Type.PARENT,
7540 parent: _parent2,
7541 child: _child2
7542 }]; // the parent-child query takes the place of the query previously being populated
7543
7544 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7545
7546 query.checks = pcQChecks; // pc query takes over
7547
7548 selector.compoundCount++;
7549 return _child2; // we're now populating the child
7550 }
7551 }
7552}, {
7553 name: 'descendant',
7554 separator: true,
7555 regex: tokens.descendant,
7556 populate: function populate(selector, query) {
7557 if (selector.currentSubject == null) {
7558 // default: descendant query
7559 var ancChQuery = newQuery();
7560 var descendant = newQuery();
7561 var ancestor = selector[selector.length - 1];
7562 ancChQuery.checks.push({
7563 type: Type.DESCENDANT,
7564 ancestor: ancestor,
7565 descendant: descendant
7566 }); // the query in the selector should be the '>' itself
7567
7568 replaceLastQuery(selector, query, ancChQuery);
7569 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7570
7571 return descendant;
7572 } else if (selector.currentSubject === query) {
7573 // compound split query
7574 var compound = newQuery();
7575 var left = selector[selector.length - 1];
7576 var right = newQuery();
7577 var subject = newQuery();
7578
7579 var _descendant = newQuery();
7580
7581 var _ancestor = newQuery(); // set up the root compound q
7582
7583
7584 compound.checks.push({
7585 type: Type.COMPOUND_SPLIT,
7586 left: left,
7587 right: right,
7588 subject: subject
7589 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7590
7591 subject.checks = query.checks; // take the checks from the left
7592
7593 query.checks = [{
7594 type: Type.TRUE
7595 }]; // checks under left refs the subject implicitly
7596 // set up the right q
7597
7598 _ancestor.checks.push({
7599 type: Type.TRUE
7600 }); // ancestor implicitly refs the subject
7601
7602
7603 right.checks.push({
7604 type: Type.ANCESTOR,
7605 // type is swapped on right side queries
7606 ancestor: _ancestor,
7607 descendant: _descendant // empty for now
7608
7609 });
7610 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7611
7612 selector.currentSubject = subject;
7613 selector.compoundCount++;
7614 return _descendant; // now populating the right side's descendant
7615 } else {
7616 // ancestor query
7617 // info for parent query
7618 var _ancestor2 = newQuery();
7619
7620 var _descendant2 = newQuery();
7621
7622 var adQChecks = [{
7623 type: Type.ANCESTOR,
7624 ancestor: _ancestor2,
7625 descendant: _descendant2
7626 }]; // the parent-child query takes the place of the query previously being populated
7627
7628 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7629
7630 query.checks = adQChecks; // pc query takes over
7631
7632 selector.compoundCount++;
7633 return _descendant2; // we're now populating the child
7634 }
7635 }
7636}, {
7637 name: 'subject',
7638 modifier: true,
7639 regex: tokens.subject,
7640 populate: function populate(selector, query) {
7641 if (selector.currentSubject != null && selector.currentSubject !== query) {
7642 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7643 return false;
7644 }
7645
7646 selector.currentSubject = query;
7647 var topQ = selector[selector.length - 1];
7648 var topChk = topQ.checks[0];
7649 var topType = topChk == null ? null : topChk.type;
7650
7651 if (topType === Type.DIRECTED_EDGE) {
7652 // directed edge with subject on the target
7653 // change to target node check
7654 topChk.type = Type.NODE_TARGET;
7655 } else if (topType === Type.UNDIRECTED_EDGE) {
7656 // undirected edge with subject on the second node
7657 // change to neighbor check
7658 topChk.type = Type.NODE_NEIGHBOR;
7659 topChk.node = topChk.nodes[1]; // second node is subject
7660
7661 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7662
7663 topChk.nodes = null;
7664 }
7665 }
7666}];
7667exprs.forEach(function (e) {
7668 return e.regexObj = new RegExp('^' + e.regex);
7669});
7670
7671/**
7672 * Of all the expressions, find the first match in the remaining text.
7673 * @param {string} remaining The remaining text to parse
7674 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7675 */
7676
7677var consumeExpr = function consumeExpr(remaining) {
7678 var expr;
7679 var match;
7680 var name;
7681
7682 for (var j = 0; j < exprs.length; j++) {
7683 var e = exprs[j];
7684 var n = e.name;
7685 var m = remaining.match(e.regexObj);
7686
7687 if (m != null) {
7688 match = m;
7689 expr = e;
7690 name = n;
7691 var consumed = m[0];
7692 remaining = remaining.substring(consumed.length);
7693 break; // we've consumed one expr, so we can return now
7694 }
7695 }
7696
7697 return {
7698 expr: expr,
7699 match: match,
7700 name: name,
7701 remaining: remaining
7702 };
7703};
7704/**
7705 * Consume all the leading whitespace
7706 * @param {string} remaining The text to consume
7707 * @returns The text with the leading whitespace removed
7708 */
7709
7710
7711var consumeWhitespace = function consumeWhitespace(remaining) {
7712 var match = remaining.match(/^\s+/);
7713
7714 if (match) {
7715 var consumed = match[0];
7716 remaining = remaining.substring(consumed.length);
7717 }
7718
7719 return remaining;
7720};
7721/**
7722 * Parse the string and store the parsed representation in the Selector.
7723 * @param {string} selector The selector string
7724 * @returns `true` if the selector was successfully parsed, `false` otherwise
7725 */
7726
7727
7728var parse = function parse(selector) {
7729 var self = this;
7730 var remaining = self.inputText = selector;
7731 var currentQuery = self[0] = newQuery();
7732 self.length = 1;
7733 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7734
7735 for (;;) {
7736 var exprInfo = consumeExpr(remaining);
7737
7738 if (exprInfo.expr == null) {
7739 warn('The selector `' + selector + '`is invalid');
7740 return false;
7741 } else {
7742 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7743
7744 var ret = exprInfo.expr.populate(self, currentQuery, args);
7745
7746 if (ret === false) {
7747 return false; // exit if population failed
7748 } else if (ret != null) {
7749 currentQuery = ret; // change the current query to be filled if the expr specifies
7750 }
7751 }
7752
7753 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7754
7755 if (remaining.match(/^\s*$/)) {
7756 break;
7757 }
7758 }
7759
7760 var lastQ = self[self.length - 1];
7761
7762 if (self.currentSubject != null) {
7763 lastQ.subject = self.currentSubject;
7764 }
7765
7766 lastQ.edgeCount = self.edgeCount;
7767 lastQ.compoundCount = self.compoundCount;
7768
7769 for (var i = 0; i < self.length; i++) {
7770 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7771
7772 if (q.compoundCount > 0 && q.edgeCount > 0) {
7773 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7774 return false;
7775 }
7776
7777 if (q.edgeCount > 1) {
7778 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7779 return false;
7780 } else if (q.edgeCount === 1) {
7781 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.');
7782 }
7783 }
7784
7785 return true; // success
7786};
7787/**
7788 * Get the selector represented as a string. This value uses default formatting,
7789 * so things like spacing may differ from the input text passed to the constructor.
7790 * @returns {string} The selector string
7791 */
7792
7793
7794var toString = function toString() {
7795 if (this.toStringCache != null) {
7796 return this.toStringCache;
7797 }
7798
7799 var clean = function clean(obj) {
7800 if (obj == null) {
7801 return '';
7802 } else {
7803 return obj;
7804 }
7805 };
7806
7807 var cleanVal = function cleanVal(val) {
7808 if (string(val)) {
7809 return '"' + val + '"';
7810 } else {
7811 return clean(val);
7812 }
7813 };
7814
7815 var space = function space(val) {
7816 return ' ' + val + ' ';
7817 };
7818
7819 var checkToString = function checkToString(check, subject) {
7820 var type = check.type,
7821 value = check.value;
7822
7823 switch (type) {
7824 case Type.GROUP:
7825 {
7826 var group = clean(value);
7827 return group.substring(0, group.length - 1);
7828 }
7829
7830 case Type.DATA_COMPARE:
7831 {
7832 var field = check.field,
7833 operator = check.operator;
7834 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7835 }
7836
7837 case Type.DATA_BOOL:
7838 {
7839 var _operator = check.operator,
7840 _field = check.field;
7841 return '[' + clean(_operator) + _field + ']';
7842 }
7843
7844 case Type.DATA_EXIST:
7845 {
7846 var _field2 = check.field;
7847 return '[' + _field2 + ']';
7848 }
7849
7850 case Type.META_COMPARE:
7851 {
7852 var _operator2 = check.operator,
7853 _field3 = check.field;
7854 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7855 }
7856
7857 case Type.STATE:
7858 {
7859 return value;
7860 }
7861
7862 case Type.ID:
7863 {
7864 return '#' + value;
7865 }
7866
7867 case Type.CLASS:
7868 {
7869 return '.' + value;
7870 }
7871
7872 case Type.PARENT:
7873 case Type.CHILD:
7874 {
7875 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7876 }
7877
7878 case Type.ANCESTOR:
7879 case Type.DESCENDANT:
7880 {
7881 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7882 }
7883
7884 case Type.COMPOUND_SPLIT:
7885 {
7886 var lhs = queryToString(check.left, subject);
7887 var sub = queryToString(check.subject, subject);
7888 var rhs = queryToString(check.right, subject);
7889 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7890 }
7891
7892 case Type.TRUE:
7893 {
7894 return '';
7895 }
7896 }
7897 };
7898
7899 var queryToString = function queryToString(query, subject) {
7900 return query.checks.reduce(function (str, chk, i) {
7901 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7902 }, '');
7903 };
7904
7905 var str = '';
7906
7907 for (var i = 0; i < this.length; i++) {
7908 var query = this[i];
7909 str += queryToString(query, query.subject);
7910
7911 if (this.length > 1 && i < this.length - 1) {
7912 str += ', ';
7913 }
7914 }
7915
7916 this.toStringCache = str;
7917 return str;
7918};
7919var parse$1 = {
7920 parse: parse,
7921 toString: toString
7922};
7923
7924var valCmp = function valCmp(fieldVal, operator, value) {
7925 var matches;
7926 var isFieldStr = string(fieldVal);
7927 var isFieldNum = number(fieldVal);
7928 var isValStr = string(value);
7929 var fieldStr, valStr;
7930 var caseInsensitive = false;
7931 var notExpr = false;
7932 var isIneqCmp = false;
7933
7934 if (operator.indexOf('!') >= 0) {
7935 operator = operator.replace('!', '');
7936 notExpr = true;
7937 }
7938
7939 if (operator.indexOf('@') >= 0) {
7940 operator = operator.replace('@', '');
7941 caseInsensitive = true;
7942 }
7943
7944 if (isFieldStr || isValStr || caseInsensitive) {
7945 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
7946 valStr = '' + value;
7947 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
7948 // even if we're comparing numbers
7949
7950
7951 if (caseInsensitive) {
7952 fieldVal = fieldStr = fieldStr.toLowerCase();
7953 value = valStr = valStr.toLowerCase();
7954 }
7955
7956 switch (operator) {
7957 case '*=':
7958 matches = fieldStr.indexOf(valStr) >= 0;
7959 break;
7960
7961 case '$=':
7962 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
7963 break;
7964
7965 case '^=':
7966 matches = fieldStr.indexOf(valStr) === 0;
7967 break;
7968
7969 case '=':
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 case '<=':
7989 isIneqCmp = true;
7990 matches = fieldVal <= value;
7991 break;
7992
7993 default:
7994 matches = false;
7995 break;
7996 } // apply the not op, but null vals for inequalities should always stay non-matching
7997
7998
7999 if (notExpr && (fieldVal != null || !isIneqCmp)) {
8000 matches = !matches;
8001 }
8002
8003 return matches;
8004};
8005var boolCmp = function boolCmp(fieldVal, operator) {
8006 switch (operator) {
8007 case '?':
8008 return fieldVal ? true : false;
8009
8010 case '!':
8011 return fieldVal ? false : true;
8012
8013 case '^':
8014 return fieldVal === undefined;
8015 }
8016};
8017var existCmp = function existCmp(fieldVal) {
8018 return fieldVal !== undefined;
8019};
8020var data = function data(ele, field) {
8021 return ele.data(field);
8022};
8023var meta = function meta(ele, field) {
8024 return ele[field]();
8025};
8026
8027/** A lookup of `match(check, ele)` functions by `Type` int */
8028
8029var match = [];
8030/**
8031 * Returns whether the query matches for the element
8032 * @param query The `{ type, value, ... }` query object
8033 * @param ele The element to compare against
8034*/
8035
8036var matches = function matches(query, ele) {
8037 return query.checks.every(function (chk) {
8038 return match[chk.type](chk, ele);
8039 });
8040};
8041
8042match[Type.GROUP] = function (check, ele) {
8043 var group = check.value;
8044 return group === '*' || group === ele.group();
8045};
8046
8047match[Type.STATE] = function (check, ele) {
8048 var stateSelector = check.value;
8049 return stateSelectorMatches(stateSelector, ele);
8050};
8051
8052match[Type.ID] = function (check, ele) {
8053 var id = check.value;
8054 return ele.id() === id;
8055};
8056
8057match[Type.CLASS] = function (check, ele) {
8058 var cls = check.value;
8059 return ele.hasClass(cls);
8060};
8061
8062match[Type.META_COMPARE] = function (check, ele) {
8063 var field = check.field,
8064 operator = check.operator,
8065 value = check.value;
8066 return valCmp(meta(ele, field), operator, value);
8067};
8068
8069match[Type.DATA_COMPARE] = function (check, ele) {
8070 var field = check.field,
8071 operator = check.operator,
8072 value = check.value;
8073 return valCmp(data(ele, field), operator, value);
8074};
8075
8076match[Type.DATA_BOOL] = function (check, ele) {
8077 var field = check.field,
8078 operator = check.operator;
8079 return boolCmp(data(ele, field), operator);
8080};
8081
8082match[Type.DATA_EXIST] = function (check, ele) {
8083 var field = check.field,
8084 operator = check.operator;
8085 return existCmp(data(ele, field));
8086};
8087
8088match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8089 var qA = check.nodes[0];
8090 var qB = check.nodes[1];
8091 var src = ele.source();
8092 var tgt = ele.target();
8093 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8094};
8095
8096match[Type.NODE_NEIGHBOR] = function (check, ele) {
8097 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8098 return n.isNode() && matches(check.neighbor, n);
8099 });
8100};
8101
8102match[Type.DIRECTED_EDGE] = function (check, ele) {
8103 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8104};
8105
8106match[Type.NODE_SOURCE] = function (check, ele) {
8107 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8108 return n.isNode() && matches(check.target, n);
8109 });
8110};
8111
8112match[Type.NODE_TARGET] = function (check, ele) {
8113 return matches(check.target, ele) && ele.incomers().some(function (n) {
8114 return n.isNode() && matches(check.source, n);
8115 });
8116};
8117
8118match[Type.CHILD] = function (check, ele) {
8119 return matches(check.child, ele) && matches(check.parent, ele.parent());
8120};
8121
8122match[Type.PARENT] = function (check, ele) {
8123 return matches(check.parent, ele) && ele.children().some(function (c) {
8124 return matches(check.child, c);
8125 });
8126};
8127
8128match[Type.DESCENDANT] = function (check, ele) {
8129 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8130 return matches(check.ancestor, a);
8131 });
8132};
8133
8134match[Type.ANCESTOR] = function (check, ele) {
8135 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8136 return matches(check.descendant, d);
8137 });
8138};
8139
8140match[Type.COMPOUND_SPLIT] = function (check, ele) {
8141 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8142};
8143
8144match[Type.TRUE] = function () {
8145 return true;
8146};
8147
8148match[Type.COLLECTION] = function (check, ele) {
8149 var collection = check.value;
8150 return collection.has(ele);
8151};
8152
8153match[Type.FILTER] = function (check, ele) {
8154 var filter = check.value;
8155 return filter(ele);
8156};
8157
8158var filter = function filter(collection) {
8159 var self = this; // for 1 id #foo queries, just get the element
8160
8161 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8162 return collection.getElementById(self[0].checks[0].value).collection();
8163 }
8164
8165 var selectorFunction = function selectorFunction(element) {
8166 for (var j = 0; j < self.length; j++) {
8167 var query = self[j];
8168
8169 if (matches(query, element)) {
8170 return true;
8171 }
8172 }
8173
8174 return false;
8175 };
8176
8177 if (self.text() == null) {
8178 selectorFunction = function selectorFunction() {
8179 return true;
8180 };
8181 }
8182
8183 return collection.filter(selectorFunction);
8184}; // filter
8185// does selector match a single element?
8186
8187
8188var matches$1 = function matches$1(ele) {
8189 var self = this;
8190
8191 for (var j = 0; j < self.length; j++) {
8192 var query = self[j];
8193
8194 if (matches(query, ele)) {
8195 return true;
8196 }
8197 }
8198
8199 return false;
8200}; // matches
8201
8202
8203var matching = {
8204 matches: matches$1,
8205 filter: filter
8206};
8207
8208var Selector = function Selector(selector) {
8209 this.inputText = selector;
8210 this.currentSubject = null;
8211 this.compoundCount = 0;
8212 this.edgeCount = 0;
8213 this.length = 0;
8214
8215 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8216 this.addQuery({
8217 checks: [{
8218 type: Type.COLLECTION,
8219 value: selector.collection()
8220 }]
8221 });
8222 } else if (fn(selector)) {
8223 this.addQuery({
8224 checks: [{
8225 type: Type.FILTER,
8226 value: selector
8227 }]
8228 });
8229 } else if (string(selector)) {
8230 if (!this.parse(selector)) {
8231 this.invalid = true;
8232 }
8233 } else {
8234 error('A selector must be created from a string; found ');
8235 }
8236};
8237
8238var selfn = Selector.prototype;
8239[parse$1, matching].forEach(function (p) {
8240 return extend(selfn, p);
8241});
8242
8243selfn.text = function () {
8244 return this.inputText;
8245};
8246
8247selfn.size = function () {
8248 return this.length;
8249};
8250
8251selfn.eq = function (i) {
8252 return this[i];
8253};
8254
8255selfn.sameText = function (otherSel) {
8256 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8257};
8258
8259selfn.addQuery = function (q) {
8260 this[this.length++] = q;
8261};
8262
8263selfn.selector = selfn.toString;
8264
8265var elesfn$f = {
8266 allAre: function allAre(selector) {
8267 var selObj = new Selector(selector);
8268 return this.every(function (ele) {
8269 return selObj.matches(ele);
8270 });
8271 },
8272 is: function is(selector) {
8273 var selObj = new Selector(selector);
8274 return this.some(function (ele) {
8275 return selObj.matches(ele);
8276 });
8277 },
8278 some: function some(fn, thisArg) {
8279 for (var i = 0; i < this.length; i++) {
8280 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8281
8282 if (ret) {
8283 return true;
8284 }
8285 }
8286
8287 return false;
8288 },
8289 every: function every(fn, thisArg) {
8290 for (var i = 0; i < this.length; i++) {
8291 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8292
8293 if (!ret) {
8294 return false;
8295 }
8296 }
8297
8298 return true;
8299 },
8300 same: function same(collection) {
8301 // cheap collection ref check
8302 if (this === collection) {
8303 return true;
8304 }
8305
8306 collection = this.cy().collection(collection);
8307 var thisLength = this.length;
8308 var collectionLength = collection.length; // cheap length check
8309
8310 if (thisLength !== collectionLength) {
8311 return false;
8312 } // cheap element ref check
8313
8314
8315 if (thisLength === 1) {
8316 return this[0] === collection[0];
8317 }
8318
8319 return this.every(function (ele) {
8320 return collection.hasElementWithId(ele.id());
8321 });
8322 },
8323 anySame: function anySame(collection) {
8324 collection = this.cy().collection(collection);
8325 return this.some(function (ele) {
8326 return collection.hasElementWithId(ele.id());
8327 });
8328 },
8329 allAreNeighbors: function allAreNeighbors(collection) {
8330 collection = this.cy().collection(collection);
8331 var nhood = this.neighborhood();
8332 return collection.every(function (ele) {
8333 return nhood.hasElementWithId(ele.id());
8334 });
8335 },
8336 contains: function contains(collection) {
8337 collection = this.cy().collection(collection);
8338 var self = this;
8339 return collection.every(function (ele) {
8340 return self.hasElementWithId(ele.id());
8341 });
8342 }
8343};
8344elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8345elesfn$f.has = elesfn$f.contains;
8346elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8347
8348var cache = function cache(fn, name) {
8349 return function traversalCache(arg1, arg2, arg3, arg4) {
8350 var selectorOrEles = arg1;
8351 var eles = this;
8352 var key;
8353
8354 if (selectorOrEles == null) {
8355 key = '';
8356 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8357 key = selectorOrEles.id();
8358 }
8359
8360 if (eles.length === 1 && key) {
8361 var _p = eles[0]._private;
8362 var tch = _p.traversalCache = _p.traversalCache || {};
8363 var ch = tch[name] = tch[name] || [];
8364 var hash = hashString(key);
8365 var cacheHit = ch[hash];
8366
8367 if (cacheHit) {
8368 return cacheHit;
8369 } else {
8370 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8371 }
8372 } else {
8373 return fn.call(eles, arg1, arg2, arg3, arg4);
8374 }
8375 };
8376};
8377
8378var elesfn$g = {
8379 parent: function parent(selector) {
8380 var parents = []; // optimisation for single ele call
8381
8382 if (this.length === 1) {
8383 var parent = this[0]._private.parent;
8384
8385 if (parent) {
8386 return parent;
8387 }
8388 }
8389
8390 for (var i = 0; i < this.length; i++) {
8391 var ele = this[i];
8392 var _parent = ele._private.parent;
8393
8394 if (_parent) {
8395 parents.push(_parent);
8396 }
8397 }
8398
8399 return this.spawn(parents, true).filter(selector);
8400 },
8401 parents: function parents(selector) {
8402 var parents = [];
8403 var eles = this.parent();
8404
8405 while (eles.nonempty()) {
8406 for (var i = 0; i < eles.length; i++) {
8407 var ele = eles[i];
8408 parents.push(ele);
8409 }
8410
8411 eles = eles.parent();
8412 }
8413
8414 return this.spawn(parents, true).filter(selector);
8415 },
8416 commonAncestors: function commonAncestors(selector) {
8417 var ancestors;
8418
8419 for (var i = 0; i < this.length; i++) {
8420 var ele = this[i];
8421 var parents = ele.parents();
8422 ancestors = ancestors || parents;
8423 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8424 }
8425
8426 return ancestors.filter(selector);
8427 },
8428 orphans: function orphans(selector) {
8429 return this.stdFilter(function (ele) {
8430 return ele.isOrphan();
8431 }).filter(selector);
8432 },
8433 nonorphans: function nonorphans(selector) {
8434 return this.stdFilter(function (ele) {
8435 return ele.isChild();
8436 }).filter(selector);
8437 },
8438 children: cache(function (selector) {
8439 var children = [];
8440
8441 for (var i = 0; i < this.length; i++) {
8442 var ele = this[i];
8443 var eleChildren = ele._private.children;
8444
8445 for (var j = 0; j < eleChildren.length; j++) {
8446 children.push(eleChildren[j]);
8447 }
8448 }
8449
8450 return this.spawn(children, true).filter(selector);
8451 }, 'children'),
8452 siblings: function siblings(selector) {
8453 return this.parent().children().not(this).filter(selector);
8454 },
8455 isParent: function isParent() {
8456 var ele = this[0];
8457
8458 if (ele) {
8459 return ele.isNode() && ele._private.children.length !== 0;
8460 }
8461 },
8462 isChildless: function isChildless() {
8463 var ele = this[0];
8464
8465 if (ele) {
8466 return ele.isNode() && ele._private.children.length === 0;
8467 }
8468 },
8469 isChild: function isChild() {
8470 var ele = this[0];
8471
8472 if (ele) {
8473 return ele.isNode() && ele._private.parent != null;
8474 }
8475 },
8476 isOrphan: function isOrphan() {
8477 var ele = this[0];
8478
8479 if (ele) {
8480 return ele.isNode() && ele._private.parent == null;
8481 }
8482 },
8483 descendants: function descendants(selector) {
8484 var elements = [];
8485
8486 function add(eles) {
8487 for (var i = 0; i < eles.length; i++) {
8488 var ele = eles[i];
8489 elements.push(ele);
8490
8491 if (ele.children().nonempty()) {
8492 add(ele.children());
8493 }
8494 }
8495 }
8496
8497 add(this.children());
8498 return this.spawn(elements, true).filter(selector);
8499 }
8500};
8501
8502function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8503 var q = [];
8504 var did = new Set$1();
8505 var cy = eles.cy();
8506 var hasCompounds = cy.hasCompoundNodes();
8507
8508 for (var i = 0; i < eles.length; i++) {
8509 var ele = eles[i];
8510
8511 if (includeSelf) {
8512 q.push(ele);
8513 } else if (hasCompounds) {
8514 recursiveStep(q, did, ele);
8515 }
8516 }
8517
8518 while (q.length > 0) {
8519 var _ele = q.shift();
8520
8521 fn(_ele);
8522 did.add(_ele.id());
8523
8524 if (hasCompounds) {
8525 recursiveStep(q, did, _ele);
8526 }
8527 }
8528
8529 return eles;
8530}
8531
8532function addChildren(q, did, ele) {
8533 if (ele.isParent()) {
8534 var children = ele._private.children;
8535
8536 for (var i = 0; i < children.length; i++) {
8537 var child = children[i];
8538
8539 if (!did.has(child.id())) {
8540 q.push(child);
8541 }
8542 }
8543 }
8544} // very efficient version of eles.add( eles.descendants() ).forEach()
8545// for internal use
8546
8547
8548elesfn$g.forEachDown = function (fn) {
8549 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8550 return forEachCompound(this, fn, includeSelf, addChildren);
8551};
8552
8553function addParent(q, did, ele) {
8554 if (ele.isChild()) {
8555 var parent = ele._private.parent;
8556
8557 if (!did.has(parent.id())) {
8558 q.push(parent);
8559 }
8560 }
8561}
8562
8563elesfn$g.forEachUp = function (fn) {
8564 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8565 return forEachCompound(this, fn, includeSelf, addParent);
8566};
8567
8568function addParentAndChildren(q, did, ele) {
8569 addParent(q, did, ele);
8570 addChildren(q, did, ele);
8571}
8572
8573elesfn$g.forEachUpAndDown = function (fn) {
8574 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8575 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8576}; // aliases
8577
8578
8579elesfn$g.ancestors = elesfn$g.parents;
8580
8581var fn$1, elesfn$h;
8582fn$1 = elesfn$h = {
8583 data: define$3.data({
8584 field: 'data',
8585 bindingEvent: 'data',
8586 allowBinding: true,
8587 allowSetting: true,
8588 settingEvent: 'data',
8589 settingTriggersEvent: true,
8590 triggerFnName: 'trigger',
8591 allowGetting: true,
8592 immutableKeys: {
8593 'id': true,
8594 'source': true,
8595 'target': true,
8596 'parent': true
8597 },
8598 updateStyle: true
8599 }),
8600 removeData: define$3.removeData({
8601 field: 'data',
8602 event: 'data',
8603 triggerFnName: 'trigger',
8604 triggerEvent: true,
8605 immutableKeys: {
8606 'id': true,
8607 'source': true,
8608 'target': true,
8609 'parent': true
8610 },
8611 updateStyle: true
8612 }),
8613 scratch: define$3.data({
8614 field: 'scratch',
8615 bindingEvent: 'scratch',
8616 allowBinding: true,
8617 allowSetting: true,
8618 settingEvent: 'scratch',
8619 settingTriggersEvent: true,
8620 triggerFnName: 'trigger',
8621 allowGetting: true,
8622 updateStyle: true
8623 }),
8624 removeScratch: define$3.removeData({
8625 field: 'scratch',
8626 event: 'scratch',
8627 triggerFnName: 'trigger',
8628 triggerEvent: true,
8629 updateStyle: true
8630 }),
8631 rscratch: define$3.data({
8632 field: 'rscratch',
8633 allowBinding: false,
8634 allowSetting: true,
8635 settingTriggersEvent: false,
8636 allowGetting: true
8637 }),
8638 removeRscratch: define$3.removeData({
8639 field: 'rscratch',
8640 triggerEvent: false
8641 }),
8642 id: function id() {
8643 var ele = this[0];
8644
8645 if (ele) {
8646 return ele._private.data.id;
8647 }
8648 }
8649}; // aliases
8650
8651fn$1.attr = fn$1.data;
8652fn$1.removeAttr = fn$1.removeData;
8653var data$1 = elesfn$h;
8654
8655var elesfn$i = {};
8656
8657function defineDegreeFunction(callback) {
8658 return function (includeLoops) {
8659 var self = this;
8660
8661 if (includeLoops === undefined) {
8662 includeLoops = true;
8663 }
8664
8665 if (self.length === 0) {
8666 return;
8667 }
8668
8669 if (self.isNode() && !self.removed()) {
8670 var degree = 0;
8671 var node = self[0];
8672 var connectedEdges = node._private.edges;
8673
8674 for (var i = 0; i < connectedEdges.length; i++) {
8675 var edge = connectedEdges[i];
8676
8677 if (!includeLoops && edge.isLoop()) {
8678 continue;
8679 }
8680
8681 degree += callback(node, edge);
8682 }
8683
8684 return degree;
8685 } else {
8686 return;
8687 }
8688 };
8689}
8690
8691extend(elesfn$i, {
8692 degree: defineDegreeFunction(function (node, edge) {
8693 if (edge.source().same(edge.target())) {
8694 return 2;
8695 } else {
8696 return 1;
8697 }
8698 }),
8699 indegree: defineDegreeFunction(function (node, edge) {
8700 if (edge.target().same(node)) {
8701 return 1;
8702 } else {
8703 return 0;
8704 }
8705 }),
8706 outdegree: defineDegreeFunction(function (node, edge) {
8707 if (edge.source().same(node)) {
8708 return 1;
8709 } else {
8710 return 0;
8711 }
8712 })
8713});
8714
8715function defineDegreeBoundsFunction(degreeFn, callback) {
8716 return function (includeLoops) {
8717 var ret;
8718 var nodes = this.nodes();
8719
8720 for (var i = 0; i < nodes.length; i++) {
8721 var ele = nodes[i];
8722 var degree = ele[degreeFn](includeLoops);
8723
8724 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8725 ret = degree;
8726 }
8727 }
8728
8729 return ret;
8730 };
8731}
8732
8733extend(elesfn$i, {
8734 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8735 return degree < min;
8736 }),
8737 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8738 return degree > max;
8739 }),
8740 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8741 return degree < min;
8742 }),
8743 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8744 return degree > max;
8745 }),
8746 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8747 return degree < min;
8748 }),
8749 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8750 return degree > max;
8751 })
8752});
8753extend(elesfn$i, {
8754 totalDegree: function totalDegree(includeLoops) {
8755 var total = 0;
8756 var nodes = this.nodes();
8757
8758 for (var i = 0; i < nodes.length; i++) {
8759 total += nodes[i].degree(includeLoops);
8760 }
8761
8762 return total;
8763 }
8764});
8765
8766var fn$2, elesfn$j;
8767
8768var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8769 for (var i = 0; i < eles.length; i++) {
8770 var ele = eles[i];
8771
8772 if (!ele.locked()) {
8773 var oldPos = ele._private.position;
8774 var delta = {
8775 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8776 y: newPos.y != null ? newPos.y - oldPos.y : 0
8777 };
8778
8779 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8780 ele.children().shift(delta, silent);
8781 }
8782
8783 ele.shiftCachedBoundingBox(delta);
8784 }
8785 }
8786};
8787
8788var positionDef = {
8789 field: 'position',
8790 bindingEvent: 'position',
8791 allowBinding: true,
8792 allowSetting: true,
8793 settingEvent: 'position',
8794 settingTriggersEvent: true,
8795 triggerFnName: 'emitAndNotify',
8796 allowGetting: true,
8797 validKeys: ['x', 'y'],
8798 beforeGet: function beforeGet(ele) {
8799 ele.updateCompoundBounds();
8800 },
8801 beforeSet: function beforeSet(eles, newPos) {
8802 beforePositionSet(eles, newPos, false);
8803 },
8804 onSet: function onSet(eles) {
8805 eles.dirtyCompoundBoundsCache();
8806 },
8807 canSet: function canSet(ele) {
8808 return !ele.locked();
8809 }
8810};
8811fn$2 = elesfn$j = {
8812 position: define$3.data(positionDef),
8813 // position but no notification to renderer
8814 silentPosition: define$3.data(extend({}, positionDef, {
8815 allowBinding: false,
8816 allowSetting: true,
8817 settingTriggersEvent: false,
8818 allowGetting: false,
8819 beforeSet: function beforeSet(eles, newPos) {
8820 beforePositionSet(eles, newPos, true);
8821 }
8822 })),
8823 positions: function positions(pos, silent) {
8824 if (plainObject(pos)) {
8825 if (silent) {
8826 this.silentPosition(pos);
8827 } else {
8828 this.position(pos);
8829 }
8830 } else if (fn(pos)) {
8831 var _fn = pos;
8832 var cy = this.cy();
8833 cy.startBatch();
8834
8835 for (var i = 0; i < this.length; i++) {
8836 var ele = this[i];
8837
8838 var _pos = void 0;
8839
8840 if (_pos = _fn(ele, i)) {
8841 if (silent) {
8842 ele.silentPosition(_pos);
8843 } else {
8844 ele.position(_pos);
8845 }
8846 }
8847 }
8848
8849 cy.endBatch();
8850 }
8851
8852 return this; // chaining
8853 },
8854 silentPositions: function silentPositions(pos) {
8855 return this.positions(pos, true);
8856 },
8857 shift: function shift(dim, val, silent) {
8858 var delta;
8859
8860 if (plainObject(dim)) {
8861 delta = {
8862 x: number(dim.x) ? dim.x : 0,
8863 y: number(dim.y) ? dim.y : 0
8864 };
8865 silent = val;
8866 } else if (string(dim) && number(val)) {
8867 delta = {
8868 x: 0,
8869 y: 0
8870 };
8871 delta[dim] = val;
8872 }
8873
8874 if (delta != null) {
8875 var cy = this.cy();
8876 cy.startBatch();
8877
8878 for (var i = 0; i < this.length; i++) {
8879 var ele = this[i];
8880 var pos = ele.position();
8881 var newPos = {
8882 x: pos.x + delta.x,
8883 y: pos.y + delta.y
8884 };
8885
8886 if (silent) {
8887 ele.silentPosition(newPos);
8888 } else {
8889 ele.position(newPos);
8890 }
8891 }
8892
8893 cy.endBatch();
8894 }
8895
8896 return this;
8897 },
8898 silentShift: function silentShift(dim, val) {
8899 if (plainObject(dim)) {
8900 this.shift(dim, true);
8901 } else if (string(dim) && number(val)) {
8902 this.shift(dim, val, true);
8903 }
8904
8905 return this;
8906 },
8907 // get/set the rendered (i.e. on screen) positon of the element
8908 renderedPosition: function renderedPosition(dim, val) {
8909 var ele = this[0];
8910 var cy = this.cy();
8911 var zoom = cy.zoom();
8912 var pan = cy.pan();
8913 var rpos = plainObject(dim) ? dim : undefined;
8914 var setting = rpos !== undefined || val !== undefined && string(dim);
8915
8916 if (ele && ele.isNode()) {
8917 // must have an element and must be a node to return position
8918 if (setting) {
8919 for (var i = 0; i < this.length; i++) {
8920 var _ele = this[i];
8921
8922 if (val !== undefined) {
8923 // set one dimension
8924 _ele.position(dim, (val - pan[dim]) / zoom);
8925 } else if (rpos !== undefined) {
8926 // set whole position
8927 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8928 }
8929 }
8930 } else {
8931 // getting
8932 var pos = ele.position();
8933 rpos = modelToRenderedPosition(pos, zoom, pan);
8934
8935 if (dim === undefined) {
8936 // then return the whole rendered position
8937 return rpos;
8938 } else {
8939 // then return the specified dimension
8940 return rpos[dim];
8941 }
8942 }
8943 } else if (!setting) {
8944 return undefined; // for empty collection case
8945 }
8946
8947 return this; // chaining
8948 },
8949 // get/set the position relative to the parent
8950 relativePosition: function relativePosition(dim, val) {
8951 var ele = this[0];
8952 var cy = this.cy();
8953 var ppos = plainObject(dim) ? dim : undefined;
8954 var setting = ppos !== undefined || val !== undefined && string(dim);
8955 var hasCompoundNodes = cy.hasCompoundNodes();
8956
8957 if (ele && ele.isNode()) {
8958 // must have an element and must be a node to return position
8959 if (setting) {
8960 for (var i = 0; i < this.length; i++) {
8961 var _ele2 = this[i];
8962 var parent = hasCompoundNodes ? _ele2.parent() : null;
8963 var hasParent = parent && parent.length > 0;
8964 var relativeToParent = hasParent;
8965
8966 if (hasParent) {
8967 parent = parent[0];
8968 }
8969
8970 var origin = relativeToParent ? parent.position() : {
8971 x: 0,
8972 y: 0
8973 };
8974
8975 if (val !== undefined) {
8976 // set one dimension
8977 _ele2.position(dim, val + origin[dim]);
8978 } else if (ppos !== undefined) {
8979 // set whole position
8980 _ele2.position({
8981 x: ppos.x + origin.x,
8982 y: ppos.y + origin.y
8983 });
8984 }
8985 }
8986 } else {
8987 // getting
8988 var pos = ele.position();
8989
8990 var _parent = hasCompoundNodes ? ele.parent() : null;
8991
8992 var _hasParent = _parent && _parent.length > 0;
8993
8994 var _relativeToParent = _hasParent;
8995
8996 if (_hasParent) {
8997 _parent = _parent[0];
8998 }
8999
9000 var _origin = _relativeToParent ? _parent.position() : {
9001 x: 0,
9002 y: 0
9003 };
9004
9005 ppos = {
9006 x: pos.x - _origin.x,
9007 y: pos.y - _origin.y
9008 };
9009
9010 if (dim === undefined) {
9011 // then return the whole rendered position
9012 return ppos;
9013 } else {
9014 // then return the specified dimension
9015 return ppos[dim];
9016 }
9017 }
9018 } else if (!setting) {
9019 return undefined; // for empty collection case
9020 }
9021
9022 return this; // chaining
9023 }
9024}; // aliases
9025
9026fn$2.modelPosition = fn$2.point = fn$2.position;
9027fn$2.modelPositions = fn$2.points = fn$2.positions;
9028fn$2.renderedPoint = fn$2.renderedPosition;
9029fn$2.relativePoint = fn$2.relativePosition;
9030var position = elesfn$j;
9031
9032var fn$3, elesfn$k;
9033fn$3 = elesfn$k = {};
9034
9035elesfn$k.renderedBoundingBox = function (options) {
9036 var bb = this.boundingBox(options);
9037 var cy = this.cy();
9038 var zoom = cy.zoom();
9039 var pan = cy.pan();
9040 var x1 = bb.x1 * zoom + pan.x;
9041 var x2 = bb.x2 * zoom + pan.x;
9042 var y1 = bb.y1 * zoom + pan.y;
9043 var y2 = bb.y2 * zoom + pan.y;
9044 return {
9045 x1: x1,
9046 x2: x2,
9047 y1: y1,
9048 y2: y2,
9049 w: x2 - x1,
9050 h: y2 - y1
9051 };
9052};
9053
9054elesfn$k.dirtyCompoundBoundsCache = function () {
9055 var cy = this.cy();
9056
9057 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9058 return this;
9059 }
9060
9061 this.forEachUp(function (ele) {
9062 if (ele.isParent()) {
9063 var _p = ele._private;
9064 _p.compoundBoundsClean = false;
9065 _p.bbCache = null;
9066 ele.emitAndNotify('bounds');
9067 }
9068 });
9069 return this;
9070};
9071
9072elesfn$k.updateCompoundBounds = function () {
9073 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9074 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9075
9076 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9077 return this;
9078 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9079
9080
9081 if (!force && cy.batching()) {
9082 return this;
9083 }
9084
9085 function update(parent) {
9086 if (!parent.isParent()) {
9087 return;
9088 }
9089
9090 var _p = parent._private;
9091 var children = parent.children();
9092 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9093 var min = {
9094 width: {
9095 val: parent.pstyle('min-width').pfValue,
9096 left: parent.pstyle('min-width-bias-left'),
9097 right: parent.pstyle('min-width-bias-right')
9098 },
9099 height: {
9100 val: parent.pstyle('min-height').pfValue,
9101 top: parent.pstyle('min-height-bias-top'),
9102 bottom: parent.pstyle('min-height-bias-bottom')
9103 }
9104 };
9105 var bb = children.boundingBox({
9106 includeLabels: includeLabels,
9107 includeOverlays: false,
9108 // updating the compound bounds happens outside of the regular
9109 // cache cycle (i.e. before fired events)
9110 useCache: false
9111 });
9112 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9113
9114 if (bb.w === 0 || bb.h === 0) {
9115 bb = {
9116 w: parent.pstyle('width').pfValue,
9117 h: parent.pstyle('height').pfValue
9118 };
9119 bb.x1 = pos.x - bb.w / 2;
9120 bb.x2 = pos.x + bb.w / 2;
9121 bb.y1 = pos.y - bb.h / 2;
9122 bb.y2 = pos.y + bb.h / 2;
9123 }
9124
9125 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9126 var biasDiff = 0;
9127 var biasComplementDiff = 0;
9128 var biasTotal = propBias + propBiasComplement;
9129
9130 if (propDiff > 0 && biasTotal > 0) {
9131 biasDiff = propBias / biasTotal * propDiff;
9132 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9133 }
9134
9135 return {
9136 biasDiff: biasDiff,
9137 biasComplementDiff: biasComplementDiff
9138 };
9139 }
9140
9141 function computePaddingValues(width, height, paddingObject, relativeTo) {
9142 // Assuming percentage is number from 0 to 1
9143 if (paddingObject.units === '%') {
9144 switch (relativeTo) {
9145 case 'width':
9146 return width > 0 ? paddingObject.pfValue * width : 0;
9147
9148 case 'height':
9149 return height > 0 ? paddingObject.pfValue * height : 0;
9150
9151 case 'average':
9152 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9153
9154 case 'min':
9155 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9156
9157 case 'max':
9158 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9159
9160 default:
9161 return 0;
9162 }
9163 } else if (paddingObject.units === 'px') {
9164 return paddingObject.pfValue;
9165 } else {
9166 return 0;
9167 }
9168 }
9169
9170 var leftVal = min.width.left.value;
9171
9172 if (min.width.left.units === 'px' && min.width.val > 0) {
9173 leftVal = leftVal * 100 / min.width.val;
9174 }
9175
9176 var rightVal = min.width.right.value;
9177
9178 if (min.width.right.units === 'px' && min.width.val > 0) {
9179 rightVal = rightVal * 100 / min.width.val;
9180 }
9181
9182 var topVal = min.height.top.value;
9183
9184 if (min.height.top.units === 'px' && min.height.val > 0) {
9185 topVal = topVal * 100 / min.height.val;
9186 }
9187
9188 var bottomVal = min.height.bottom.value;
9189
9190 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9191 bottomVal = bottomVal * 100 / min.height.val;
9192 }
9193
9194 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9195 var diffLeft = widthBiasDiffs.biasDiff;
9196 var diffRight = widthBiasDiffs.biasComplementDiff;
9197 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9198 var diffTop = heightBiasDiffs.biasDiff;
9199 var diffBottom = heightBiasDiffs.biasComplementDiff;
9200 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9201 _p.autoWidth = Math.max(bb.w, min.width.val);
9202 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9203 _p.autoHeight = Math.max(bb.h, min.height.val);
9204 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9205 }
9206
9207 for (var i = 0; i < this.length; i++) {
9208 var ele = this[i];
9209 var _p = ele._private;
9210
9211 if (!_p.compoundBoundsClean) {
9212 update(ele);
9213
9214 if (!cy.batching()) {
9215 _p.compoundBoundsClean = true;
9216 }
9217 }
9218 }
9219
9220 return this;
9221};
9222
9223var noninf = function noninf(x) {
9224 if (x === Infinity || x === -Infinity) {
9225 return 0;
9226 }
9227
9228 return x;
9229};
9230
9231var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9232 // don't update with zero area boxes
9233 if (x2 - x1 === 0 || y2 - y1 === 0) {
9234 return;
9235 } // don't update with null dim
9236
9237
9238 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9239 return;
9240 }
9241
9242 b.x1 = x1 < b.x1 ? x1 : b.x1;
9243 b.x2 = x2 > b.x2 ? x2 : b.x2;
9244 b.y1 = y1 < b.y1 ? y1 : b.y1;
9245 b.y2 = y2 > b.y2 ? y2 : b.y2;
9246 b.w = b.x2 - b.x1;
9247 b.h = b.y2 - b.y1;
9248};
9249
9250var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9251 if (b2 == null) {
9252 return b;
9253 }
9254
9255 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9256};
9257
9258var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9259 return getPrefixedProperty(obj, field, prefix);
9260};
9261
9262var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9263 if (ele.cy().headless()) {
9264 return;
9265 }
9266
9267 var _p = ele._private;
9268 var rstyle = _p.rstyle;
9269 var halfArW = rstyle.arrowWidth / 2;
9270 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9271 var x;
9272 var y;
9273
9274 if (arrowType !== 'none') {
9275 if (prefix === 'source') {
9276 x = rstyle.srcX;
9277 y = rstyle.srcY;
9278 } else if (prefix === 'target') {
9279 x = rstyle.tgtX;
9280 y = rstyle.tgtY;
9281 } else {
9282 x = rstyle.midX;
9283 y = rstyle.midY;
9284 } // always store the individual arrow bounds
9285
9286
9287 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9288 var bb = bbs[prefix] = bbs[prefix] || {};
9289 bb.x1 = x - halfArW;
9290 bb.y1 = y - halfArW;
9291 bb.x2 = x + halfArW;
9292 bb.y2 = y + halfArW;
9293 bb.w = bb.x2 - bb.x1;
9294 bb.h = bb.y2 - bb.y1;
9295 expandBoundingBox(bb, 1);
9296 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9297 }
9298};
9299
9300var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9301 if (ele.cy().headless()) {
9302 return;
9303 }
9304
9305 var prefixDash;
9306
9307 if (prefix) {
9308 prefixDash = prefix + '-';
9309 } else {
9310 prefixDash = '';
9311 }
9312
9313 var _p = ele._private;
9314 var rstyle = _p.rstyle;
9315 var label = ele.pstyle(prefixDash + 'label').strValue;
9316
9317 if (label) {
9318 var halign = ele.pstyle('text-halign');
9319 var valign = ele.pstyle('text-valign');
9320 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9321 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9322 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9323 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9324 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9325 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9326 var isEdge = ele.isEdge();
9327 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9328 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9329 var borderWidth = ele.pstyle('text-border-width').pfValue;
9330 var halfBorderWidth = borderWidth / 2;
9331 var padding = ele.pstyle('text-background-padding').pfValue;
9332 var lh = labelHeight;
9333 var lw = labelWidth;
9334 var lw_2 = lw / 2;
9335 var lh_2 = lh / 2;
9336 var lx1, lx2, ly1, ly2;
9337
9338 if (isEdge) {
9339 lx1 = labelX - lw_2;
9340 lx2 = labelX + lw_2;
9341 ly1 = labelY - lh_2;
9342 ly2 = labelY + lh_2;
9343 } else {
9344 switch (halign.value) {
9345 case 'left':
9346 lx1 = labelX - lw;
9347 lx2 = labelX;
9348 break;
9349
9350 case 'center':
9351 lx1 = labelX - lw_2;
9352 lx2 = labelX + lw_2;
9353 break;
9354
9355 case 'right':
9356 lx1 = labelX;
9357 lx2 = labelX + lw;
9358 break;
9359 }
9360
9361 switch (valign.value) {
9362 case 'top':
9363 ly1 = labelY - lh;
9364 ly2 = labelY;
9365 break;
9366
9367 case 'center':
9368 ly1 = labelY - lh_2;
9369 ly2 = labelY + lh_2;
9370 break;
9371
9372 case 'bottom':
9373 ly1 = labelY;
9374 ly2 = labelY + lh;
9375 break;
9376 }
9377 } // shift by margin and expand by outline and border
9378
9379
9380 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
9381 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
9382 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
9383 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
9384
9385 var bbPrefix = prefix || 'main';
9386 var bbs = _p.labelBounds;
9387 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9388 bb.x1 = lx1;
9389 bb.y1 = ly1;
9390 bb.x2 = lx2;
9391 bb.y2 = ly2;
9392 bb.w = lx2 - lx1;
9393 bb.h = ly2 - ly1;
9394 expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
9395
9396 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9397 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9398
9399 if (isAutorotate || isPfValue) {
9400 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9401 var cos = Math.cos(theta);
9402 var sin = Math.sin(theta); // rotation point (default value for center-center)
9403
9404 var xo = (lx1 + lx2) / 2;
9405 var yo = (ly1 + ly2) / 2;
9406
9407 if (!isEdge) {
9408 switch (halign.value) {
9409 case 'left':
9410 xo = lx2;
9411 break;
9412
9413 case 'right':
9414 xo = lx1;
9415 break;
9416 }
9417
9418 switch (valign.value) {
9419 case 'top':
9420 yo = ly2;
9421 break;
9422
9423 case 'bottom':
9424 yo = ly1;
9425 break;
9426 }
9427 }
9428
9429 var rotate = function rotate(x, y) {
9430 x = x - xo;
9431 y = y - yo;
9432 return {
9433 x: x * cos - y * sin + xo,
9434 y: x * sin + y * cos + yo
9435 };
9436 };
9437
9438 var px1y1 = rotate(lx1, ly1);
9439 var px1y2 = rotate(lx1, ly2);
9440 var px2y1 = rotate(lx2, ly1);
9441 var px2y2 = rotate(lx2, ly2);
9442 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9443 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9444 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9445 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9446 }
9447
9448 var bbPrefixRot = bbPrefix + 'Rot';
9449 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9450 bbRot.x1 = lx1;
9451 bbRot.y1 = ly1;
9452 bbRot.x2 = lx2;
9453 bbRot.y2 = ly2;
9454 bbRot.w = lx2 - lx1;
9455 bbRot.h = ly2 - ly1;
9456 updateBounds(bounds, lx1, ly1, lx2, ly2);
9457 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9458 }
9459
9460 return bounds;
9461}; // get the bounding box of the elements (in raw model position)
9462
9463
9464var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9465 var cy = ele._private.cy;
9466 var styleEnabled = cy.styleEnabled();
9467 var headless = cy.headless();
9468 var bounds = makeBoundingBox();
9469 var _p = ele._private;
9470 var isNode = ele.isNode();
9471 var isEdge = ele.isEdge();
9472 var ex1, ex2, ey1, ey2; // extrema of body / lines
9473
9474 var x, y; // node pos
9475
9476 var rstyle = _p.rstyle;
9477 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9478 // (other factors like width values will be considered later in this function anyway)
9479
9480 var isDisplayed = function isDisplayed(ele) {
9481 return ele.pstyle('display').value !== 'none';
9482 };
9483
9484 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9485 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9486
9487 if (displayed) {
9488 // displayed suffices, since we will find zero area eles anyway
9489 var overlayOpacity = 0;
9490 var overlayPadding = 0;
9491
9492 if (styleEnabled && options.includeOverlays) {
9493 overlayOpacity = ele.pstyle('overlay-opacity').value;
9494
9495 if (overlayOpacity !== 0) {
9496 overlayPadding = ele.pstyle('overlay-padding').value;
9497 }
9498 }
9499
9500 var w = 0;
9501 var wHalf = 0;
9502
9503 if (styleEnabled) {
9504 w = ele.pstyle('width').pfValue;
9505 wHalf = w / 2;
9506 }
9507
9508 if (isNode && options.includeNodes) {
9509 var pos = ele.position();
9510 x = pos.x;
9511 y = pos.y;
9512
9513 var _w = ele.outerWidth();
9514
9515 var halfW = _w / 2;
9516 var h = ele.outerHeight();
9517 var halfH = h / 2; // handle node dimensions
9518 /////////////////////////
9519
9520 ex1 = x - halfW;
9521 ex2 = x + halfW;
9522 ey1 = y - halfH;
9523 ey2 = y + halfH;
9524 updateBounds(bounds, ex1, ey1, ex2, ey2);
9525 } else if (isEdge && options.includeEdges) {
9526 if (styleEnabled && !headless) {
9527 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9528 //////////////////////////////////////////////
9529
9530 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9531 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9532 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9533 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9534
9535 ex1 -= wHalf;
9536 ex2 += wHalf;
9537 ey1 -= wHalf;
9538 ey2 += wHalf;
9539 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9540 ////////////////
9541
9542 if (curveStyle === 'haystack') {
9543 var hpts = rstyle.haystackPts;
9544
9545 if (hpts && hpts.length === 2) {
9546 ex1 = hpts[0].x;
9547 ey1 = hpts[0].y;
9548 ex2 = hpts[1].x;
9549 ey2 = hpts[1].y;
9550
9551 if (ex1 > ex2) {
9552 var temp = ex1;
9553 ex1 = ex2;
9554 ex2 = temp;
9555 }
9556
9557 if (ey1 > ey2) {
9558 var _temp = ey1;
9559 ey1 = ey2;
9560 ey2 = _temp;
9561 }
9562
9563 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9564 }
9565 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9566 var pts;
9567
9568 switch (curveStyle) {
9569 case 'bezier':
9570 case 'unbundled-bezier':
9571 pts = rstyle.bezierPts;
9572 break;
9573
9574 case 'segments':
9575 case 'taxi':
9576 pts = rstyle.linePts;
9577 break;
9578 }
9579
9580 if (pts != null) {
9581 for (var j = 0; j < pts.length; j++) {
9582 var pt = pts[j];
9583 ex1 = pt.x - wHalf;
9584 ex2 = pt.x + wHalf;
9585 ey1 = pt.y - wHalf;
9586 ey2 = pt.y + wHalf;
9587 updateBounds(bounds, ex1, ey1, ex2, ey2);
9588 }
9589 }
9590 } // bezier-like or segment-like edge
9591
9592 } else {
9593 // headless or style disabled
9594 // fallback on source and target positions
9595 //////////////////////////////////////////
9596 var n1 = ele.source();
9597 var n1pos = n1.position();
9598 var n2 = ele.target();
9599 var n2pos = n2.position();
9600 ex1 = n1pos.x;
9601 ex2 = n2pos.x;
9602 ey1 = n1pos.y;
9603 ey2 = n2pos.y;
9604
9605 if (ex1 > ex2) {
9606 var _temp2 = ex1;
9607 ex1 = ex2;
9608 ex2 = _temp2;
9609 }
9610
9611 if (ey1 > ey2) {
9612 var _temp3 = ey1;
9613 ey1 = ey2;
9614 ey2 = _temp3;
9615 } // take into account edge width
9616
9617
9618 ex1 -= wHalf;
9619 ex2 += wHalf;
9620 ey1 -= wHalf;
9621 ey2 += wHalf;
9622 updateBounds(bounds, ex1, ey1, ex2, ey2);
9623 } // headless or style disabled
9624
9625 } // edges
9626 // handle edge arrow size
9627 /////////////////////////
9628
9629
9630 if (styleEnabled && options.includeEdges && isEdge) {
9631 updateBoundsFromArrow(bounds, ele, 'mid-source');
9632 updateBoundsFromArrow(bounds, ele, 'mid-target');
9633 updateBoundsFromArrow(bounds, ele, 'source');
9634 updateBoundsFromArrow(bounds, ele, 'target');
9635 } // ghost
9636 ////////
9637
9638
9639 if (styleEnabled) {
9640 var ghost = ele.pstyle('ghost').value === 'yes';
9641
9642 if (ghost) {
9643 var gx = ele.pstyle('ghost-offset-x').pfValue;
9644 var gy = ele.pstyle('ghost-offset-y').pfValue;
9645 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9646 }
9647 } // always store the body bounds separately from the labels
9648
9649
9650 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9651 assignBoundingBox(bbBody, bounds);
9652 expandBoundingBoxSides(bbBody, manualExpansion);
9653 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9654 // overlay
9655 //////////
9656
9657 if (styleEnabled) {
9658 ex1 = bounds.x1;
9659 ex2 = bounds.x2;
9660 ey1 = bounds.y1;
9661 ey2 = bounds.y2;
9662 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
9663 } // always store the body bounds separately from the labels
9664
9665
9666 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9667 assignBoundingBox(bbOverlay, bounds);
9668 expandBoundingBoxSides(bbOverlay, manualExpansion);
9669 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9670 // handle label dimensions
9671 //////////////////////////
9672
9673 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9674
9675 if (bbLabels.all != null) {
9676 clearBoundingBox(bbLabels.all);
9677 } else {
9678 bbLabels.all = makeBoundingBox();
9679 }
9680
9681 if (styleEnabled && options.includeLabels) {
9682 if (options.includeMainLabels) {
9683 updateBoundsFromLabel(bounds, ele, null);
9684 }
9685
9686 if (isEdge) {
9687 if (options.includeSourceLabels) {
9688 updateBoundsFromLabel(bounds, ele, 'source');
9689 }
9690
9691 if (options.includeTargetLabels) {
9692 updateBoundsFromLabel(bounds, ele, 'target');
9693 }
9694 }
9695 } // style enabled for labels
9696
9697 } // if displayed
9698
9699
9700 bounds.x1 = noninf(bounds.x1);
9701 bounds.y1 = noninf(bounds.y1);
9702 bounds.x2 = noninf(bounds.x2);
9703 bounds.y2 = noninf(bounds.y2);
9704 bounds.w = noninf(bounds.x2 - bounds.x1);
9705 bounds.h = noninf(bounds.y2 - bounds.y1);
9706
9707 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9708 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9709
9710 expandBoundingBox(bounds, 1);
9711 }
9712
9713 return bounds;
9714};
9715
9716var getKey = function getKey(opts) {
9717 var i = 0;
9718
9719 var tf = function tf(val) {
9720 return (val ? 1 : 0) << i++;
9721 };
9722
9723 var key = 0;
9724 key += tf(opts.incudeNodes);
9725 key += tf(opts.includeEdges);
9726 key += tf(opts.includeLabels);
9727 key += tf(opts.includeMainLabels);
9728 key += tf(opts.includeSourceLabels);
9729 key += tf(opts.includeTargetLabels);
9730 key += tf(opts.includeOverlays);
9731 return key;
9732};
9733
9734var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9735 if (ele.isEdge()) {
9736 var p1 = ele.source().position();
9737 var p2 = ele.target().position();
9738
9739 var r = function r(x) {
9740 return Math.round(x);
9741 };
9742
9743 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9744 } else {
9745 return 0;
9746 }
9747};
9748
9749var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9750 var _p = ele._private;
9751 var bb;
9752 var isEdge = ele.isEdge();
9753 var key = opts == null ? defBbOptsKey : getKey(opts);
9754 var usingDefOpts = key === defBbOptsKey;
9755 var currPosKey = getBoundingBoxPosKey(ele);
9756 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9757 var useCache = opts.useCache && isPosKeySame;
9758
9759 var isDirty = function isDirty(ele) {
9760 return ele._private.bbCache == null;
9761 };
9762
9763 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9764
9765 if (needRecalc) {
9766 if (!isPosKeySame) {
9767 ele.recalculateRenderedStyle();
9768 }
9769
9770 bb = boundingBoxImpl(ele, defBbOpts);
9771 _p.bbCache = bb;
9772 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9773 _p.bbCachePosKey = currPosKey;
9774 } else {
9775 bb = _p.bbCache;
9776 }
9777
9778 if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
9779 var shift = assignShiftToBoundingBox;
9780 var delta = _p.bbCacheShift;
9781
9782 var safeShift = function safeShift(bb, delta) {
9783 if (bb != null) {
9784 shift(bb, delta);
9785 }
9786 };
9787
9788 shift(bb, delta);
9789 var bodyBounds = _p.bodyBounds,
9790 overlayBounds = _p.overlayBounds,
9791 labelBounds = _p.labelBounds,
9792 arrowBounds = _p.arrowBounds;
9793 safeShift(bodyBounds, delta);
9794 safeShift(overlayBounds, delta);
9795
9796 if (arrowBounds != null) {
9797 safeShift(arrowBounds.source, delta);
9798 safeShift(arrowBounds.target, delta);
9799 safeShift(arrowBounds['mid-source'], delta);
9800 safeShift(arrowBounds['mid-target'], delta);
9801 }
9802
9803 if (labelBounds != null) {
9804 safeShift(labelBounds.main, delta);
9805 safeShift(labelBounds.all, delta);
9806 safeShift(labelBounds.source, delta);
9807 safeShift(labelBounds.target, delta);
9808 }
9809 } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
9810
9811
9812 _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
9813
9814 if (!usingDefOpts) {
9815 var isNode = ele.isNode();
9816 bb = makeBoundingBox();
9817
9818 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9819 if (opts.includeOverlays) {
9820 updateBoundsFromBox(bb, _p.overlayBounds);
9821 } else {
9822 updateBoundsFromBox(bb, _p.bodyBounds);
9823 }
9824 }
9825
9826 if (opts.includeLabels) {
9827 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9828 updateBoundsFromBox(bb, _p.labelBounds.all);
9829 } else {
9830 if (opts.includeMainLabels) {
9831 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9832 }
9833
9834 if (opts.includeSourceLabels) {
9835 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9836 }
9837
9838 if (opts.includeTargetLabels) {
9839 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9840 }
9841 }
9842 }
9843
9844 bb.w = bb.x2 - bb.x1;
9845 bb.h = bb.y2 - bb.y1;
9846 }
9847
9848 return bb;
9849};
9850
9851var defBbOpts = {
9852 includeNodes: true,
9853 includeEdges: true,
9854 includeLabels: true,
9855 includeMainLabels: true,
9856 includeSourceLabels: true,
9857 includeTargetLabels: true,
9858 includeOverlays: true,
9859 useCache: true
9860};
9861var defBbOptsKey = getKey(defBbOpts);
9862var filledBbOpts = defaults(defBbOpts);
9863
9864elesfn$k.boundingBox = function (options) {
9865 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9866 // specified s.t. the cache is used, so check for this case to make it faster by
9867 // avoiding the overhead of the rest of the function
9868
9869 if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9870 if (options === undefined) {
9871 options = defBbOpts;
9872 } else {
9873 options = filledBbOpts(options);
9874 }
9875
9876 bounds = cachedBoundingBoxImpl(this[0], options);
9877 } else {
9878 bounds = makeBoundingBox();
9879 options = options || defBbOpts;
9880 var opts = filledBbOpts(options);
9881 var eles = this;
9882 var cy = eles.cy();
9883 var styleEnabled = cy.styleEnabled();
9884
9885 if (styleEnabled) {
9886 for (var i = 0; i < eles.length; i++) {
9887 var ele = eles[i];
9888 var _p = ele._private;
9889 var currPosKey = getBoundingBoxPosKey(ele);
9890 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9891 var useCache = opts.useCache && isPosKeySame;
9892 ele.recalculateRenderedStyle(useCache);
9893 }
9894 }
9895
9896 this.updateCompoundBounds();
9897
9898 for (var _i = 0; _i < eles.length; _i++) {
9899 var _ele = eles[_i];
9900 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9901 }
9902 }
9903
9904 bounds.x1 = noninf(bounds.x1);
9905 bounds.y1 = noninf(bounds.y1);
9906 bounds.x2 = noninf(bounds.x2);
9907 bounds.y2 = noninf(bounds.y2);
9908 bounds.w = noninf(bounds.x2 - bounds.x1);
9909 bounds.h = noninf(bounds.y2 - bounds.y1);
9910 return bounds;
9911};
9912
9913elesfn$k.dirtyBoundingBoxCache = function () {
9914 for (var i = 0; i < this.length; i++) {
9915 var _p = this[i]._private;
9916 _p.bbCache = null;
9917 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9918 _p.bbCachePosKey = null;
9919 _p.bodyBounds = null;
9920 _p.overlayBounds = null;
9921 _p.labelBounds.all = null;
9922 _p.labelBounds.source = null;
9923 _p.labelBounds.target = null;
9924 _p.labelBounds.main = null;
9925 _p.labelBounds.sourceRot = null;
9926 _p.labelBounds.targetRot = null;
9927 _p.labelBounds.mainRot = null;
9928 _p.arrowBounds.source = null;
9929 _p.arrowBounds.target = null;
9930 _p.arrowBounds['mid-source'] = null;
9931 _p.arrowBounds['mid-target'] = null;
9932 }
9933
9934 this.emitAndNotify('bounds');
9935 return this;
9936};
9937
9938elesfn$k.shiftCachedBoundingBox = function (delta) {
9939 for (var i = 0; i < this.length; i++) {
9940 var ele = this[i];
9941 var _p = ele._private;
9942 var bb = _p.bbCache;
9943
9944 if (bb != null) {
9945 _p.bbCacheShift.x += delta.x;
9946 _p.bbCacheShift.y += delta.y;
9947 }
9948 }
9949
9950 this.emitAndNotify('bounds');
9951 return this;
9952}; // private helper to get bounding box for custom node positions
9953// - good for perf in certain cases but currently requires dirtying the rendered style
9954// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9955// - try to use for only things like discrete layouts where the node position would change anyway
9956
9957
9958elesfn$k.boundingBoxAt = function (fn) {
9959 var nodes = this.nodes();
9960 var cy = this.cy();
9961 var hasCompoundNodes = cy.hasCompoundNodes();
9962
9963 if (hasCompoundNodes) {
9964 nodes = nodes.filter(function (node) {
9965 return !node.isParent();
9966 });
9967 }
9968
9969 if (plainObject(fn)) {
9970 var obj = fn;
9971
9972 fn = function fn() {
9973 return obj;
9974 };
9975 }
9976
9977 var storeOldPos = function storeOldPos(node, i) {
9978 return node._private.bbAtOldPos = fn(node, i);
9979 };
9980
9981 var getOldPos = function getOldPos(node) {
9982 return node._private.bbAtOldPos;
9983 };
9984
9985 cy.startBatch();
9986 nodes.forEach(storeOldPos).silentPositions(fn);
9987
9988 if (hasCompoundNodes) {
9989 this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9990 }
9991
9992 var bb = copyBoundingBox(this.boundingBox({
9993 useCache: false
9994 }));
9995 nodes.silentPositions(getOldPos);
9996 cy.endBatch();
9997 return bb;
9998};
9999
10000fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
10001fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
10002var bounds = elesfn$k;
10003
10004var fn$4, elesfn$l;
10005fn$4 = elesfn$l = {};
10006
10007var defineDimFns = function defineDimFns(opts) {
10008 opts.uppercaseName = capitalize(opts.name);
10009 opts.autoName = 'auto' + opts.uppercaseName;
10010 opts.labelName = 'label' + opts.uppercaseName;
10011 opts.outerName = 'outer' + opts.uppercaseName;
10012 opts.uppercaseOuterName = capitalize(opts.outerName);
10013
10014 fn$4[opts.name] = function dimImpl() {
10015 var ele = this[0];
10016 var _p = ele._private;
10017 var cy = _p.cy;
10018 var styleEnabled = cy._private.styleEnabled;
10019
10020 if (ele) {
10021 if (styleEnabled) {
10022 if (ele.isParent()) {
10023 ele.updateCompoundBounds();
10024 return _p[opts.autoName] || 0;
10025 }
10026
10027 var d = ele.pstyle(opts.name);
10028
10029 switch (d.strValue) {
10030 case 'label':
10031 ele.recalculateRenderedStyle();
10032 return _p.rstyle[opts.labelName] || 0;
10033
10034 default:
10035 return d.pfValue;
10036 }
10037 } else {
10038 return 1;
10039 }
10040 }
10041 };
10042
10043 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10044 var ele = this[0];
10045 var _p = ele._private;
10046 var cy = _p.cy;
10047 var styleEnabled = cy._private.styleEnabled;
10048
10049 if (ele) {
10050 if (styleEnabled) {
10051 var dim = ele[opts.name]();
10052 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10053
10054 var padding = 2 * ele.padding();
10055 return dim + border + padding;
10056 } else {
10057 return 1;
10058 }
10059 }
10060 };
10061
10062 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10063 var ele = this[0];
10064
10065 if (ele) {
10066 var d = ele[opts.name]();
10067 return d * this.cy().zoom();
10068 }
10069 };
10070
10071 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10072 var ele = this[0];
10073
10074 if (ele) {
10075 var od = ele[opts.outerName]();
10076 return od * this.cy().zoom();
10077 }
10078 };
10079};
10080
10081defineDimFns({
10082 name: 'width'
10083});
10084defineDimFns({
10085 name: 'height'
10086});
10087
10088elesfn$l.padding = function () {
10089 var ele = this[0];
10090 var _p = ele._private;
10091
10092 if (ele.isParent()) {
10093 ele.updateCompoundBounds();
10094
10095 if (_p.autoPadding !== undefined) {
10096 return _p.autoPadding;
10097 } else {
10098 return ele.pstyle('padding').pfValue;
10099 }
10100 } else {
10101 return ele.pstyle('padding').pfValue;
10102 }
10103};
10104
10105elesfn$l.paddedHeight = function () {
10106 var ele = this[0];
10107 return ele.height() + 2 * ele.padding();
10108};
10109
10110elesfn$l.paddedWidth = function () {
10111 var ele = this[0];
10112 return ele.width() + 2 * ele.padding();
10113};
10114
10115var widthHeight = elesfn$l;
10116
10117var ifEdge = function ifEdge(ele, getValue) {
10118 if (ele.isEdge()) {
10119 return getValue(ele);
10120 }
10121};
10122
10123var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10124 if (ele.isEdge()) {
10125 var cy = ele.cy();
10126 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10127 }
10128};
10129
10130var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10131 if (ele.isEdge()) {
10132 var cy = ele.cy();
10133 var pan = cy.pan();
10134 var zoom = cy.zoom();
10135 return getPoints(ele).map(function (p) {
10136 return modelToRenderedPosition(p, zoom, pan);
10137 });
10138 }
10139};
10140
10141var controlPoints = function controlPoints(ele) {
10142 return ele.renderer().getControlPoints(ele);
10143};
10144
10145var segmentPoints = function segmentPoints(ele) {
10146 return ele.renderer().getSegmentPoints(ele);
10147};
10148
10149var sourceEndpoint = function sourceEndpoint(ele) {
10150 return ele.renderer().getSourceEndpoint(ele);
10151};
10152
10153var targetEndpoint = function targetEndpoint(ele) {
10154 return ele.renderer().getTargetEndpoint(ele);
10155};
10156
10157var midpoint = function midpoint(ele) {
10158 return ele.renderer().getEdgeMidpoint(ele);
10159};
10160
10161var pts = {
10162 controlPoints: {
10163 get: controlPoints,
10164 mult: true
10165 },
10166 segmentPoints: {
10167 get: segmentPoints,
10168 mult: true
10169 },
10170 sourceEndpoint: {
10171 get: sourceEndpoint
10172 },
10173 targetEndpoint: {
10174 get: targetEndpoint
10175 },
10176 midpoint: {
10177 get: midpoint
10178 }
10179};
10180
10181var renderedName = function renderedName(name) {
10182 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10183};
10184
10185var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10186 var spec = pts[name];
10187 var rName = renderedName(name);
10188
10189 obj[name] = function () {
10190 return ifEdge(this, spec.get);
10191 };
10192
10193 if (spec.mult) {
10194 obj[rName] = function () {
10195 return ifEdgeRenderedPositions(this, spec.get);
10196 };
10197 } else {
10198 obj[rName] = function () {
10199 return ifEdgeRenderedPosition(this, spec.get);
10200 };
10201 }
10202
10203 return obj;
10204}, {});
10205
10206var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10207
10208/*!
10209Event object based on jQuery events, MIT license
10210
10211https://jquery.org/license/
10212https://tldrlegal.com/license/mit-license
10213https://github.com/jquery/jquery/blob/master/src/event.js
10214*/
10215var Event = function Event(src, props) {
10216 this.recycle(src, props);
10217};
10218
10219function returnFalse() {
10220 return false;
10221}
10222
10223function returnTrue() {
10224 return true;
10225} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10226
10227
10228Event.prototype = {
10229 instanceString: function instanceString() {
10230 return 'event';
10231 },
10232 recycle: function recycle(src, props) {
10233 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10234
10235 if (src != null && src.preventDefault) {
10236 // Browser Event object
10237 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10238 // by a handler lower down the tree; reflect the correct value.
10239
10240 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10241 } else if (src != null && src.type) {
10242 // Plain object containing all event details
10243 props = src;
10244 } else {
10245 // Event string
10246 this.type = src;
10247 } // Put explicitly provided properties onto the event object
10248
10249
10250 if (props != null) {
10251 // more efficient to manually copy fields we use
10252 this.originalEvent = props.originalEvent;
10253 this.type = props.type != null ? props.type : this.type;
10254 this.cy = props.cy;
10255 this.target = props.target;
10256 this.position = props.position;
10257 this.renderedPosition = props.renderedPosition;
10258 this.namespace = props.namespace;
10259 this.layout = props.layout;
10260 }
10261
10262 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10263 // create a rendered position based on the passed position
10264 var pos = this.position;
10265 var zoom = this.cy.zoom();
10266 var pan = this.cy.pan();
10267 this.renderedPosition = {
10268 x: pos.x * zoom + pan.x,
10269 y: pos.y * zoom + pan.y
10270 };
10271 } // Create a timestamp if incoming event doesn't have one
10272
10273
10274 this.timeStamp = src && src.timeStamp || Date.now();
10275 },
10276 preventDefault: function preventDefault() {
10277 this.isDefaultPrevented = returnTrue;
10278 var e = this.originalEvent;
10279
10280 if (!e) {
10281 return;
10282 } // if preventDefault exists run it on the original event
10283
10284
10285 if (e.preventDefault) {
10286 e.preventDefault();
10287 }
10288 },
10289 stopPropagation: function stopPropagation() {
10290 this.isPropagationStopped = returnTrue;
10291 var e = this.originalEvent;
10292
10293 if (!e) {
10294 return;
10295 } // if stopPropagation exists run it on the original event
10296
10297
10298 if (e.stopPropagation) {
10299 e.stopPropagation();
10300 }
10301 },
10302 stopImmediatePropagation: function stopImmediatePropagation() {
10303 this.isImmediatePropagationStopped = returnTrue;
10304 this.stopPropagation();
10305 },
10306 isDefaultPrevented: returnFalse,
10307 isPropagationStopped: returnFalse,
10308 isImmediatePropagationStopped: returnFalse
10309};
10310
10311var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10312
10313var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10314
10315var defaults$8 = {
10316 qualifierCompare: function qualifierCompare(q1, q2) {
10317 return q1 === q2;
10318 },
10319 eventMatches: function eventMatches()
10320 /*context, listener, eventObj*/
10321 {
10322 return true;
10323 },
10324 addEventFields: function addEventFields()
10325 /*context, evt*/
10326 {},
10327 callbackContext: function callbackContext(context
10328 /*, listener, eventObj*/
10329 ) {
10330 return context;
10331 },
10332 beforeEmit: function beforeEmit()
10333 /* context, listener, eventObj */
10334 {},
10335 afterEmit: function afterEmit()
10336 /* context, listener, eventObj */
10337 {},
10338 bubble: function bubble()
10339 /*context*/
10340 {
10341 return false;
10342 },
10343 parent: function parent()
10344 /*context*/
10345 {
10346 return null;
10347 },
10348 context: null
10349};
10350var defaultsKeys = Object.keys(defaults$8);
10351var emptyOpts = {};
10352
10353function Emitter() {
10354 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10355 var context = arguments.length > 1 ? arguments[1] : undefined;
10356
10357 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10358 for (var i = 0; i < defaultsKeys.length; i++) {
10359 var key = defaultsKeys[i];
10360 this[key] = opts[key] || defaults$8[key];
10361 }
10362
10363 this.context = context || this.context;
10364 this.listeners = [];
10365 this.emitting = 0;
10366}
10367
10368var p = Emitter.prototype;
10369
10370var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10371 if (fn(qualifier)) {
10372 callback = qualifier;
10373 qualifier = null;
10374 }
10375
10376 if (confOverrides) {
10377 if (conf == null) {
10378 conf = confOverrides;
10379 } else {
10380 conf = extend({}, conf, confOverrides);
10381 }
10382 }
10383
10384 var eventList = array(events) ? events : events.split(/\s+/);
10385
10386 for (var i = 0; i < eventList.length; i++) {
10387 var evt = eventList[i];
10388
10389 if (emptyString(evt)) {
10390 continue;
10391 }
10392
10393 var match = evt.match(eventRegex); // type[.namespace]
10394
10395 if (match) {
10396 var type = match[1];
10397 var namespace = match[2] ? match[2] : null;
10398 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10399
10400 if (ret === false) {
10401 break;
10402 } // allow exiting early
10403
10404 }
10405 }
10406};
10407
10408var makeEventObj = function makeEventObj(self, obj) {
10409 self.addEventFields(self.context, obj);
10410 return new Event(obj.type, obj);
10411};
10412
10413var forEachEventObj = function forEachEventObj(self, handler, events) {
10414 if (event(events)) {
10415 handler(self, events);
10416 return;
10417 } else if (plainObject(events)) {
10418 handler(self, makeEventObj(self, events));
10419 return;
10420 }
10421
10422 var eventList = array(events) ? events : events.split(/\s+/);
10423
10424 for (var i = 0; i < eventList.length; i++) {
10425 var evt = eventList[i];
10426
10427 if (emptyString(evt)) {
10428 continue;
10429 }
10430
10431 var match = evt.match(eventRegex); // type[.namespace]
10432
10433 if (match) {
10434 var type = match[1];
10435 var namespace = match[2] ? match[2] : null;
10436 var eventObj = makeEventObj(self, {
10437 type: type,
10438 namespace: namespace,
10439 target: self.context
10440 });
10441 handler(self, eventObj);
10442 }
10443 }
10444};
10445
10446p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10447 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10448 if (fn(callback)) {
10449 self.listeners.push({
10450 event: event,
10451 // full event string
10452 callback: callback,
10453 // callback to run
10454 type: type,
10455 // the event type (e.g. 'click')
10456 namespace: namespace,
10457 // the event namespace (e.g. ".foo")
10458 qualifier: qualifier,
10459 // a restriction on whether to match this emitter
10460 conf: conf // additional configuration
10461
10462 });
10463 }
10464 }, events, qualifier, callback, conf, confOverrides);
10465 return this;
10466};
10467
10468p.one = function (events, qualifier, callback, conf) {
10469 return this.on(events, qualifier, callback, conf, {
10470 one: true
10471 });
10472};
10473
10474p.removeListener = p.off = function (events, qualifier, callback, conf) {
10475 var _this = this;
10476
10477 if (this.emitting !== 0) {
10478 this.listeners = copyArray(this.listeners);
10479 }
10480
10481 var listeners = this.listeners;
10482
10483 var _loop = function _loop(i) {
10484 var listener = listeners[i];
10485 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10486 /*, conf*/
10487 ) {
10488 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10489 listeners.splice(i, 1);
10490 return false;
10491 }
10492 }, events, qualifier, callback, conf);
10493 };
10494
10495 for (var i = listeners.length - 1; i >= 0; i--) {
10496 _loop(i);
10497 }
10498
10499 return this;
10500};
10501
10502p.removeAllListeners = function () {
10503 return this.removeListener('*');
10504};
10505
10506p.emit = p.trigger = function (events, extraParams, manualCallback) {
10507 var listeners = this.listeners;
10508 var numListenersBeforeEmit = listeners.length;
10509 this.emitting++;
10510
10511 if (!array(extraParams)) {
10512 extraParams = [extraParams];
10513 }
10514
10515 forEachEventObj(this, function (self, eventObj) {
10516 if (manualCallback != null) {
10517 listeners = [{
10518 event: eventObj.event,
10519 type: eventObj.type,
10520 namespace: eventObj.namespace,
10521 callback: manualCallback
10522 }];
10523 numListenersBeforeEmit = listeners.length;
10524 }
10525
10526 var _loop2 = function _loop2(i) {
10527 var listener = listeners[i];
10528
10529 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10530 var args = [eventObj];
10531
10532 if (extraParams != null) {
10533 push(args, extraParams);
10534 }
10535
10536 self.beforeEmit(self.context, listener, eventObj);
10537
10538 if (listener.conf && listener.conf.one) {
10539 self.listeners = self.listeners.filter(function (l) {
10540 return l !== listener;
10541 });
10542 }
10543
10544 var context = self.callbackContext(self.context, listener, eventObj);
10545 var ret = listener.callback.apply(context, args);
10546 self.afterEmit(self.context, listener, eventObj);
10547
10548 if (ret === false) {
10549 eventObj.stopPropagation();
10550 eventObj.preventDefault();
10551 }
10552 } // if listener matches
10553
10554 };
10555
10556 for (var i = 0; i < numListenersBeforeEmit; i++) {
10557 _loop2(i);
10558 } // for listener
10559
10560
10561 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10562 self.parent(self.context).emit(eventObj, extraParams);
10563 }
10564 }, events);
10565 this.emitting--;
10566 return this;
10567};
10568
10569var emitterOptions = {
10570 qualifierCompare: function qualifierCompare(selector1, selector2) {
10571 if (selector1 == null || selector2 == null) {
10572 return selector1 == null && selector2 == null;
10573 } else {
10574 return selector1.sameText(selector2);
10575 }
10576 },
10577 eventMatches: function eventMatches(ele, listener, eventObj) {
10578 var selector = listener.qualifier;
10579
10580 if (selector != null) {
10581 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10582 }
10583
10584 return true;
10585 },
10586 addEventFields: function addEventFields(ele, evt) {
10587 evt.cy = ele.cy();
10588 evt.target = ele;
10589 },
10590 callbackContext: function callbackContext(ele, listener, eventObj) {
10591 return listener.qualifier != null ? eventObj.target : ele;
10592 },
10593 beforeEmit: function beforeEmit(context, listener
10594 /*, eventObj*/
10595 ) {
10596 if (listener.conf && listener.conf.once) {
10597 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10598 }
10599 },
10600 bubble: function bubble() {
10601 return true;
10602 },
10603 parent: function parent(ele) {
10604 return ele.isChild() ? ele.parent() : ele.cy();
10605 }
10606};
10607
10608var argSelector = function argSelector(arg) {
10609 if (string(arg)) {
10610 return new Selector(arg);
10611 } else {
10612 return arg;
10613 }
10614};
10615
10616var elesfn$m = {
10617 createEmitter: function createEmitter() {
10618 for (var i = 0; i < this.length; i++) {
10619 var ele = this[i];
10620 var _p = ele._private;
10621
10622 if (!_p.emitter) {
10623 _p.emitter = new Emitter(emitterOptions, ele);
10624 }
10625 }
10626
10627 return this;
10628 },
10629 emitter: function emitter() {
10630 return this._private.emitter;
10631 },
10632 on: function on(events, selector, callback) {
10633 var argSel = argSelector(selector);
10634
10635 for (var i = 0; i < this.length; i++) {
10636 var ele = this[i];
10637 ele.emitter().on(events, argSel, callback);
10638 }
10639
10640 return this;
10641 },
10642 removeListener: function removeListener(events, selector, callback) {
10643 var argSel = argSelector(selector);
10644
10645 for (var i = 0; i < this.length; i++) {
10646 var ele = this[i];
10647 ele.emitter().removeListener(events, argSel, callback);
10648 }
10649
10650 return this;
10651 },
10652 removeAllListeners: function removeAllListeners() {
10653 for (var i = 0; i < this.length; i++) {
10654 var ele = this[i];
10655 ele.emitter().removeAllListeners();
10656 }
10657
10658 return this;
10659 },
10660 one: function one(events, selector, callback) {
10661 var argSel = argSelector(selector);
10662
10663 for (var i = 0; i < this.length; i++) {
10664 var ele = this[i];
10665 ele.emitter().one(events, argSel, callback);
10666 }
10667
10668 return this;
10669 },
10670 once: function once(events, selector, callback) {
10671 var argSel = argSelector(selector);
10672
10673 for (var i = 0; i < this.length; i++) {
10674 var ele = this[i];
10675 ele.emitter().on(events, argSel, callback, {
10676 once: true,
10677 onceCollection: this
10678 });
10679 }
10680 },
10681 emit: function emit(events, extraParams) {
10682 for (var i = 0; i < this.length; i++) {
10683 var ele = this[i];
10684 ele.emitter().emit(events, extraParams);
10685 }
10686
10687 return this;
10688 },
10689 emitAndNotify: function emitAndNotify(event, extraParams) {
10690 // for internal use only
10691 if (this.length === 0) {
10692 return;
10693 } // empty collections don't need to notify anything
10694 // notify renderer
10695
10696
10697 this.cy().notify(event, this);
10698 this.emit(event, extraParams);
10699 return this;
10700 }
10701};
10702define$3.eventAliasesOn(elesfn$m);
10703
10704var elesfn$n = {
10705 nodes: function nodes(selector) {
10706 return this.filter(function (ele) {
10707 return ele.isNode();
10708 }).filter(selector);
10709 },
10710 edges: function edges(selector) {
10711 return this.filter(function (ele) {
10712 return ele.isEdge();
10713 }).filter(selector);
10714 },
10715 // internal helper to get nodes and edges as separate collections with single iteration over elements
10716 byGroup: function byGroup() {
10717 var nodes = this.spawn();
10718 var edges = this.spawn();
10719
10720 for (var i = 0; i < this.length; i++) {
10721 var ele = this[i];
10722
10723 if (ele.isNode()) {
10724 nodes.push(ele);
10725 } else {
10726 edges.push(ele);
10727 }
10728 }
10729
10730 return {
10731 nodes: nodes,
10732 edges: edges
10733 };
10734 },
10735 filter: function filter(_filter, thisArg) {
10736 if (_filter === undefined) {
10737 // check this first b/c it's the most common/performant case
10738 return this;
10739 } else if (string(_filter) || elementOrCollection(_filter)) {
10740 return new Selector(_filter).filter(this);
10741 } else if (fn(_filter)) {
10742 var filterEles = this.spawn();
10743 var eles = this;
10744
10745 for (var i = 0; i < eles.length; i++) {
10746 var ele = eles[i];
10747 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10748
10749 if (include) {
10750 filterEles.push(ele);
10751 }
10752 }
10753
10754 return filterEles;
10755 }
10756
10757 return this.spawn(); // if not handled by above, give 'em an empty collection
10758 },
10759 not: function not(toRemove) {
10760 if (!toRemove) {
10761 return this;
10762 } else {
10763 if (string(toRemove)) {
10764 toRemove = this.filter(toRemove);
10765 }
10766
10767 var elements = this.spawn();
10768
10769 for (var i = 0; i < this.length; i++) {
10770 var element = this[i];
10771 var remove = toRemove.has(element);
10772
10773 if (!remove) {
10774 elements.push(element);
10775 }
10776 }
10777
10778 return elements;
10779 }
10780 },
10781 absoluteComplement: function absoluteComplement() {
10782 var cy = this.cy();
10783 return cy.mutableElements().not(this);
10784 },
10785 intersect: function intersect(other) {
10786 // if a selector is specified, then filter by it instead
10787 if (string(other)) {
10788 var selector = other;
10789 return this.filter(selector);
10790 }
10791
10792 var elements = this.spawn();
10793 var col1 = this;
10794 var col2 = other;
10795 var col1Smaller = this.length < other.length;
10796 var colS = col1Smaller ? col1 : col2;
10797 var colL = col1Smaller ? col2 : col1;
10798
10799 for (var i = 0; i < colS.length; i++) {
10800 var ele = colS[i];
10801
10802 if (colL.has(ele)) {
10803 elements.push(ele);
10804 }
10805 }
10806
10807 return elements;
10808 },
10809 xor: function xor(other) {
10810 var cy = this._private.cy;
10811
10812 if (string(other)) {
10813 other = cy.$(other);
10814 }
10815
10816 var elements = this.spawn();
10817 var col1 = this;
10818 var col2 = other;
10819
10820 var add = function add(col, other) {
10821 for (var i = 0; i < col.length; i++) {
10822 var ele = col[i];
10823 var id = ele._private.data.id;
10824 var inOther = other.hasElementWithId(id);
10825
10826 if (!inOther) {
10827 elements.push(ele);
10828 }
10829 }
10830 };
10831
10832 add(col1, col2);
10833 add(col2, col1);
10834 return elements;
10835 },
10836 diff: function diff(other) {
10837 var cy = this._private.cy;
10838
10839 if (string(other)) {
10840 other = cy.$(other);
10841 }
10842
10843 var left = this.spawn();
10844 var right = this.spawn();
10845 var both = this.spawn();
10846 var col1 = this;
10847 var col2 = other;
10848
10849 var add = function add(col, other, retEles) {
10850 for (var i = 0; i < col.length; i++) {
10851 var ele = col[i];
10852 var id = ele._private.data.id;
10853 var inOther = other.hasElementWithId(id);
10854
10855 if (inOther) {
10856 both.merge(ele);
10857 } else {
10858 retEles.push(ele);
10859 }
10860 }
10861 };
10862
10863 add(col1, col2, left);
10864 add(col2, col1, right);
10865 return {
10866 left: left,
10867 right: right,
10868 both: both
10869 };
10870 },
10871 add: function add(toAdd) {
10872 var cy = this._private.cy;
10873
10874 if (!toAdd) {
10875 return this;
10876 }
10877
10878 if (string(toAdd)) {
10879 var selector = toAdd;
10880 toAdd = cy.mutableElements().filter(selector);
10881 }
10882
10883 var elements = this.spawnSelf();
10884
10885 for (var i = 0; i < toAdd.length; i++) {
10886 var ele = toAdd[i];
10887 var add = !this.has(ele);
10888
10889 if (add) {
10890 elements.push(ele);
10891 }
10892 }
10893
10894 return elements;
10895 },
10896 // in place merge on calling collection
10897 merge: function merge(toAdd) {
10898 var _p = this._private;
10899 var cy = _p.cy;
10900
10901 if (!toAdd) {
10902 return this;
10903 }
10904
10905 if (toAdd && string(toAdd)) {
10906 var selector = toAdd;
10907 toAdd = cy.mutableElements().filter(selector);
10908 }
10909
10910 var map = _p.map;
10911
10912 for (var i = 0; i < toAdd.length; i++) {
10913 var toAddEle = toAdd[i];
10914 var id = toAddEle._private.data.id;
10915 var add = !map.has(id);
10916
10917 if (add) {
10918 var index = this.length++;
10919 this[index] = toAddEle;
10920 map.set(id, {
10921 ele: toAddEle,
10922 index: index
10923 });
10924 }
10925 }
10926
10927 return this; // chaining
10928 },
10929 unmergeAt: function unmergeAt(i) {
10930 var ele = this[i];
10931 var id = ele.id();
10932 var _p = this._private;
10933 var map = _p.map; // remove ele
10934
10935 this[i] = undefined;
10936 map["delete"](id);
10937 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10938
10939 if (this.length > 1 && !unmergedLastEle) {
10940 var lastEleI = this.length - 1;
10941 var lastEle = this[lastEleI];
10942 var lastEleId = lastEle._private.data.id;
10943 this[lastEleI] = undefined;
10944 this[i] = lastEle;
10945 map.set(lastEleId, {
10946 ele: lastEle,
10947 index: i
10948 });
10949 } // the collection is now 1 ele smaller
10950
10951
10952 this.length--;
10953 return this;
10954 },
10955 // remove single ele in place in calling collection
10956 unmergeOne: function unmergeOne(ele) {
10957 ele = ele[0];
10958 var _p = this._private;
10959 var id = ele._private.data.id;
10960 var map = _p.map;
10961 var entry = map.get(id);
10962
10963 if (!entry) {
10964 return this; // no need to remove
10965 }
10966
10967 var i = entry.index;
10968 this.unmergeAt(i);
10969 return this;
10970 },
10971 // remove eles in place on calling collection
10972 unmerge: function unmerge(toRemove) {
10973 var cy = this._private.cy;
10974
10975 if (!toRemove) {
10976 return this;
10977 }
10978
10979 if (toRemove && string(toRemove)) {
10980 var selector = toRemove;
10981 toRemove = cy.mutableElements().filter(selector);
10982 }
10983
10984 for (var i = 0; i < toRemove.length; i++) {
10985 this.unmergeOne(toRemove[i]);
10986 }
10987
10988 return this; // chaining
10989 },
10990 unmergeBy: function unmergeBy(toRmFn) {
10991 for (var i = this.length - 1; i >= 0; i--) {
10992 var ele = this[i];
10993
10994 if (toRmFn(ele)) {
10995 this.unmergeAt(i);
10996 }
10997 }
10998
10999 return this;
11000 },
11001 map: function map(mapFn, thisArg) {
11002 var arr = [];
11003 var eles = this;
11004
11005 for (var i = 0; i < eles.length; i++) {
11006 var ele = eles[i];
11007 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11008 arr.push(ret);
11009 }
11010
11011 return arr;
11012 },
11013 reduce: function reduce(fn, initialValue) {
11014 var val = initialValue;
11015 var eles = this;
11016
11017 for (var i = 0; i < eles.length; i++) {
11018 val = fn(val, eles[i], i, eles);
11019 }
11020
11021 return val;
11022 },
11023 max: function max(valFn, thisArg) {
11024 var max = -Infinity;
11025 var maxEle;
11026 var eles = this;
11027
11028 for (var i = 0; i < eles.length; i++) {
11029 var ele = eles[i];
11030 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11031
11032 if (val > max) {
11033 max = val;
11034 maxEle = ele;
11035 }
11036 }
11037
11038 return {
11039 value: max,
11040 ele: maxEle
11041 };
11042 },
11043 min: function min(valFn, thisArg) {
11044 var min = Infinity;
11045 var minEle;
11046 var eles = this;
11047
11048 for (var i = 0; i < eles.length; i++) {
11049 var ele = eles[i];
11050 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11051
11052 if (val < min) {
11053 min = val;
11054 minEle = ele;
11055 }
11056 }
11057
11058 return {
11059 value: min,
11060 ele: minEle
11061 };
11062 }
11063}; // aliases
11064
11065var fn$5 = elesfn$n;
11066fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11067fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11068fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11069fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11070fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11071fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11072
11073var elesfn$o = {
11074 isNode: function isNode() {
11075 return this.group() === 'nodes';
11076 },
11077 isEdge: function isEdge() {
11078 return this.group() === 'edges';
11079 },
11080 isLoop: function isLoop() {
11081 return this.isEdge() && this.source()[0] === this.target()[0];
11082 },
11083 isSimple: function isSimple() {
11084 return this.isEdge() && this.source()[0] !== this.target()[0];
11085 },
11086 group: function group() {
11087 var ele = this[0];
11088
11089 if (ele) {
11090 return ele._private.group;
11091 }
11092 }
11093};
11094
11095/**
11096 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11097 * and z-index (low to high). These styles affect how this applies:
11098 *
11099 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11100 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11101 * root to leaves of the compound graph. The last drawn is `top`.
11102 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11103 * `manual` ignores this convention and draws based on the `z-index` value setting.
11104 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11105 * `z-index` will be drawn on top of an element with a lower `z-index`.
11106 */
11107
11108var zIndexSort = function zIndexSort(a, b) {
11109 var cy = a.cy();
11110 var hasCompoundNodes = cy.hasCompoundNodes();
11111
11112 function getDepth(ele) {
11113 var style = ele.pstyle('z-compound-depth');
11114
11115 if (style.value === 'auto') {
11116 return hasCompoundNodes ? ele.zDepth() : 0;
11117 } else if (style.value === 'bottom') {
11118 return -1;
11119 } else if (style.value === 'top') {
11120 return MAX_INT;
11121 } // 'orphan'
11122
11123
11124 return 0;
11125 }
11126
11127 var depthDiff = getDepth(a) - getDepth(b);
11128
11129 if (depthDiff !== 0) {
11130 return depthDiff;
11131 }
11132
11133 function getEleDepth(ele) {
11134 var style = ele.pstyle('z-index-compare');
11135
11136 if (style.value === 'auto') {
11137 return ele.isNode() ? 1 : 0;
11138 } // 'manual'
11139
11140
11141 return 0;
11142 }
11143
11144 var eleDiff = getEleDepth(a) - getEleDepth(b);
11145
11146 if (eleDiff !== 0) {
11147 return eleDiff;
11148 }
11149
11150 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11151
11152 if (zDiff !== 0) {
11153 return zDiff;
11154 } // compare indices in the core (order added to graph w/ last on top)
11155
11156
11157 return a.poolIndex() - b.poolIndex();
11158};
11159
11160var elesfn$p = {
11161 forEach: function forEach(fn$1, thisArg) {
11162 if (fn(fn$1)) {
11163 var N = this.length;
11164
11165 for (var i = 0; i < N; i++) {
11166 var ele = this[i];
11167 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11168
11169 if (ret === false) {
11170 break;
11171 } // exit each early on return false
11172
11173 }
11174 }
11175
11176 return this;
11177 },
11178 toArray: function toArray() {
11179 var array = [];
11180
11181 for (var i = 0; i < this.length; i++) {
11182 array.push(this[i]);
11183 }
11184
11185 return array;
11186 },
11187 slice: function slice(start, end) {
11188 var array = [];
11189 var thisSize = this.length;
11190
11191 if (end == null) {
11192 end = thisSize;
11193 }
11194
11195 if (start == null) {
11196 start = 0;
11197 }
11198
11199 if (start < 0) {
11200 start = thisSize + start;
11201 }
11202
11203 if (end < 0) {
11204 end = thisSize + end;
11205 }
11206
11207 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11208 array.push(this[i]);
11209 }
11210
11211 return this.spawn(array);
11212 },
11213 size: function size() {
11214 return this.length;
11215 },
11216 eq: function eq(i) {
11217 return this[i] || this.spawn();
11218 },
11219 first: function first() {
11220 return this[0] || this.spawn();
11221 },
11222 last: function last() {
11223 return this[this.length - 1] || this.spawn();
11224 },
11225 empty: function empty() {
11226 return this.length === 0;
11227 },
11228 nonempty: function nonempty() {
11229 return !this.empty();
11230 },
11231 sort: function sort(sortFn) {
11232 if (!fn(sortFn)) {
11233 return this;
11234 }
11235
11236 var sorted = this.toArray().sort(sortFn);
11237 return this.spawn(sorted);
11238 },
11239 sortByZIndex: function sortByZIndex() {
11240 return this.sort(zIndexSort);
11241 },
11242 zDepth: function zDepth() {
11243 var ele = this[0];
11244
11245 if (!ele) {
11246 return undefined;
11247 } // let cy = ele.cy();
11248
11249
11250 var _p = ele._private;
11251 var group = _p.group;
11252
11253 if (group === 'nodes') {
11254 var depth = _p.data.parent ? ele.parents().size() : 0;
11255
11256 if (!ele.isParent()) {
11257 return MAX_INT - 1; // childless nodes always on top
11258 }
11259
11260 return depth;
11261 } else {
11262 var src = _p.source;
11263 var tgt = _p.target;
11264 var srcDepth = src.zDepth();
11265 var tgtDepth = tgt.zDepth();
11266 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11267 }
11268 }
11269};
11270elesfn$p.each = elesfn$p.forEach;
11271
11272var defineSymbolIterator = function defineSymbolIterator() {
11273 var typeofUndef = "undefined" ;
11274 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11275
11276 if (isIteratorSupported) {
11277 elesfn$p[Symbol.iterator] = function () {
11278 var _this = this;
11279
11280 // eslint-disable-line no-undef
11281 var entry = {
11282 value: undefined,
11283 done: false
11284 };
11285 var i = 0;
11286 var length = this.length;
11287 return _defineProperty({
11288 next: function next() {
11289 if (i < length) {
11290 entry.value = _this[i++];
11291 } else {
11292 entry.value = undefined;
11293 entry.done = true;
11294 }
11295
11296 return entry;
11297 }
11298 }, Symbol.iterator, function () {
11299 // eslint-disable-line no-undef
11300 return this;
11301 });
11302 };
11303 }
11304};
11305
11306defineSymbolIterator();
11307
11308var getLayoutDimensionOptions = defaults({
11309 nodeDimensionsIncludeLabels: false
11310});
11311var elesfn$q = {
11312 // Calculates and returns node dimensions { x, y } based on options given
11313 layoutDimensions: function layoutDimensions(options) {
11314 options = getLayoutDimensionOptions(options);
11315 var dims;
11316
11317 if (!this.takesUpSpace()) {
11318 dims = {
11319 w: 0,
11320 h: 0
11321 };
11322 } else if (options.nodeDimensionsIncludeLabels) {
11323 var bbDim = this.boundingBox();
11324 dims = {
11325 w: bbDim.w,
11326 h: bbDim.h
11327 };
11328 } else {
11329 dims = {
11330 w: this.outerWidth(),
11331 h: this.outerHeight()
11332 };
11333 } // sanitise the dimensions for external layouts (avoid division by zero)
11334
11335
11336 if (dims.w === 0 || dims.h === 0) {
11337 dims.w = dims.h = 1;
11338 }
11339
11340 return dims;
11341 },
11342 // using standard layout options, apply position function (w/ or w/o animation)
11343 layoutPositions: function layoutPositions(layout, options, fn) {
11344 var nodes = this.nodes();
11345 var cy = this.cy();
11346 var layoutEles = options.eles; // nodes & edges
11347
11348 var getMemoizeKey = function getMemoizeKey(node) {
11349 return node.id();
11350 };
11351
11352 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11353
11354 layout.emit({
11355 type: 'layoutstart',
11356 layout: layout
11357 });
11358 layout.animations = [];
11359
11360 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11361 var center = {
11362 x: nodesBb.x1 + nodesBb.w / 2,
11363 y: nodesBb.y1 + nodesBb.h / 2
11364 };
11365 var spacingVector = {
11366 // scale from center of bounding box (not necessarily 0,0)
11367 x: (pos.x - center.x) * spacing,
11368 y: (pos.y - center.y) * spacing
11369 };
11370 return {
11371 x: center.x + spacingVector.x,
11372 y: center.y + spacingVector.y
11373 };
11374 };
11375
11376 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11377
11378 var spacingBb = function spacingBb() {
11379 if (!useSpacingFactor) {
11380 return null;
11381 }
11382
11383 var bb = makeBoundingBox();
11384
11385 for (var i = 0; i < nodes.length; i++) {
11386 var node = nodes[i];
11387 var pos = fnMem(node, i);
11388 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11389 }
11390
11391 return bb;
11392 };
11393
11394 var bb = spacingBb();
11395 var getFinalPos = memoize(function (node, i) {
11396 var newPos = fnMem(node, i);
11397
11398 if (useSpacingFactor) {
11399 var spacing = Math.abs(options.spacingFactor);
11400 newPos = calculateSpacing(spacing, bb, newPos);
11401 }
11402
11403 if (options.transform != null) {
11404 newPos = options.transform(node, newPos);
11405 }
11406
11407 return newPos;
11408 }, getMemoizeKey);
11409
11410 if (options.animate) {
11411 for (var i = 0; i < nodes.length; i++) {
11412 var node = nodes[i];
11413 var newPos = getFinalPos(node, i);
11414 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11415
11416 if (animateNode) {
11417 var ani = node.animation({
11418 position: newPos,
11419 duration: options.animationDuration,
11420 easing: options.animationEasing
11421 });
11422 layout.animations.push(ani);
11423 } else {
11424 node.position(newPos);
11425 }
11426 }
11427
11428 if (options.fit) {
11429 var fitAni = cy.animation({
11430 fit: {
11431 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11432 padding: options.padding
11433 },
11434 duration: options.animationDuration,
11435 easing: options.animationEasing
11436 });
11437 layout.animations.push(fitAni);
11438 } else if (options.zoom !== undefined && options.pan !== undefined) {
11439 var zoomPanAni = cy.animation({
11440 zoom: options.zoom,
11441 pan: options.pan,
11442 duration: options.animationDuration,
11443 easing: options.animationEasing
11444 });
11445 layout.animations.push(zoomPanAni);
11446 }
11447
11448 layout.animations.forEach(function (ani) {
11449 return ani.play();
11450 });
11451 layout.one('layoutready', options.ready);
11452 layout.emit({
11453 type: 'layoutready',
11454 layout: layout
11455 });
11456 Promise$1.all(layout.animations.map(function (ani) {
11457 return ani.promise();
11458 })).then(function () {
11459 layout.one('layoutstop', options.stop);
11460 layout.emit({
11461 type: 'layoutstop',
11462 layout: layout
11463 });
11464 });
11465 } else {
11466 nodes.positions(getFinalPos);
11467
11468 if (options.fit) {
11469 cy.fit(options.eles, options.padding);
11470 }
11471
11472 if (options.zoom != null) {
11473 cy.zoom(options.zoom);
11474 }
11475
11476 if (options.pan) {
11477 cy.pan(options.pan);
11478 }
11479
11480 layout.one('layoutready', options.ready);
11481 layout.emit({
11482 type: 'layoutready',
11483 layout: layout
11484 });
11485 layout.one('layoutstop', options.stop);
11486 layout.emit({
11487 type: 'layoutstop',
11488 layout: layout
11489 });
11490 }
11491
11492 return this; // chaining
11493 },
11494 layout: function layout(options) {
11495 var cy = this.cy();
11496 return cy.makeLayout(extend({}, options, {
11497 eles: this
11498 }));
11499 }
11500}; // aliases:
11501
11502elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11503
11504function styleCache(key, fn, ele) {
11505 var _p = ele._private;
11506 var cache = _p.styleCache = _p.styleCache || [];
11507 var val;
11508
11509 if ((val = cache[key]) != null) {
11510 return val;
11511 } else {
11512 val = cache[key] = fn(ele);
11513 return val;
11514 }
11515}
11516
11517function cacheStyleFunction(key, fn) {
11518 key = hashString(key);
11519 return function cachedStyleFunction(ele) {
11520 return styleCache(key, fn, ele);
11521 };
11522}
11523
11524function cachePrototypeStyleFunction(key, fn) {
11525 key = hashString(key);
11526
11527 var selfFn = function selfFn(ele) {
11528 return fn.call(ele);
11529 };
11530
11531 return function cachedPrototypeStyleFunction() {
11532 var ele = this[0];
11533
11534 if (ele) {
11535 return styleCache(key, selfFn, ele);
11536 }
11537 };
11538}
11539
11540var elesfn$r = {
11541 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11542 var cy = this.cy();
11543 var renderer = cy.renderer();
11544 var styleEnabled = cy.styleEnabled();
11545
11546 if (renderer && styleEnabled) {
11547 renderer.recalculateRenderedStyle(this, useCache);
11548 }
11549
11550 return this;
11551 },
11552 dirtyStyleCache: function dirtyStyleCache() {
11553 var cy = this.cy();
11554
11555 var dirty = function dirty(ele) {
11556 return ele._private.styleCache = null;
11557 };
11558
11559 if (cy.hasCompoundNodes()) {
11560 var eles;
11561 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11562 eles.merge(eles.connectedEdges());
11563 eles.forEach(dirty);
11564 } else {
11565 this.forEach(function (ele) {
11566 dirty(ele);
11567 ele.connectedEdges().forEach(dirty);
11568 });
11569 }
11570
11571 return this;
11572 },
11573 // fully updates (recalculates) the style for the elements
11574 updateStyle: function updateStyle(notifyRenderer) {
11575 var cy = this._private.cy;
11576
11577 if (!cy.styleEnabled()) {
11578 return this;
11579 }
11580
11581 if (cy.batching()) {
11582 var bEles = cy._private.batchStyleEles;
11583 bEles.merge(this);
11584 return this; // chaining and exit early when batching
11585 }
11586
11587 var hasCompounds = cy.hasCompoundNodes();
11588 var updatedEles = this;
11589 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11590
11591 if (hasCompounds) {
11592 // then add everything up and down for compound selector checks
11593 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11594 } // let changedEles = style.apply( updatedEles );
11595
11596
11597 var changedEles = updatedEles;
11598
11599 if (notifyRenderer) {
11600 changedEles.emitAndNotify('style'); // let renderer know we changed style
11601 } else {
11602 changedEles.emit('style'); // just fire the event
11603 }
11604
11605 updatedEles.forEach(function (ele) {
11606 return ele._private.styleDirty = true;
11607 });
11608 return this; // chaining
11609 },
11610 // get the internal parsed style object for the specified property
11611 parsedStyle: function parsedStyle(property) {
11612 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11613 var ele = this[0];
11614 var cy = ele.cy();
11615
11616 if (!cy.styleEnabled()) {
11617 return;
11618 }
11619
11620 if (ele) {
11621 if (ele._private.styleDirty) {
11622 ele._private.styleDirty = false;
11623 cy.style().apply(ele);
11624 ele.emitAndNotify('style');
11625 }
11626
11627 var overriddenStyle = ele._private.style[property];
11628
11629 if (overriddenStyle != null) {
11630 return overriddenStyle;
11631 } else if (includeNonDefault) {
11632 return cy.style().getDefaultProperty(property);
11633 } else {
11634 return null;
11635 }
11636 }
11637 },
11638 numericStyle: function numericStyle(property) {
11639 var ele = this[0];
11640
11641 if (!ele.cy().styleEnabled()) {
11642 return;
11643 }
11644
11645 if (ele) {
11646 var pstyle = ele.pstyle(property);
11647 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11648 }
11649 },
11650 numericStyleUnits: function numericStyleUnits(property) {
11651 var ele = this[0];
11652
11653 if (!ele.cy().styleEnabled()) {
11654 return;
11655 }
11656
11657 if (ele) {
11658 return ele.pstyle(property).units;
11659 }
11660 },
11661 // get the specified css property as a rendered value (i.e. on-screen value)
11662 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11663 renderedStyle: function renderedStyle(property) {
11664 var cy = this.cy();
11665
11666 if (!cy.styleEnabled()) {
11667 return this;
11668 }
11669
11670 var ele = this[0];
11671
11672 if (ele) {
11673 return cy.style().getRenderedStyle(ele, property);
11674 }
11675 },
11676 // read the calculated css style of the element or override the style (via a bypass)
11677 style: function style(name, value) {
11678 var cy = this.cy();
11679
11680 if (!cy.styleEnabled()) {
11681 return this;
11682 }
11683
11684 var updateTransitions = false;
11685 var style = cy.style();
11686
11687 if (plainObject(name)) {
11688 // then extend the bypass
11689 var props = name;
11690 style.applyBypass(this, props, updateTransitions);
11691 this.emitAndNotify('style'); // let the renderer know we've updated style
11692 } else if (string(name)) {
11693 if (value === undefined) {
11694 // then get the property from the style
11695 var ele = this[0];
11696
11697 if (ele) {
11698 return style.getStylePropertyValue(ele, name);
11699 } else {
11700 // empty collection => can't get any value
11701 return;
11702 }
11703 } else {
11704 // then set the bypass with the property value
11705 style.applyBypass(this, name, value, updateTransitions);
11706 this.emitAndNotify('style'); // let the renderer know we've updated style
11707 }
11708 } else if (name === undefined) {
11709 var _ele = this[0];
11710
11711 if (_ele) {
11712 return style.getRawStyle(_ele);
11713 } else {
11714 // empty collection => can't get any value
11715 return;
11716 }
11717 }
11718
11719 return this; // chaining
11720 },
11721 removeStyle: function removeStyle(names) {
11722 var cy = this.cy();
11723
11724 if (!cy.styleEnabled()) {
11725 return this;
11726 }
11727
11728 var updateTransitions = false;
11729 var style = cy.style();
11730 var eles = this;
11731
11732 if (names === undefined) {
11733 for (var i = 0; i < eles.length; i++) {
11734 var ele = eles[i];
11735 style.removeAllBypasses(ele, updateTransitions);
11736 }
11737 } else {
11738 names = names.split(/\s+/);
11739
11740 for (var _i = 0; _i < eles.length; _i++) {
11741 var _ele2 = eles[_i];
11742 style.removeBypasses(_ele2, names, updateTransitions);
11743 }
11744 }
11745
11746 this.emitAndNotify('style'); // let the renderer know we've updated style
11747
11748 return this; // chaining
11749 },
11750 show: function show() {
11751 this.css('display', 'element');
11752 return this; // chaining
11753 },
11754 hide: function hide() {
11755 this.css('display', 'none');
11756 return this; // chaining
11757 },
11758 effectiveOpacity: function effectiveOpacity() {
11759 var cy = this.cy();
11760
11761 if (!cy.styleEnabled()) {
11762 return 1;
11763 }
11764
11765 var hasCompoundNodes = cy.hasCompoundNodes();
11766 var ele = this[0];
11767
11768 if (ele) {
11769 var _p = ele._private;
11770 var parentOpacity = ele.pstyle('opacity').value;
11771
11772 if (!hasCompoundNodes) {
11773 return parentOpacity;
11774 }
11775
11776 var parents = !_p.data.parent ? null : ele.parents();
11777
11778 if (parents) {
11779 for (var i = 0; i < parents.length; i++) {
11780 var parent = parents[i];
11781 var opacity = parent.pstyle('opacity').value;
11782 parentOpacity = opacity * parentOpacity;
11783 }
11784 }
11785
11786 return parentOpacity;
11787 }
11788 },
11789 transparent: function transparent() {
11790 var cy = this.cy();
11791
11792 if (!cy.styleEnabled()) {
11793 return false;
11794 }
11795
11796 var ele = this[0];
11797 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11798
11799 if (ele) {
11800 if (!hasCompoundNodes) {
11801 return ele.pstyle('opacity').value === 0;
11802 } else {
11803 return ele.effectiveOpacity() === 0;
11804 }
11805 }
11806 },
11807 backgrounding: function backgrounding() {
11808 var cy = this.cy();
11809
11810 if (!cy.styleEnabled()) {
11811 return false;
11812 }
11813
11814 var ele = this[0];
11815 return ele._private.backgrounding ? true : false;
11816 }
11817};
11818
11819function checkCompound(ele, parentOk) {
11820 var _p = ele._private;
11821 var parents = _p.data.parent ? ele.parents() : null;
11822
11823 if (parents) {
11824 for (var i = 0; i < parents.length; i++) {
11825 var parent = parents[i];
11826
11827 if (!parentOk(parent)) {
11828 return false;
11829 }
11830 }
11831 }
11832
11833 return true;
11834}
11835
11836function defineDerivedStateFunction(specs) {
11837 var ok = specs.ok;
11838 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11839 var parentOk = specs.parentOk || specs.ok;
11840 return function () {
11841 var cy = this.cy();
11842
11843 if (!cy.styleEnabled()) {
11844 return true;
11845 }
11846
11847 var ele = this[0];
11848 var hasCompoundNodes = cy.hasCompoundNodes();
11849
11850 if (ele) {
11851 var _p = ele._private;
11852
11853 if (!ok(ele)) {
11854 return false;
11855 }
11856
11857 if (ele.isNode()) {
11858 return !hasCompoundNodes || checkCompound(ele, parentOk);
11859 } else {
11860 var src = _p.source;
11861 var tgt = _p.target;
11862 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11863 }
11864 }
11865 };
11866}
11867
11868var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11869 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11870});
11871elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11872 ok: eleTakesUpSpace
11873}));
11874var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11875 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11876});
11877var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11878 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11879});
11880elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11881 ok: eleInteractive,
11882 parentOk: parentInteractive,
11883 edgeOkViaNode: eleTakesUpSpace
11884}));
11885
11886elesfn$r.noninteractive = function () {
11887 var ele = this[0];
11888
11889 if (ele) {
11890 return !ele.interactive();
11891 }
11892};
11893
11894var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11895 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11896});
11897var edgeVisibleViaNode = eleTakesUpSpace;
11898elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11899 ok: eleVisible,
11900 edgeOkViaNode: edgeVisibleViaNode
11901}));
11902
11903elesfn$r.hidden = function () {
11904 var ele = this[0];
11905
11906 if (ele) {
11907 return !ele.visible();
11908 }
11909};
11910
11911elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11912 if (!this.cy().styleEnabled()) {
11913 return false;
11914 }
11915
11916 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11917});
11918elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11919elesfn$r.renderedCss = elesfn$r.renderedStyle;
11920elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11921elesfn$r.pstyle = elesfn$r.parsedStyle;
11922
11923var elesfn$s = {};
11924
11925function defineSwitchFunction(params) {
11926 return function () {
11927 var args = arguments;
11928 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11929
11930 if (args.length === 2) {
11931 var data = args[0];
11932 var handler = args[1];
11933 this.on(params.event, data, handler);
11934 } // e.g. cy.nodes().select( handler )
11935 else if (args.length === 1 && fn(args[0])) {
11936 var _handler = args[0];
11937 this.on(params.event, _handler);
11938 } // e.g. cy.nodes().select()
11939 // e.g. (private) cy.nodes().select(['tapselect'])
11940 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11941 var addlEvents = args.length === 1 ? args[0] : null;
11942
11943 for (var i = 0; i < this.length; i++) {
11944 var ele = this[i];
11945 var able = !params.ableField || ele._private[params.ableField];
11946 var changed = ele._private[params.field] != params.value;
11947
11948 if (params.overrideAble) {
11949 var overrideAble = params.overrideAble(ele);
11950
11951 if (overrideAble !== undefined) {
11952 able = overrideAble;
11953
11954 if (!overrideAble) {
11955 return this;
11956 } // to save cycles assume not able for all on override
11957
11958 }
11959 }
11960
11961 if (able) {
11962 ele._private[params.field] = params.value;
11963
11964 if (changed) {
11965 changedEles.push(ele);
11966 }
11967 }
11968 }
11969
11970 var changedColl = this.spawn(changedEles);
11971 changedColl.updateStyle(); // change of state => possible change of style
11972
11973 changedColl.emit(params.event);
11974
11975 if (addlEvents) {
11976 changedColl.emit(addlEvents);
11977 }
11978 }
11979
11980 return this;
11981 };
11982}
11983
11984function defineSwitchSet(params) {
11985 elesfn$s[params.field] = function () {
11986 var ele = this[0];
11987
11988 if (ele) {
11989 if (params.overrideField) {
11990 var val = params.overrideField(ele);
11991
11992 if (val !== undefined) {
11993 return val;
11994 }
11995 }
11996
11997 return ele._private[params.field];
11998 }
11999 };
12000
12001 elesfn$s[params.on] = defineSwitchFunction({
12002 event: params.on,
12003 field: params.field,
12004 ableField: params.ableField,
12005 overrideAble: params.overrideAble,
12006 value: true
12007 });
12008 elesfn$s[params.off] = defineSwitchFunction({
12009 event: params.off,
12010 field: params.field,
12011 ableField: params.ableField,
12012 overrideAble: params.overrideAble,
12013 value: false
12014 });
12015}
12016
12017defineSwitchSet({
12018 field: 'locked',
12019 overrideField: function overrideField(ele) {
12020 return ele.cy().autolock() ? true : undefined;
12021 },
12022 on: 'lock',
12023 off: 'unlock'
12024});
12025defineSwitchSet({
12026 field: 'grabbable',
12027 overrideField: function overrideField(ele) {
12028 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12029 },
12030 on: 'grabify',
12031 off: 'ungrabify'
12032});
12033defineSwitchSet({
12034 field: 'selected',
12035 ableField: 'selectable',
12036 overrideAble: function overrideAble(ele) {
12037 return ele.cy().autounselectify() ? false : undefined;
12038 },
12039 on: 'select',
12040 off: 'unselect'
12041});
12042defineSwitchSet({
12043 field: 'selectable',
12044 overrideField: function overrideField(ele) {
12045 return ele.cy().autounselectify() ? false : undefined;
12046 },
12047 on: 'selectify',
12048 off: 'unselectify'
12049});
12050elesfn$s.deselect = elesfn$s.unselect;
12051
12052elesfn$s.grabbed = function () {
12053 var ele = this[0];
12054
12055 if (ele) {
12056 return ele._private.grabbed;
12057 }
12058};
12059
12060defineSwitchSet({
12061 field: 'active',
12062 on: 'activate',
12063 off: 'unactivate'
12064});
12065defineSwitchSet({
12066 field: 'pannable',
12067 on: 'panify',
12068 off: 'unpanify'
12069});
12070
12071elesfn$s.inactive = function () {
12072 var ele = this[0];
12073
12074 if (ele) {
12075 return !ele._private.active;
12076 }
12077};
12078
12079var elesfn$t = {}; // DAG functions
12080////////////////
12081
12082var defineDagExtremity = function defineDagExtremity(params) {
12083 return function dagExtremityImpl(selector) {
12084 var eles = this;
12085 var ret = [];
12086
12087 for (var i = 0; i < eles.length; i++) {
12088 var ele = eles[i];
12089
12090 if (!ele.isNode()) {
12091 continue;
12092 }
12093
12094 var disqualified = false;
12095 var edges = ele.connectedEdges();
12096
12097 for (var j = 0; j < edges.length; j++) {
12098 var edge = edges[j];
12099 var src = edge.source();
12100 var tgt = edge.target();
12101
12102 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12103 disqualified = true;
12104 break;
12105 }
12106 }
12107
12108 if (!disqualified) {
12109 ret.push(ele);
12110 }
12111 }
12112
12113 return this.spawn(ret, true).filter(selector);
12114 };
12115};
12116
12117var defineDagOneHop = function defineDagOneHop(params) {
12118 return function (selector) {
12119 var eles = this;
12120 var oEles = [];
12121
12122 for (var i = 0; i < eles.length; i++) {
12123 var ele = eles[i];
12124
12125 if (!ele.isNode()) {
12126 continue;
12127 }
12128
12129 var edges = ele.connectedEdges();
12130
12131 for (var j = 0; j < edges.length; j++) {
12132 var edge = edges[j];
12133 var src = edge.source();
12134 var tgt = edge.target();
12135
12136 if (params.outgoing && src === ele) {
12137 oEles.push(edge);
12138 oEles.push(tgt);
12139 } else if (params.incoming && tgt === ele) {
12140 oEles.push(edge);
12141 oEles.push(src);
12142 }
12143 }
12144 }
12145
12146 return this.spawn(oEles, true).filter(selector);
12147 };
12148};
12149
12150var defineDagAllHops = function defineDagAllHops(params) {
12151 return function (selector) {
12152 var eles = this;
12153 var sEles = [];
12154 var sElesIds = {};
12155
12156 for (;;) {
12157 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12158
12159 if (next.length === 0) {
12160 break;
12161 } // done if none left
12162
12163
12164 var newNext = false;
12165
12166 for (var i = 0; i < next.length; i++) {
12167 var n = next[i];
12168 var nid = n.id();
12169
12170 if (!sElesIds[nid]) {
12171 sElesIds[nid] = true;
12172 sEles.push(n);
12173 newNext = true;
12174 }
12175 }
12176
12177 if (!newNext) {
12178 break;
12179 } // done if touched all outgoers already
12180
12181
12182 eles = next;
12183 }
12184
12185 return this.spawn(sEles, true).filter(selector);
12186 };
12187};
12188
12189elesfn$t.clearTraversalCache = function () {
12190 for (var i = 0; i < this.length; i++) {
12191 this[i]._private.traversalCache = null;
12192 }
12193};
12194
12195extend(elesfn$t, {
12196 // get the root nodes in the DAG
12197 roots: defineDagExtremity({
12198 noIncomingEdges: true
12199 }),
12200 // get the leaf nodes in the DAG
12201 leaves: defineDagExtremity({
12202 noOutgoingEdges: true
12203 }),
12204 // normally called children in graph theory
12205 // these nodes =edges=> outgoing nodes
12206 outgoers: cache(defineDagOneHop({
12207 outgoing: true
12208 }), 'outgoers'),
12209 // aka DAG descendants
12210 successors: defineDagAllHops({
12211 outgoing: true
12212 }),
12213 // normally called parents in graph theory
12214 // these nodes <=edges= incoming nodes
12215 incomers: cache(defineDagOneHop({
12216 incoming: true
12217 }), 'incomers'),
12218 // aka DAG ancestors
12219 predecessors: defineDagAllHops({
12220 incoming: true
12221 })
12222}); // Neighbourhood functions
12223//////////////////////////
12224
12225extend(elesfn$t, {
12226 neighborhood: cache(function (selector) {
12227 var elements = [];
12228 var nodes = this.nodes();
12229
12230 for (var i = 0; i < nodes.length; i++) {
12231 // for all nodes
12232 var node = nodes[i];
12233 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12234
12235 for (var j = 0; j < connectedEdges.length; j++) {
12236 var edge = connectedEdges[j];
12237 var src = edge.source();
12238 var tgt = edge.target();
12239 var otherNode = node === src ? tgt : src; // need check in case of loop
12240
12241 if (otherNode.length > 0) {
12242 elements.push(otherNode[0]); // add node 1 hop away
12243 } // add connected edge
12244
12245
12246 elements.push(edge[0]);
12247 }
12248 }
12249
12250 return this.spawn(elements, true).filter(selector);
12251 }, 'neighborhood'),
12252 closedNeighborhood: function closedNeighborhood(selector) {
12253 return this.neighborhood().add(this).filter(selector);
12254 },
12255 openNeighborhood: function openNeighborhood(selector) {
12256 return this.neighborhood(selector);
12257 }
12258}); // aliases
12259
12260elesfn$t.neighbourhood = elesfn$t.neighborhood;
12261elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12262elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12263/////////////////
12264
12265extend(elesfn$t, {
12266 source: cache(function sourceImpl(selector) {
12267 var ele = this[0];
12268 var src;
12269
12270 if (ele) {
12271 src = ele._private.source || ele.cy().collection();
12272 }
12273
12274 return src && selector ? src.filter(selector) : src;
12275 }, 'source'),
12276 target: cache(function targetImpl(selector) {
12277 var ele = this[0];
12278 var tgt;
12279
12280 if (ele) {
12281 tgt = ele._private.target || ele.cy().collection();
12282 }
12283
12284 return tgt && selector ? tgt.filter(selector) : tgt;
12285 }, 'target'),
12286 sources: defineSourceFunction({
12287 attr: 'source'
12288 }),
12289 targets: defineSourceFunction({
12290 attr: 'target'
12291 })
12292});
12293
12294function defineSourceFunction(params) {
12295 return function sourceImpl(selector) {
12296 var sources = [];
12297
12298 for (var i = 0; i < this.length; i++) {
12299 var ele = this[i];
12300 var src = ele._private[params.attr];
12301
12302 if (src) {
12303 sources.push(src);
12304 }
12305 }
12306
12307 return this.spawn(sources, true).filter(selector);
12308 };
12309}
12310
12311extend(elesfn$t, {
12312 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12313 edgesTo: cache(defineEdgesWithFunction({
12314 thisIsSrc: true
12315 }), 'edgesTo')
12316});
12317
12318function defineEdgesWithFunction(params) {
12319 return function edgesWithImpl(otherNodes) {
12320 var elements = [];
12321 var cy = this._private.cy;
12322 var p = params || {}; // get elements if a selector is specified
12323
12324 if (string(otherNodes)) {
12325 otherNodes = cy.$(otherNodes);
12326 }
12327
12328 for (var h = 0; h < otherNodes.length; h++) {
12329 var edges = otherNodes[h]._private.edges;
12330
12331 for (var i = 0; i < edges.length; i++) {
12332 var edge = edges[i];
12333 var edgeData = edge._private.data;
12334 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12335 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12336 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12337
12338 if (!edgeConnectsThisAndOther) {
12339 continue;
12340 }
12341
12342 if (p.thisIsSrc || p.thisIsTgt) {
12343 if (p.thisIsSrc && !thisToOther) {
12344 continue;
12345 }
12346
12347 if (p.thisIsTgt && !otherToThis) {
12348 continue;
12349 }
12350 }
12351
12352 elements.push(edge);
12353 }
12354 }
12355
12356 return this.spawn(elements, true);
12357 };
12358}
12359
12360extend(elesfn$t, {
12361 connectedEdges: cache(function (selector) {
12362 var retEles = [];
12363 var eles = this;
12364
12365 for (var i = 0; i < eles.length; i++) {
12366 var node = eles[i];
12367
12368 if (!node.isNode()) {
12369 continue;
12370 }
12371
12372 var edges = node._private.edges;
12373
12374 for (var j = 0; j < edges.length; j++) {
12375 var edge = edges[j];
12376 retEles.push(edge);
12377 }
12378 }
12379
12380 return this.spawn(retEles, true).filter(selector);
12381 }, 'connectedEdges'),
12382 connectedNodes: cache(function (selector) {
12383 var retEles = [];
12384 var eles = this;
12385
12386 for (var i = 0; i < eles.length; i++) {
12387 var edge = eles[i];
12388
12389 if (!edge.isEdge()) {
12390 continue;
12391 }
12392
12393 retEles.push(edge.source()[0]);
12394 retEles.push(edge.target()[0]);
12395 }
12396
12397 return this.spawn(retEles, true).filter(selector);
12398 }, 'connectedNodes'),
12399 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12400 codirectedEdges: cache(defineParallelEdgesFunction({
12401 codirected: true
12402 }), 'codirectedEdges')
12403});
12404
12405function defineParallelEdgesFunction(params) {
12406 var defaults = {
12407 codirected: false
12408 };
12409 params = extend({}, defaults, params);
12410 return function parallelEdgesImpl(selector) {
12411 // micro-optimised for renderer
12412 var elements = [];
12413 var edges = this.edges();
12414 var p = params; // look at all the edges in the collection
12415
12416 for (var i = 0; i < edges.length; i++) {
12417 var edge1 = edges[i];
12418 var edge1_p = edge1._private;
12419 var src1 = edge1_p.source;
12420 var srcid1 = src1._private.data.id;
12421 var tgtid1 = edge1_p.data.target;
12422 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12423
12424 for (var j = 0; j < srcEdges1.length; j++) {
12425 var edge2 = srcEdges1[j];
12426 var edge2data = edge2._private.data;
12427 var tgtid2 = edge2data.target;
12428 var srcid2 = edge2data.source;
12429 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12430 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12431
12432 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12433 elements.push(edge2);
12434 }
12435 }
12436 }
12437
12438 return this.spawn(elements, true).filter(selector);
12439 };
12440} // Misc functions
12441/////////////////
12442
12443
12444extend(elesfn$t, {
12445 components: function components(root) {
12446 var self = this;
12447 var cy = self.cy();
12448 var visited = cy.collection();
12449 var unvisited = root == null ? self.nodes() : root.nodes();
12450 var components = [];
12451
12452 if (root != null && unvisited.empty()) {
12453 // root may contain only edges
12454 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12455 }
12456
12457 var visitInComponent = function visitInComponent(node, component) {
12458 visited.merge(node);
12459 unvisited.unmerge(node);
12460 component.merge(node);
12461 };
12462
12463 if (unvisited.empty()) {
12464 return self.spawn();
12465 }
12466
12467 var _loop = function _loop() {
12468 // each iteration yields a component
12469 var cmpt = cy.collection();
12470 components.push(cmpt);
12471 var root = unvisited[0];
12472 visitInComponent(root, cmpt);
12473 self.bfs({
12474 directed: false,
12475 roots: root,
12476 visit: function visit(v) {
12477 return visitInComponent(v, cmpt);
12478 }
12479 });
12480 cmpt.forEach(function (node) {
12481 node.connectedEdges().forEach(function (e) {
12482 // connectedEdges() usually cached
12483 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12484 // has() is cheap
12485 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12486 }
12487 });
12488 });
12489 };
12490
12491 do {
12492 _loop();
12493 } while (unvisited.length > 0);
12494
12495 return components;
12496 },
12497 component: function component() {
12498 var ele = this[0];
12499 return ele.cy().mutableElements().components(ele)[0];
12500 }
12501});
12502elesfn$t.componentsOf = elesfn$t.components;
12503
12504var Collection = function Collection(cy, elements) {
12505 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12506
12507 if (cy === undefined) {
12508 error('A collection must have a reference to the core');
12509 return;
12510 }
12511
12512 var map = new Map$1();
12513 var createdElements = false;
12514
12515 if (!elements) {
12516 elements = [];
12517 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12518 createdElements = true; // make elements from json and restore all at once later
12519
12520 var eles = [];
12521 var elesIds = new Set$1();
12522
12523 for (var i = 0, l = elements.length; i < l; i++) {
12524 var json = elements[i];
12525
12526 if (json.data == null) {
12527 json.data = {};
12528 }
12529
12530 var _data = json.data; // make sure newly created elements have valid ids
12531
12532 if (_data.id == null) {
12533 _data.id = uuid();
12534 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12535 continue; // can't create element if prior id already exists
12536 }
12537
12538 var ele = new Element(cy, json, false);
12539 eles.push(ele);
12540 elesIds.add(_data.id);
12541 }
12542
12543 elements = eles;
12544 }
12545
12546 this.length = 0;
12547
12548 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12549 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12550
12551 if (element$1 == null) {
12552 continue;
12553 }
12554
12555 var id = element$1._private.data.id;
12556
12557 if (!unique || !map.has(id)) {
12558 if (unique) {
12559 map.set(id, {
12560 index: this.length,
12561 ele: element$1
12562 });
12563 }
12564
12565 this[this.length] = element$1;
12566 this.length++;
12567 }
12568 }
12569
12570 this._private = {
12571 eles: this,
12572 cy: cy,
12573
12574 get map() {
12575 if (this.lazyMap == null) {
12576 this.rebuildMap();
12577 }
12578
12579 return this.lazyMap;
12580 },
12581
12582 set map(m) {
12583 this.lazyMap = m;
12584 },
12585
12586 rebuildMap: function rebuildMap() {
12587 var m = this.lazyMap = new Map$1();
12588 var eles = this.eles;
12589
12590 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12591 var _ele = eles[_i2];
12592 m.set(_ele.id(), {
12593 index: _i2,
12594 ele: _ele
12595 });
12596 }
12597 }
12598 };
12599
12600 if (unique) {
12601 this._private.map = map;
12602 } // restore the elements if we created them from json
12603
12604
12605 if (createdElements) {
12606 this.restore();
12607 }
12608}; // Functions
12609////////////////////////////////////////////////////////////////////////////////////////////////////
12610// keep the prototypes in sync (an element has the same functions as a collection)
12611// and use elefn and elesfn as shorthands to the prototypes
12612
12613
12614var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12615
12616elesfn$u.instanceString = function () {
12617 return 'collection';
12618};
12619
12620elesfn$u.spawn = function (eles, unique) {
12621 return new Collection(this.cy(), eles, unique);
12622};
12623
12624elesfn$u.spawnSelf = function () {
12625 return this.spawn(this);
12626};
12627
12628elesfn$u.cy = function () {
12629 return this._private.cy;
12630};
12631
12632elesfn$u.renderer = function () {
12633 return this._private.cy.renderer();
12634};
12635
12636elesfn$u.element = function () {
12637 return this[0];
12638};
12639
12640elesfn$u.collection = function () {
12641 if (collection(this)) {
12642 return this;
12643 } else {
12644 // an element
12645 return new Collection(this._private.cy, [this]);
12646 }
12647};
12648
12649elesfn$u.unique = function () {
12650 return new Collection(this._private.cy, this, true);
12651};
12652
12653elesfn$u.hasElementWithId = function (id) {
12654 id = '' + id; // id must be string
12655
12656 return this._private.map.has(id);
12657};
12658
12659elesfn$u.getElementById = function (id) {
12660 id = '' + id; // id must be string
12661
12662 var cy = this._private.cy;
12663
12664 var entry = this._private.map.get(id);
12665
12666 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12667};
12668
12669elesfn$u.$id = elesfn$u.getElementById;
12670
12671elesfn$u.poolIndex = function () {
12672 var cy = this._private.cy;
12673 var eles = cy._private.elements;
12674 var id = this[0]._private.data.id;
12675 return eles._private.map.get(id).index;
12676};
12677
12678elesfn$u.indexOf = function (ele) {
12679 var id = ele[0]._private.data.id;
12680 return this._private.map.get(id).index;
12681};
12682
12683elesfn$u.indexOfId = function (id) {
12684 id = '' + id; // id must be string
12685
12686 return this._private.map.get(id).index;
12687};
12688
12689elesfn$u.json = function (obj) {
12690 var ele = this.element();
12691 var cy = this.cy();
12692
12693 if (ele == null && obj) {
12694 return this;
12695 } // can't set to no eles
12696
12697
12698 if (ele == null) {
12699 return undefined;
12700 } // can't get from no eles
12701
12702
12703 var p = ele._private;
12704
12705 if (plainObject(obj)) {
12706 // set
12707 cy.startBatch();
12708
12709 if (obj.data) {
12710 ele.data(obj.data);
12711 var _data2 = p.data;
12712
12713 if (ele.isEdge()) {
12714 // source and target are immutable via data()
12715 var move = false;
12716 var spec = {};
12717 var src = obj.data.source;
12718 var tgt = obj.data.target;
12719
12720 if (src != null && src != _data2.source) {
12721 spec.source = '' + src; // id must be string
12722
12723 move = true;
12724 }
12725
12726 if (tgt != null && tgt != _data2.target) {
12727 spec.target = '' + tgt; // id must be string
12728
12729 move = true;
12730 }
12731
12732 if (move) {
12733 ele = ele.move(spec);
12734 }
12735 } else {
12736 // parent is immutable via data()
12737 var newParentValSpecd = 'parent' in obj.data;
12738 var parent = obj.data.parent;
12739
12740 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12741 if (parent === undefined) {
12742 // can't set undefined imperatively, so use null
12743 parent = null;
12744 }
12745
12746 if (parent != null) {
12747 parent = '' + parent; // id must be string
12748 }
12749
12750 ele = ele.move({
12751 parent: parent
12752 });
12753 }
12754 }
12755 }
12756
12757 if (obj.position) {
12758 ele.position(obj.position);
12759 } // ignore group -- immutable
12760
12761
12762 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12763 var obj_k = obj[k];
12764
12765 if (obj_k != null && obj_k !== p[k]) {
12766 if (obj_k) {
12767 ele[trueFnName]();
12768 } else {
12769 ele[falseFnName]();
12770 }
12771 }
12772 };
12773
12774 checkSwitch('removed', 'remove', 'restore');
12775 checkSwitch('selected', 'select', 'unselect');
12776 checkSwitch('selectable', 'selectify', 'unselectify');
12777 checkSwitch('locked', 'lock', 'unlock');
12778 checkSwitch('grabbable', 'grabify', 'ungrabify');
12779 checkSwitch('pannable', 'panify', 'unpanify');
12780
12781 if (obj.classes != null) {
12782 ele.classes(obj.classes);
12783 }
12784
12785 cy.endBatch();
12786 return this;
12787 } else if (obj === undefined) {
12788 // get
12789 var json = {
12790 data: copy(p.data),
12791 position: copy(p.position),
12792 group: p.group,
12793 removed: p.removed,
12794 selected: p.selected,
12795 selectable: p.selectable,
12796 locked: p.locked,
12797 grabbable: p.grabbable,
12798 pannable: p.pannable,
12799 classes: null
12800 };
12801 json.classes = '';
12802 var i = 0;
12803 p.classes.forEach(function (cls) {
12804 return json.classes += i++ === 0 ? cls : ' ' + cls;
12805 });
12806 return json;
12807 }
12808};
12809
12810elesfn$u.jsons = function () {
12811 var jsons = [];
12812
12813 for (var i = 0; i < this.length; i++) {
12814 var ele = this[i];
12815 var json = ele.json();
12816 jsons.push(json);
12817 }
12818
12819 return jsons;
12820};
12821
12822elesfn$u.clone = function () {
12823 var cy = this.cy();
12824 var elesArr = [];
12825
12826 for (var i = 0; i < this.length; i++) {
12827 var ele = this[i];
12828 var json = ele.json();
12829 var clone = new Element(cy, json, false); // NB no restore
12830
12831 elesArr.push(clone);
12832 }
12833
12834 return new Collection(cy, elesArr);
12835};
12836
12837elesfn$u.copy = elesfn$u.clone;
12838
12839elesfn$u.restore = function () {
12840 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12841 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12842 var self = this;
12843 var cy = self.cy();
12844 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12845 // restore the nodes first
12846
12847 var nodes = [];
12848 var edges = [];
12849 var elements;
12850
12851 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12852 var ele = self[_i3];
12853
12854 if (addToPool && !ele.removed()) {
12855 // don't need to handle this ele
12856 continue;
12857 } // keep nodes first in the array and edges after
12858
12859
12860 if (ele.isNode()) {
12861 // put to front of array if node
12862 nodes.push(ele);
12863 } else {
12864 // put to end of array if edge
12865 edges.push(ele);
12866 }
12867 }
12868
12869 elements = nodes.concat(edges);
12870 var i;
12871
12872 var removeFromElements = function removeFromElements() {
12873 elements.splice(i, 1);
12874 i--;
12875 }; // now, restore each element
12876
12877
12878 for (i = 0; i < elements.length; i++) {
12879 var _ele2 = elements[i];
12880 var _private = _ele2._private;
12881 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12882
12883 _ele2.clearTraversalCache(); // set id and validate
12884
12885
12886 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12887 _data3.id = uuid();
12888 } else if (number(_data3.id)) {
12889 _data3.id = '' + _data3.id; // now it's a string
12890 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12891 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
12892
12893 removeFromElements();
12894 continue;
12895 } else if (cy.hasElementWithId(_data3.id)) {
12896 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12897
12898 removeFromElements();
12899 continue;
12900 }
12901
12902 var id = _data3.id; // id is finalised, now let's keep a ref
12903
12904 if (_ele2.isNode()) {
12905 // extra checks for nodes
12906 var pos = _private.position; // make sure the nodes have a defined position
12907
12908 if (pos.x == null) {
12909 pos.x = 0;
12910 }
12911
12912 if (pos.y == null) {
12913 pos.y = 0;
12914 }
12915 }
12916
12917 if (_ele2.isEdge()) {
12918 // extra checks for edges
12919 var edge = _ele2;
12920 var fields = ['source', 'target'];
12921 var fieldsLength = fields.length;
12922 var badSourceOrTarget = false;
12923
12924 for (var j = 0; j < fieldsLength; j++) {
12925 var field = fields[j];
12926 var val = _data3[field];
12927
12928 if (number(val)) {
12929 val = _data3[field] = '' + _data3[field]; // now string
12930 }
12931
12932 if (val == null || val === '') {
12933 // can't create if source or target is not defined properly
12934 error('Can not create edge `' + id + '` with unspecified ' + field);
12935 badSourceOrTarget = true;
12936 } else if (!cy.hasElementWithId(val)) {
12937 // can't create edge if one of its nodes doesn't exist
12938 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12939 badSourceOrTarget = true;
12940 }
12941 }
12942
12943 if (badSourceOrTarget) {
12944 removeFromElements();
12945 continue;
12946 } // can't create this
12947
12948
12949 var src = cy.getElementById(_data3.source);
12950 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
12951
12952 if (src.same(tgt)) {
12953 src._private.edges.push(edge);
12954 } else {
12955 src._private.edges.push(edge);
12956
12957 tgt._private.edges.push(edge);
12958 }
12959
12960 edge._private.source = src;
12961 edge._private.target = tgt;
12962 } // if is edge
12963 // create mock ids / indexes maps for element so it can be used like collections
12964
12965
12966 _private.map = new Map$1();
12967
12968 _private.map.set(id, {
12969 ele: _ele2,
12970 index: 0
12971 });
12972
12973 _private.removed = false;
12974
12975 if (addToPool) {
12976 cy.addToPool(_ele2);
12977 }
12978 } // for each element
12979 // do compound node sanity checks
12980
12981
12982 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
12983 // each node
12984 var node = nodes[_i4];
12985 var _data4 = node._private.data;
12986
12987 if (number(_data4.parent)) {
12988 // then automake string
12989 _data4.parent = '' + _data4.parent;
12990 }
12991
12992 var parentId = _data4.parent;
12993 var specifiedParent = parentId != null;
12994
12995 if (specifiedParent) {
12996 var parent = cy.getElementById(parentId);
12997
12998 if (parent.empty()) {
12999 // non-existant parent; just remove it
13000 _data4.parent = undefined;
13001 } else {
13002 var selfAsParent = false;
13003 var ancestor = parent;
13004
13005 while (!ancestor.empty()) {
13006 if (node.same(ancestor)) {
13007 // mark self as parent and remove from data
13008 selfAsParent = true;
13009 _data4.parent = undefined; // remove parent reference
13010 // exit or we loop forever
13011
13012 break;
13013 }
13014
13015 ancestor = ancestor.parent();
13016 }
13017
13018 if (!selfAsParent) {
13019 // connect with children
13020 parent[0]._private.children.push(node);
13021
13022 node._private.parent = parent[0]; // let the core know we have a compound graph
13023
13024 cy_p.hasCompoundNodes = true;
13025 }
13026 } // else
13027
13028 } // if specified parent
13029
13030 } // for each node
13031
13032
13033 if (elements.length > 0) {
13034 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13035
13036 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13037 var _ele3 = restored[_i5];
13038
13039 if (_ele3.isNode()) {
13040 continue;
13041 } // adding an edge invalidates the traversal caches for the parallel edges
13042
13043
13044 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13045
13046
13047 _ele3.source().clearTraversalCache();
13048
13049 _ele3.target().clearTraversalCache();
13050 }
13051
13052 var toUpdateStyle;
13053
13054 if (cy_p.hasCompoundNodes) {
13055 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13056 } else {
13057 toUpdateStyle = restored;
13058 }
13059
13060 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13061
13062 if (notifyRenderer) {
13063 restored.emitAndNotify('add');
13064 } else if (addToPool) {
13065 restored.emit('add');
13066 }
13067 }
13068
13069 return self; // chainability
13070};
13071
13072elesfn$u.removed = function () {
13073 var ele = this[0];
13074 return ele && ele._private.removed;
13075};
13076
13077elesfn$u.inside = function () {
13078 var ele = this[0];
13079 return ele && !ele._private.removed;
13080};
13081
13082elesfn$u.remove = function () {
13083 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13084 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13085 var self = this;
13086 var elesToRemove = [];
13087 var elesToRemoveIds = {};
13088 var cy = self._private.cy; // add connected edges
13089
13090 function addConnectedEdges(node) {
13091 var edges = node._private.edges;
13092
13093 for (var i = 0; i < edges.length; i++) {
13094 add(edges[i]);
13095 }
13096 } // add descendant nodes
13097
13098
13099 function addChildren(node) {
13100 var children = node._private.children;
13101
13102 for (var i = 0; i < children.length; i++) {
13103 add(children[i]);
13104 }
13105 }
13106
13107 function add(ele) {
13108 var alreadyAdded = elesToRemoveIds[ele.id()];
13109
13110 if (removeFromPool && ele.removed() || alreadyAdded) {
13111 return;
13112 } else {
13113 elesToRemoveIds[ele.id()] = true;
13114 }
13115
13116 if (ele.isNode()) {
13117 elesToRemove.push(ele); // nodes are removed last
13118
13119 addConnectedEdges(ele);
13120 addChildren(ele);
13121 } else {
13122 elesToRemove.unshift(ele); // edges are removed first
13123 }
13124 } // make the list of elements to remove
13125 // (may be removing more than specified due to connected edges etc)
13126
13127
13128 for (var i = 0, l = self.length; i < l; i++) {
13129 var ele = self[i];
13130 add(ele);
13131 }
13132
13133 function removeEdgeRef(node, edge) {
13134 var connectedEdges = node._private.edges;
13135 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13136
13137 node.clearTraversalCache();
13138 }
13139
13140 function removeParallelRef(pllEdge) {
13141 // removing an edge invalidates the traversal caches for the parallel edges
13142 pllEdge.clearTraversalCache();
13143 }
13144
13145 var alteredParents = [];
13146 alteredParents.ids = {};
13147
13148 function removeChildRef(parent, ele) {
13149 ele = ele[0];
13150 parent = parent[0];
13151 var children = parent._private.children;
13152 var pid = parent.id();
13153 removeFromArray(children, ele); // remove parent => child ref
13154
13155 ele._private.parent = null; // remove child => parent ref
13156
13157 if (!alteredParents.ids[pid]) {
13158 alteredParents.ids[pid] = true;
13159 alteredParents.push(parent);
13160 }
13161 }
13162
13163 self.dirtyCompoundBoundsCache();
13164
13165 if (removeFromPool) {
13166 cy.removeFromPool(elesToRemove); // remove from core pool
13167 }
13168
13169 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13170 var _ele4 = elesToRemove[_i6];
13171
13172 if (_ele4.isEdge()) {
13173 // remove references to this edge in its connected nodes
13174 var src = _ele4.source()[0];
13175
13176 var tgt = _ele4.target()[0];
13177
13178 removeEdgeRef(src, _ele4);
13179 removeEdgeRef(tgt, _ele4);
13180
13181 var pllEdges = _ele4.parallelEdges();
13182
13183 for (var j = 0; j < pllEdges.length; j++) {
13184 var pllEdge = pllEdges[j];
13185 removeParallelRef(pllEdge);
13186
13187 if (pllEdge.isBundledBezier()) {
13188 pllEdge.dirtyBoundingBoxCache();
13189 }
13190 }
13191 } else {
13192 // remove reference to parent
13193 var parent = _ele4.parent();
13194
13195 if (parent.length !== 0) {
13196 removeChildRef(parent, _ele4);
13197 }
13198 }
13199
13200 if (removeFromPool) {
13201 // mark as removed
13202 _ele4._private.removed = true;
13203 }
13204 } // check to see if we have a compound graph or not
13205
13206
13207 var elesStillInside = cy._private.elements;
13208 cy._private.hasCompoundNodes = false;
13209
13210 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13211 var _ele5 = elesStillInside[_i7];
13212
13213 if (_ele5.isParent()) {
13214 cy._private.hasCompoundNodes = true;
13215 break;
13216 }
13217 }
13218
13219 var removedElements = new Collection(this.cy(), elesToRemove);
13220
13221 if (removedElements.size() > 0) {
13222 // must manually notify since trigger won't do this automatically once removed
13223 if (notifyRenderer) {
13224 removedElements.emitAndNotify('remove');
13225 } else if (removeFromPool) {
13226 removedElements.emit('remove');
13227 }
13228 } // the parents who were modified by the removal need their style updated
13229
13230
13231 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13232 var _ele6 = alteredParents[_i8];
13233
13234 if (!removeFromPool || !_ele6.removed()) {
13235 _ele6.updateStyle();
13236 }
13237 }
13238
13239 return removedElements;
13240};
13241
13242elesfn$u.move = function (struct) {
13243 var cy = this._private.cy;
13244 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13245 // (our calls to remove/restore do not remove from the graph or make events)
13246
13247 var notifyRenderer = false;
13248 var modifyPool = false;
13249
13250 var toString = function toString(id) {
13251 return id == null ? id : '' + id;
13252 }; // id must be string
13253
13254
13255 if (struct.source !== undefined || struct.target !== undefined) {
13256 var srcId = toString(struct.source);
13257 var tgtId = toString(struct.target);
13258 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13259 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13260
13261 if (srcExists || tgtExists) {
13262 cy.batch(function () {
13263 // avoid duplicate style updates
13264 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13265
13266 eles.emitAndNotify('moveout');
13267
13268 for (var i = 0; i < eles.length; i++) {
13269 var ele = eles[i];
13270 var _data5 = ele._private.data;
13271
13272 if (ele.isEdge()) {
13273 if (srcExists) {
13274 _data5.source = srcId;
13275 }
13276
13277 if (tgtExists) {
13278 _data5.target = tgtId;
13279 }
13280 }
13281 }
13282
13283 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13284 });
13285 eles.emitAndNotify('move');
13286 }
13287 } else if (struct.parent !== undefined) {
13288 // move node to new parent
13289 var parentId = toString(struct.parent);
13290 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13291
13292 if (parentExists) {
13293 var pidToAssign = parentId === null ? undefined : parentId;
13294 cy.batch(function () {
13295 // avoid duplicate style updates
13296 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13297
13298 updated.emitAndNotify('moveout');
13299
13300 for (var i = 0; i < eles.length; i++) {
13301 var ele = eles[i];
13302 var _data6 = ele._private.data;
13303
13304 if (ele.isNode()) {
13305 _data6.parent = pidToAssign;
13306 }
13307 }
13308
13309 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13310 });
13311 eles.emitAndNotify('move');
13312 }
13313 }
13314
13315 return this;
13316};
13317
13318[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) {
13319 extend(elesfn$u, props);
13320});
13321
13322var corefn = {
13323 add: function add(opts) {
13324 var elements;
13325 var cy = this; // add the elements
13326
13327 if (elementOrCollection(opts)) {
13328 var eles = opts;
13329
13330 if (eles._private.cy === cy) {
13331 // same instance => just restore
13332 elements = eles.restore();
13333 } else {
13334 // otherwise, copy from json
13335 var jsons = [];
13336
13337 for (var i = 0; i < eles.length; i++) {
13338 var ele = eles[i];
13339 jsons.push(ele.json());
13340 }
13341
13342 elements = new Collection(cy, jsons);
13343 }
13344 } // specify an array of options
13345 else if (array(opts)) {
13346 var _jsons = opts;
13347 elements = new Collection(cy, _jsons);
13348 } // specify via opts.nodes and opts.edges
13349 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13350 var elesByGroup = opts;
13351 var _jsons2 = [];
13352 var grs = ['nodes', 'edges'];
13353
13354 for (var _i = 0, il = grs.length; _i < il; _i++) {
13355 var group = grs[_i];
13356 var elesArray = elesByGroup[group];
13357
13358 if (array(elesArray)) {
13359 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13360 var json = extend({
13361 group: group
13362 }, elesArray[j]);
13363
13364 _jsons2.push(json);
13365 }
13366 }
13367 }
13368
13369 elements = new Collection(cy, _jsons2);
13370 } // specify options for one element
13371 else {
13372 var _json = opts;
13373 elements = new Element(cy, _json).collection();
13374 }
13375
13376 return elements;
13377 },
13378 remove: function remove(collection) {
13379 if (elementOrCollection(collection)) ; else if (string(collection)) {
13380 var selector = collection;
13381 collection = this.$(selector);
13382 }
13383
13384 return collection.remove();
13385 }
13386};
13387
13388/* global Float32Array */
13389
13390/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13391function generateCubicBezier(mX1, mY1, mX2, mY2) {
13392 var NEWTON_ITERATIONS = 4,
13393 NEWTON_MIN_SLOPE = 0.001,
13394 SUBDIVISION_PRECISION = 0.0000001,
13395 SUBDIVISION_MAX_ITERATIONS = 10,
13396 kSplineTableSize = 11,
13397 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13398 float32ArraySupported = typeof Float32Array !== 'undefined';
13399 /* Must contain four arguments. */
13400
13401 if (arguments.length !== 4) {
13402 return false;
13403 }
13404 /* Arguments must be numbers. */
13405
13406
13407 for (var i = 0; i < 4; ++i) {
13408 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13409 return false;
13410 }
13411 }
13412 /* X values must be in the [0, 1] range. */
13413
13414
13415 mX1 = Math.min(mX1, 1);
13416 mX2 = Math.min(mX2, 1);
13417 mX1 = Math.max(mX1, 0);
13418 mX2 = Math.max(mX2, 0);
13419 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13420
13421 function A(aA1, aA2) {
13422 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13423 }
13424
13425 function B(aA1, aA2) {
13426 return 3.0 * aA2 - 6.0 * aA1;
13427 }
13428
13429 function C(aA1) {
13430 return 3.0 * aA1;
13431 }
13432
13433 function calcBezier(aT, aA1, aA2) {
13434 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13435 }
13436
13437 function getSlope(aT, aA1, aA2) {
13438 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13439 }
13440
13441 function newtonRaphsonIterate(aX, aGuessT) {
13442 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13443 var currentSlope = getSlope(aGuessT, mX1, mX2);
13444
13445 if (currentSlope === 0.0) {
13446 return aGuessT;
13447 }
13448
13449 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13450 aGuessT -= currentX / currentSlope;
13451 }
13452
13453 return aGuessT;
13454 }
13455
13456 function calcSampleValues() {
13457 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13458 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13459 }
13460 }
13461
13462 function binarySubdivide(aX, aA, aB) {
13463 var currentX,
13464 currentT,
13465 i = 0;
13466
13467 do {
13468 currentT = aA + (aB - aA) / 2.0;
13469 currentX = calcBezier(currentT, mX1, mX2) - aX;
13470
13471 if (currentX > 0.0) {
13472 aB = currentT;
13473 } else {
13474 aA = currentT;
13475 }
13476 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13477
13478 return currentT;
13479 }
13480
13481 function getTForX(aX) {
13482 var intervalStart = 0.0,
13483 currentSample = 1,
13484 lastSample = kSplineTableSize - 1;
13485
13486 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13487 intervalStart += kSampleStepSize;
13488 }
13489
13490 --currentSample;
13491 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13492 guessForT = intervalStart + dist * kSampleStepSize,
13493 initialSlope = getSlope(guessForT, mX1, mX2);
13494
13495 if (initialSlope >= NEWTON_MIN_SLOPE) {
13496 return newtonRaphsonIterate(aX, guessForT);
13497 } else if (initialSlope === 0.0) {
13498 return guessForT;
13499 } else {
13500 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13501 }
13502 }
13503
13504 var _precomputed = false;
13505
13506 function precompute() {
13507 _precomputed = true;
13508
13509 if (mX1 !== mY1 || mX2 !== mY2) {
13510 calcSampleValues();
13511 }
13512 }
13513
13514 var f = function f(aX) {
13515 if (!_precomputed) {
13516 precompute();
13517 }
13518
13519 if (mX1 === mY1 && mX2 === mY2) {
13520 return aX;
13521 }
13522
13523 if (aX === 0) {
13524 return 0;
13525 }
13526
13527 if (aX === 1) {
13528 return 1;
13529 }
13530
13531 return calcBezier(getTForX(aX), mY1, mY2);
13532 };
13533
13534 f.getControlPoints = function () {
13535 return [{
13536 x: mX1,
13537 y: mY1
13538 }, {
13539 x: mX2,
13540 y: mY2
13541 }];
13542 };
13543
13544 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13545
13546 f.toString = function () {
13547 return str;
13548 };
13549
13550 return f;
13551}
13552
13553/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13554
13555/* 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
13556 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13557var generateSpringRK4 = function () {
13558 function springAccelerationForState(state) {
13559 return -state.tension * state.x - state.friction * state.v;
13560 }
13561
13562 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13563 var state = {
13564 x: initialState.x + derivative.dx * dt,
13565 v: initialState.v + derivative.dv * dt,
13566 tension: initialState.tension,
13567 friction: initialState.friction
13568 };
13569 return {
13570 dx: state.v,
13571 dv: springAccelerationForState(state)
13572 };
13573 }
13574
13575 function springIntegrateState(state, dt) {
13576 var a = {
13577 dx: state.v,
13578 dv: springAccelerationForState(state)
13579 },
13580 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13581 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13582 d = springEvaluateStateWithDerivative(state, dt, c),
13583 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13584 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13585 state.x = state.x + dxdt * dt;
13586 state.v = state.v + dvdt * dt;
13587 return state;
13588 }
13589
13590 return function springRK4Factory(tension, friction, duration) {
13591 var initState = {
13592 x: -1,
13593 v: 0,
13594 tension: null,
13595 friction: null
13596 },
13597 path = [0],
13598 time_lapsed = 0,
13599 tolerance = 1 / 10000,
13600 DT = 16 / 1000,
13601 have_duration,
13602 dt,
13603 last_state;
13604 tension = parseFloat(tension) || 500;
13605 friction = parseFloat(friction) || 20;
13606 duration = duration || null;
13607 initState.tension = tension;
13608 initState.friction = friction;
13609 have_duration = duration !== null;
13610 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13611
13612 if (have_duration) {
13613 /* Run the simulation without a duration. */
13614 time_lapsed = springRK4Factory(tension, friction);
13615 /* Compute the adjusted time delta. */
13616
13617 dt = time_lapsed / duration * DT;
13618 } else {
13619 dt = DT;
13620 }
13621
13622 for (;;) {
13623 /* Next/step function .*/
13624 last_state = springIntegrateState(last_state || initState, dt);
13625 /* Store the position. */
13626
13627 path.push(1 + last_state.x);
13628 time_lapsed += 16;
13629 /* If the change threshold is reached, break. */
13630
13631 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13632 break;
13633 }
13634 }
13635 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13636 computed path and returns a snapshot of the position according to a given percentComplete. */
13637
13638
13639 return !have_duration ? time_lapsed : function (percentComplete) {
13640 return path[percentComplete * (path.length - 1) | 0];
13641 };
13642 };
13643}();
13644
13645var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13646 var bezier = generateCubicBezier(t1, p1, t2, p2);
13647 return function (start, end, percent) {
13648 return start + (end - start) * bezier(percent);
13649 };
13650};
13651
13652var easings = {
13653 'linear': function linear(start, end, percent) {
13654 return start + (end - start) * percent;
13655 },
13656 // default easings
13657 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13658 'ease-in': cubicBezier(0.42, 0, 1, 1),
13659 'ease-out': cubicBezier(0, 0, 0.58, 1),
13660 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13661 // sine
13662 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13663 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13664 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13665 // quad
13666 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13667 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13668 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13669 // cubic
13670 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13671 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13672 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13673 // quart
13674 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13675 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13676 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13677 // quint
13678 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13679 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13680 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13681 // expo
13682 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13683 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13684 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13685 // circ
13686 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13687 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13688 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13689 // user param easings...
13690 'spring': function spring(tension, friction, duration) {
13691 if (duration === 0) {
13692 // can't get a spring w/ duration 0
13693 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13694 }
13695
13696 var spring = generateSpringRK4(tension, friction, duration);
13697 return function (start, end, percent) {
13698 return start + (end - start) * spring(percent);
13699 };
13700 },
13701 'cubic-bezier': cubicBezier
13702};
13703
13704function getEasedValue(type, start, end, percent, easingFn) {
13705 if (percent === 1) {
13706 return end;
13707 }
13708
13709 if (start === end) {
13710 return end;
13711 }
13712
13713 var val = easingFn(start, end, percent);
13714
13715 if (type == null) {
13716 return val;
13717 }
13718
13719 if (type.roundValue || type.color) {
13720 val = Math.round(val);
13721 }
13722
13723 if (type.min !== undefined) {
13724 val = Math.max(val, type.min);
13725 }
13726
13727 if (type.max !== undefined) {
13728 val = Math.min(val, type.max);
13729 }
13730
13731 return val;
13732}
13733
13734function getValue(prop, spec) {
13735 if (prop.pfValue != null || prop.value != null) {
13736 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13737 return prop.pfValue;
13738 } else {
13739 return prop.value;
13740 }
13741 } else {
13742 return prop;
13743 }
13744}
13745
13746function ease(startProp, endProp, percent, easingFn, propSpec) {
13747 var type = propSpec != null ? propSpec.type : null;
13748
13749 if (percent < 0) {
13750 percent = 0;
13751 } else if (percent > 1) {
13752 percent = 1;
13753 }
13754
13755 var start = getValue(startProp, propSpec);
13756 var end = getValue(endProp, propSpec);
13757
13758 if (number(start) && number(end)) {
13759 return getEasedValue(type, start, end, percent, easingFn);
13760 } else if (array(start) && array(end)) {
13761 var easedArr = [];
13762
13763 for (var i = 0; i < end.length; i++) {
13764 var si = start[i];
13765 var ei = end[i];
13766
13767 if (si != null && ei != null) {
13768 var val = getEasedValue(type, si, ei, percent, easingFn);
13769 easedArr.push(val);
13770 } else {
13771 easedArr.push(ei);
13772 }
13773 }
13774
13775 return easedArr;
13776 }
13777
13778 return undefined;
13779}
13780
13781function step(self, ani, now, isCore) {
13782 var isEles = !isCore;
13783 var _p = self._private;
13784 var ani_p = ani._private;
13785 var pEasing = ani_p.easing;
13786 var startTime = ani_p.startTime;
13787 var cy = isCore ? self : self.cy();
13788 var style = cy.style();
13789
13790 if (!ani_p.easingImpl) {
13791 if (pEasing == null) {
13792 // use default
13793 ani_p.easingImpl = easings['linear'];
13794 } else {
13795 // then define w/ name
13796 var easingVals;
13797
13798 if (string(pEasing)) {
13799 var easingProp = style.parse('transition-timing-function', pEasing);
13800 easingVals = easingProp.value;
13801 } else {
13802 // then assume preparsed array
13803 easingVals = pEasing;
13804 }
13805
13806 var name, args;
13807
13808 if (string(easingVals)) {
13809 name = easingVals;
13810 args = [];
13811 } else {
13812 name = easingVals[1];
13813 args = easingVals.slice(2).map(function (n) {
13814 return +n;
13815 });
13816 }
13817
13818 if (args.length > 0) {
13819 // create with args
13820 if (name === 'spring') {
13821 args.push(ani_p.duration); // need duration to generate spring
13822 }
13823
13824 ani_p.easingImpl = easings[name].apply(null, args);
13825 } else {
13826 // static impl by name
13827 ani_p.easingImpl = easings[name];
13828 }
13829 }
13830 }
13831
13832 var easing = ani_p.easingImpl;
13833 var percent;
13834
13835 if (ani_p.duration === 0) {
13836 percent = 1;
13837 } else {
13838 percent = (now - startTime) / ani_p.duration;
13839 }
13840
13841 if (ani_p.applying) {
13842 percent = ani_p.progress;
13843 }
13844
13845 if (percent < 0) {
13846 percent = 0;
13847 } else if (percent > 1) {
13848 percent = 1;
13849 }
13850
13851 if (ani_p.delay == null) {
13852 // then update
13853 var startPos = ani_p.startPosition;
13854 var endPos = ani_p.position;
13855
13856 if (endPos && isEles && !self.locked()) {
13857 var newPos = {};
13858
13859 if (valid(startPos.x, endPos.x)) {
13860 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13861 }
13862
13863 if (valid(startPos.y, endPos.y)) {
13864 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13865 }
13866
13867 self.position(newPos);
13868 }
13869
13870 var startPan = ani_p.startPan;
13871 var endPan = ani_p.pan;
13872 var pan = _p.pan;
13873 var animatingPan = endPan != null && isCore;
13874
13875 if (animatingPan) {
13876 if (valid(startPan.x, endPan.x)) {
13877 pan.x = ease(startPan.x, endPan.x, percent, easing);
13878 }
13879
13880 if (valid(startPan.y, endPan.y)) {
13881 pan.y = ease(startPan.y, endPan.y, percent, easing);
13882 }
13883
13884 self.emit('pan');
13885 }
13886
13887 var startZoom = ani_p.startZoom;
13888 var endZoom = ani_p.zoom;
13889 var animatingZoom = endZoom != null && isCore;
13890
13891 if (animatingZoom) {
13892 if (valid(startZoom, endZoom)) {
13893 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13894 }
13895
13896 self.emit('zoom');
13897 }
13898
13899 if (animatingPan || animatingZoom) {
13900 self.emit('viewport');
13901 }
13902
13903 var props = ani_p.style;
13904
13905 if (props && props.length > 0 && isEles) {
13906 for (var i = 0; i < props.length; i++) {
13907 var prop = props[i];
13908 var _name = prop.name;
13909 var end = prop;
13910 var start = ani_p.startStyle[_name];
13911 var propSpec = style.properties[start.name];
13912 var easedVal = ease(start, end, percent, easing, propSpec);
13913 style.overrideBypass(self, _name, easedVal);
13914 } // for props
13915
13916
13917 self.emit('style');
13918 } // if
13919
13920 }
13921
13922 ani_p.progress = percent;
13923 return percent;
13924}
13925
13926function valid(start, end) {
13927 if (start == null || end == null) {
13928 return false;
13929 }
13930
13931 if (number(start) && number(end)) {
13932 return true;
13933 } else if (start && end) {
13934 return true;
13935 }
13936
13937 return false;
13938}
13939
13940function startAnimation(self, ani, now, isCore) {
13941 var ani_p = ani._private;
13942 ani_p.started = true;
13943 ani_p.startTime = now - ani_p.progress * ani_p.duration;
13944}
13945
13946function stepAll(now, cy) {
13947 var eles = cy._private.aniEles;
13948 var doneEles = [];
13949
13950 function stepOne(ele, isCore) {
13951 var _p = ele._private;
13952 var current = _p.animation.current;
13953 var queue = _p.animation.queue;
13954 var ranAnis = false; // if nothing currently animating, get something from the queue
13955
13956 if (current.length === 0) {
13957 var next = queue.shift();
13958
13959 if (next) {
13960 current.push(next);
13961 }
13962 }
13963
13964 var callbacks = function callbacks(_callbacks) {
13965 for (var j = _callbacks.length - 1; j >= 0; j--) {
13966 var cb = _callbacks[j];
13967 cb();
13968 }
13969
13970 _callbacks.splice(0, _callbacks.length);
13971 }; // step and remove if done
13972
13973
13974 for (var i = current.length - 1; i >= 0; i--) {
13975 var ani = current[i];
13976 var ani_p = ani._private;
13977
13978 if (ani_p.stopped) {
13979 current.splice(i, 1);
13980 ani_p.hooked = false;
13981 ani_p.playing = false;
13982 ani_p.started = false;
13983 callbacks(ani_p.frames);
13984 continue;
13985 }
13986
13987 if (!ani_p.playing && !ani_p.applying) {
13988 continue;
13989 } // an apply() while playing shouldn't do anything
13990
13991
13992 if (ani_p.playing && ani_p.applying) {
13993 ani_p.applying = false;
13994 }
13995
13996 if (!ani_p.started) {
13997 startAnimation(ele, ani, now);
13998 }
13999
14000 step(ele, ani, now, isCore);
14001
14002 if (ani_p.applying) {
14003 ani_p.applying = false;
14004 }
14005
14006 callbacks(ani_p.frames);
14007
14008 if (ani_p.step != null) {
14009 ani_p.step(now);
14010 }
14011
14012 if (ani.completed()) {
14013 current.splice(i, 1);
14014 ani_p.hooked = false;
14015 ani_p.playing = false;
14016 ani_p.started = false;
14017 callbacks(ani_p.completes);
14018 }
14019
14020 ranAnis = true;
14021 }
14022
14023 if (!isCore && current.length === 0 && queue.length === 0) {
14024 doneEles.push(ele);
14025 }
14026
14027 return ranAnis;
14028 } // stepElement
14029 // handle all eles
14030
14031
14032 var ranEleAni = false;
14033
14034 for (var e = 0; e < eles.length; e++) {
14035 var ele = eles[e];
14036 var handledThisEle = stepOne(ele);
14037 ranEleAni = ranEleAni || handledThisEle;
14038 } // each element
14039
14040
14041 var ranCoreAni = stepOne(cy, true); // notify renderer
14042
14043 if (ranEleAni || ranCoreAni) {
14044 if (eles.length > 0) {
14045 cy.notify('draw', eles);
14046 } else {
14047 cy.notify('draw');
14048 }
14049 } // remove elements from list of currently animating if its queues are empty
14050
14051
14052 eles.unmerge(doneEles);
14053 cy.emit('step');
14054} // stepAll
14055
14056var corefn$1 = {
14057 // pull in animation functions
14058 animate: define$3.animate(),
14059 animation: define$3.animation(),
14060 animated: define$3.animated(),
14061 clearQueue: define$3.clearQueue(),
14062 delay: define$3.delay(),
14063 delayAnimation: define$3.delayAnimation(),
14064 stop: define$3.stop(),
14065 addToAnimationPool: function addToAnimationPool(eles) {
14066 var cy = this;
14067
14068 if (!cy.styleEnabled()) {
14069 return;
14070 } // save cycles when no style used
14071
14072
14073 cy._private.aniEles.merge(eles);
14074 },
14075 stopAnimationLoop: function stopAnimationLoop() {
14076 this._private.animationsRunning = false;
14077 },
14078 startAnimationLoop: function startAnimationLoop() {
14079 var cy = this;
14080 cy._private.animationsRunning = true;
14081
14082 if (!cy.styleEnabled()) {
14083 return;
14084 } // save cycles when no style used
14085 // NB the animation loop will exec in headless environments if style enabled
14086 // and explicit cy.destroy() is necessary to stop the loop
14087
14088
14089 function headlessStep() {
14090 if (!cy._private.animationsRunning) {
14091 return;
14092 }
14093
14094 requestAnimationFrame(function animationStep(now) {
14095 stepAll(now, cy);
14096 headlessStep();
14097 });
14098 }
14099
14100 var renderer = cy.renderer();
14101
14102 if (renderer && renderer.beforeRender) {
14103 // let the renderer schedule animations
14104 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14105 stepAll(now, cy);
14106 }, renderer.beforeRenderPriorities.animations);
14107 } else {
14108 // manage the animation loop ourselves
14109 headlessStep(); // first call
14110 }
14111 }
14112};
14113
14114var emitterOptions$1 = {
14115 qualifierCompare: function qualifierCompare(selector1, selector2) {
14116 if (selector1 == null || selector2 == null) {
14117 return selector1 == null && selector2 == null;
14118 } else {
14119 return selector1.sameText(selector2);
14120 }
14121 },
14122 eventMatches: function eventMatches(cy, listener, eventObj) {
14123 var selector = listener.qualifier;
14124
14125 if (selector != null) {
14126 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14127 }
14128
14129 return true;
14130 },
14131 addEventFields: function addEventFields(cy, evt) {
14132 evt.cy = cy;
14133 evt.target = cy;
14134 },
14135 callbackContext: function callbackContext(cy, listener, eventObj) {
14136 return listener.qualifier != null ? eventObj.target : cy;
14137 }
14138};
14139
14140var argSelector$1 = function argSelector(arg) {
14141 if (string(arg)) {
14142 return new Selector(arg);
14143 } else {
14144 return arg;
14145 }
14146};
14147
14148var elesfn$v = {
14149 createEmitter: function createEmitter() {
14150 var _p = this._private;
14151
14152 if (!_p.emitter) {
14153 _p.emitter = new Emitter(emitterOptions$1, this);
14154 }
14155
14156 return this;
14157 },
14158 emitter: function emitter() {
14159 return this._private.emitter;
14160 },
14161 on: function on(events, selector, callback) {
14162 this.emitter().on(events, argSelector$1(selector), callback);
14163 return this;
14164 },
14165 removeListener: function removeListener(events, selector, callback) {
14166 this.emitter().removeListener(events, argSelector$1(selector), callback);
14167 return this;
14168 },
14169 removeAllListeners: function removeAllListeners() {
14170 this.emitter().removeAllListeners();
14171 return this;
14172 },
14173 one: function one(events, selector, callback) {
14174 this.emitter().one(events, argSelector$1(selector), callback);
14175 return this;
14176 },
14177 once: function once(events, selector, callback) {
14178 this.emitter().one(events, argSelector$1(selector), callback);
14179 return this;
14180 },
14181 emit: function emit(events, extraParams) {
14182 this.emitter().emit(events, extraParams);
14183 return this;
14184 },
14185 emitAndNotify: function emitAndNotify(event, eles) {
14186 this.emit(event);
14187 this.notify(event, eles);
14188 return this;
14189 }
14190};
14191define$3.eventAliasesOn(elesfn$v);
14192
14193var corefn$2 = {
14194 png: function png(options) {
14195 var renderer = this._private.renderer;
14196 options = options || {};
14197 return renderer.png(options);
14198 },
14199 jpg: function jpg(options) {
14200 var renderer = this._private.renderer;
14201 options = options || {};
14202 options.bg = options.bg || '#fff';
14203 return renderer.jpg(options);
14204 }
14205};
14206corefn$2.jpeg = corefn$2.jpg;
14207
14208var corefn$3 = {
14209 layout: function layout(options) {
14210 var cy = this;
14211
14212 if (options == null) {
14213 error('Layout options must be specified to make a layout');
14214 return;
14215 }
14216
14217 if (options.name == null) {
14218 error('A `name` must be specified to make a layout');
14219 return;
14220 }
14221
14222 var name = options.name;
14223 var Layout = cy.extension('layout', name);
14224
14225 if (Layout == null) {
14226 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14227 return;
14228 }
14229
14230 var eles;
14231
14232 if (string(options.eles)) {
14233 eles = cy.$(options.eles);
14234 } else {
14235 eles = options.eles != null ? options.eles : cy.$();
14236 }
14237
14238 var layout = new Layout(extend({}, options, {
14239 cy: cy,
14240 eles: eles
14241 }));
14242 return layout;
14243 }
14244};
14245corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14246
14247var corefn$4 = {
14248 notify: function notify(eventName, eventEles) {
14249 var _p = this._private;
14250
14251 if (this.batching()) {
14252 _p.batchNotifications = _p.batchNotifications || {};
14253 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14254
14255 if (eventEles != null) {
14256 eles.merge(eventEles);
14257 }
14258
14259 return; // notifications are disabled during batching
14260 }
14261
14262 if (!_p.notificationsEnabled) {
14263 return;
14264 } // exit on disabled
14265
14266
14267 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14268
14269 if (this.destroyed() || !renderer) {
14270 return;
14271 }
14272
14273 renderer.notify(eventName, eventEles);
14274 },
14275 notifications: function notifications(bool) {
14276 var p = this._private;
14277
14278 if (bool === undefined) {
14279 return p.notificationsEnabled;
14280 } else {
14281 p.notificationsEnabled = bool ? true : false;
14282 }
14283
14284 return this;
14285 },
14286 noNotifications: function noNotifications(callback) {
14287 this.notifications(false);
14288 callback();
14289 this.notifications(true);
14290 },
14291 batching: function batching() {
14292 return this._private.batchCount > 0;
14293 },
14294 startBatch: function startBatch() {
14295 var _p = this._private;
14296
14297 if (_p.batchCount == null) {
14298 _p.batchCount = 0;
14299 }
14300
14301 if (_p.batchCount === 0) {
14302 _p.batchStyleEles = this.collection();
14303 _p.batchNotifications = {};
14304 }
14305
14306 _p.batchCount++;
14307 return this;
14308 },
14309 endBatch: function endBatch() {
14310 var _p = this._private;
14311
14312 if (_p.batchCount === 0) {
14313 return this;
14314 }
14315
14316 _p.batchCount--;
14317
14318 if (_p.batchCount === 0) {
14319 // update style for dirty eles
14320 _p.batchStyleEles.updateStyle();
14321
14322 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14323
14324 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14325 var eles = _p.batchNotifications[eventName];
14326
14327 if (eles.empty()) {
14328 renderer.notify(eventName);
14329 } else {
14330 renderer.notify(eventName, eles);
14331 }
14332 });
14333 }
14334
14335 return this;
14336 },
14337 batch: function batch(callback) {
14338 this.startBatch();
14339 callback();
14340 this.endBatch();
14341 return this;
14342 },
14343 // for backwards compatibility
14344 batchData: function batchData(map) {
14345 var cy = this;
14346 return this.batch(function () {
14347 var ids = Object.keys(map);
14348
14349 for (var i = 0; i < ids.length; i++) {
14350 var id = ids[i];
14351 var data = map[id];
14352 var ele = cy.getElementById(id);
14353 ele.data(data);
14354 }
14355 });
14356 }
14357};
14358
14359var rendererDefaults = defaults({
14360 hideEdgesOnViewport: false,
14361 textureOnViewport: false,
14362 motionBlur: false,
14363 motionBlurOpacity: 0.05,
14364 pixelRatio: undefined,
14365 desktopTapThreshold: 4,
14366 touchTapThreshold: 8,
14367 wheelSensitivity: 1,
14368 debug: false,
14369 showFps: false
14370});
14371var corefn$5 = {
14372 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14373 var r = this._private.renderer;
14374 r.renderTo(context, zoom, pan, pxRatio);
14375 return this;
14376 },
14377 renderer: function renderer() {
14378 return this._private.renderer;
14379 },
14380 forceRender: function forceRender() {
14381 this.notify('draw');
14382 return this;
14383 },
14384 resize: function resize() {
14385 this.invalidateSize();
14386 this.emitAndNotify('resize');
14387 return this;
14388 },
14389 initRenderer: function initRenderer(options) {
14390 var cy = this;
14391 var RendererProto = cy.extension('renderer', options.name);
14392
14393 if (RendererProto == null) {
14394 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14395 return;
14396 }
14397
14398 if (options.wheelSensitivity !== undefined) {
14399 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.");
14400 }
14401
14402 var rOpts = rendererDefaults(options);
14403 rOpts.cy = cy;
14404 cy._private.renderer = new RendererProto(rOpts);
14405 this.notify('init');
14406 },
14407 destroyRenderer: function destroyRenderer() {
14408 var cy = this;
14409 cy.notify('destroy'); // destroy the renderer
14410
14411 var domEle = cy.container();
14412
14413 if (domEle) {
14414 domEle._cyreg = null;
14415
14416 while (domEle.childNodes.length > 0) {
14417 domEle.removeChild(domEle.childNodes[0]);
14418 }
14419 }
14420
14421 cy._private.renderer = null; // to be extra safe, remove the ref
14422
14423 cy.mutableElements().forEach(function (ele) {
14424 var _p = ele._private;
14425 _p.rscratch = {};
14426 _p.rstyle = {};
14427 _p.animation.current = [];
14428 _p.animation.queue = [];
14429 });
14430 },
14431 onRender: function onRender(fn) {
14432 return this.on('render', fn);
14433 },
14434 offRender: function offRender(fn) {
14435 return this.off('render', fn);
14436 }
14437};
14438corefn$5.invalidateDimensions = corefn$5.resize;
14439
14440var corefn$6 = {
14441 // get a collection
14442 // - empty collection on no args
14443 // - collection of elements in the graph on selector arg
14444 // - guarantee a returned collection when elements or collection specified
14445 collection: function collection(eles, opts) {
14446 if (string(eles)) {
14447 return this.$(eles);
14448 } else if (elementOrCollection(eles)) {
14449 return eles.collection();
14450 } else if (array(eles)) {
14451 return new Collection(this, eles, opts);
14452 }
14453
14454 return new Collection(this);
14455 },
14456 nodes: function nodes(selector) {
14457 var nodes = this.$(function (ele) {
14458 return ele.isNode();
14459 });
14460
14461 if (selector) {
14462 return nodes.filter(selector);
14463 }
14464
14465 return nodes;
14466 },
14467 edges: function edges(selector) {
14468 var edges = this.$(function (ele) {
14469 return ele.isEdge();
14470 });
14471
14472 if (selector) {
14473 return edges.filter(selector);
14474 }
14475
14476 return edges;
14477 },
14478 // search the graph like jQuery
14479 $: function $(selector) {
14480 var eles = this._private.elements;
14481
14482 if (selector) {
14483 return eles.filter(selector);
14484 } else {
14485 return eles.spawnSelf();
14486 }
14487 },
14488 mutableElements: function mutableElements() {
14489 return this._private.elements;
14490 }
14491}; // aliases
14492
14493corefn$6.elements = corefn$6.filter = corefn$6.$;
14494
14495var styfn = {}; // keys for style blocks, e.g. ttfftt
14496
14497var TRUE = 't';
14498var FALSE = 'f'; // (potentially expensive calculation)
14499// apply the style to the element based on
14500// - its bypass
14501// - what selectors match it
14502
14503styfn.apply = function (eles) {
14504 var self = this;
14505 var _p = self._private;
14506 var cy = _p.cy;
14507 var updatedEles = cy.collection();
14508
14509 if (_p.newStyle) {
14510 // clear style caches
14511 _p.contextStyles = {};
14512 _p.propDiffs = {};
14513 self.cleanElements(eles, true);
14514 }
14515
14516 for (var ie = 0; ie < eles.length; ie++) {
14517 var ele = eles[ie];
14518 var cxtMeta = self.getContextMeta(ele);
14519
14520 if (cxtMeta.empty) {
14521 continue;
14522 }
14523
14524 var cxtStyle = self.getContextStyle(cxtMeta);
14525 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14526
14527 if (!_p.newStyle) {
14528 self.updateTransitions(ele, app.diffProps);
14529 }
14530
14531 var hintsDiff = self.updateStyleHints(ele);
14532
14533 if (hintsDiff) {
14534 updatedEles.push(ele);
14535 }
14536 } // for elements
14537
14538
14539 _p.newStyle = false;
14540 return updatedEles;
14541};
14542
14543styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14544 var self = this;
14545 var cache = self._private.propDiffs = self._private.propDiffs || {};
14546 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14547 var cachedVal = cache[dualCxtKey];
14548
14549 if (cachedVal) {
14550 return cachedVal;
14551 }
14552
14553 var diffProps = [];
14554 var addedProp = {};
14555
14556 for (var i = 0; i < self.length; i++) {
14557 var cxt = self[i];
14558 var oldHasCxt = oldCxtKey[i] === TRUE;
14559 var newHasCxt = newCxtKey[i] === TRUE;
14560 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14561 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14562
14563 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14564 var props = void 0;
14565
14566 if (cxtHasDiffed && cxtHasMappedProps) {
14567 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14568 } else if (cxtHasDiffed) {
14569 props = cxt.properties; // need to check them all
14570 } else if (cxtHasMappedProps) {
14571 props = cxt.mappedProperties; // only need to check mapped
14572 }
14573
14574 for (var j = 0; j < props.length; j++) {
14575 var prop = props[j];
14576 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14577 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14578 // is cached)
14579
14580 var laterCxtOverrides = false;
14581
14582 for (var k = i + 1; k < self.length; k++) {
14583 var laterCxt = self[k];
14584 var hasLaterCxt = newCxtKey[k] === TRUE;
14585
14586 if (!hasLaterCxt) {
14587 continue;
14588 } // can't override unless the context is active
14589
14590
14591 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14592
14593 if (laterCxtOverrides) {
14594 break;
14595 } // exit early as long as one later context overrides
14596
14597 }
14598
14599 if (!addedProp[name] && !laterCxtOverrides) {
14600 addedProp[name] = true;
14601 diffProps.push(name);
14602 }
14603 } // for props
14604
14605 } // if
14606
14607 } // for contexts
14608
14609
14610 cache[dualCxtKey] = diffProps;
14611 return diffProps;
14612};
14613
14614styfn.getContextMeta = function (ele) {
14615 var self = this;
14616 var cxtKey = '';
14617 var diffProps;
14618 var prevKey = ele._private.styleCxtKey || '';
14619
14620 if (self._private.newStyle) {
14621 prevKey = ''; // since we need to apply all style if a fresh stylesheet
14622 } // get the cxt key
14623
14624
14625 for (var i = 0; i < self.length; i++) {
14626 var context = self[i];
14627 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14628
14629 if (contextSelectorMatches) {
14630 cxtKey += TRUE;
14631 } else {
14632 cxtKey += FALSE;
14633 }
14634 } // for context
14635
14636
14637 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14638 ele._private.styleCxtKey = cxtKey;
14639 return {
14640 key: cxtKey,
14641 diffPropNames: diffProps,
14642 empty: diffProps.length === 0
14643 };
14644}; // gets a computed ele style object based on matched contexts
14645
14646
14647styfn.getContextStyle = function (cxtMeta) {
14648 var cxtKey = cxtMeta.key;
14649 var self = this;
14650 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14651
14652 if (cxtStyles[cxtKey]) {
14653 return cxtStyles[cxtKey];
14654 }
14655
14656 var style = {
14657 _private: {
14658 key: cxtKey
14659 }
14660 };
14661
14662 for (var i = 0; i < self.length; i++) {
14663 var cxt = self[i];
14664 var hasCxt = cxtKey[i] === TRUE;
14665
14666 if (!hasCxt) {
14667 continue;
14668 }
14669
14670 for (var j = 0; j < cxt.properties.length; j++) {
14671 var prop = cxt.properties[j];
14672 style[prop.name] = prop;
14673 }
14674 }
14675
14676 cxtStyles[cxtKey] = style;
14677 return style;
14678};
14679
14680styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14681 var self = this;
14682 var diffProps = cxtMeta.diffPropNames;
14683 var retDiffProps = {};
14684 var types = self.types;
14685
14686 for (var i = 0; i < diffProps.length; i++) {
14687 var diffPropName = diffProps[i];
14688 var cxtProp = cxtStyle[diffPropName];
14689 var eleProp = ele.pstyle(diffPropName);
14690
14691 if (!cxtProp) {
14692 // no context prop means delete
14693 if (!eleProp) {
14694 continue; // no existing prop means nothing needs to be removed
14695 // nb affects initial application on mapped values like control-point-distances
14696 } else if (eleProp.bypass) {
14697 cxtProp = {
14698 name: diffPropName,
14699 deleteBypassed: true
14700 };
14701 } else {
14702 cxtProp = {
14703 name: diffPropName,
14704 "delete": true
14705 };
14706 }
14707 } // save cycles when the context prop doesn't need to be applied
14708
14709
14710 if (eleProp === cxtProp) {
14711 continue;
14712 } // save cycles when a mapped context prop doesn't need to be applied
14713
14714
14715 if (cxtProp.mapped === types.fn // context prop is function mapper
14716 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14717 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14718 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14719 ) {
14720 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14721 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14722
14723 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14724
14725 if (fnValue === mapping.prevFnValue) {
14726 continue;
14727 }
14728 }
14729
14730 var retDiffProp = retDiffProps[diffPropName] = {
14731 prev: eleProp
14732 };
14733 self.applyParsedProperty(ele, cxtProp);
14734 retDiffProp.next = ele.pstyle(diffPropName);
14735
14736 if (retDiffProp.next && retDiffProp.next.bypass) {
14737 retDiffProp.next = retDiffProp.next.bypassed;
14738 }
14739 }
14740
14741 return {
14742 diffProps: retDiffProps
14743 };
14744};
14745
14746styfn.updateStyleHints = function (ele) {
14747 var _p = ele._private;
14748 var self = this;
14749 var propNames = self.propertyGroupNames;
14750 var propGrKeys = self.propertyGroupKeys;
14751
14752 var propHash = function propHash(ele, propNames, seedKey) {
14753 return self.getPropertiesHash(ele, propNames, seedKey);
14754 };
14755
14756 var oldStyleKey = _p.styleKey;
14757
14758 if (ele.removed()) {
14759 return false;
14760 }
14761
14762 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14763 // but lazily -- only use non-default prop values to reduce the number of hashes
14764 //
14765
14766 var overriddenStyles = ele._private.style;
14767 propNames = Object.keys(overriddenStyles);
14768
14769 for (var i = 0; i < propGrKeys.length; i++) {
14770 var grKey = propGrKeys[i];
14771 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14772 }
14773
14774 var updateGrKey1 = function updateGrKey1(val, grKey) {
14775 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14776 };
14777
14778 var updateGrKey2 = function updateGrKey2(val, grKey) {
14779 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14780 };
14781
14782 var updateGrKey = function updateGrKey(val, grKey) {
14783 updateGrKey1(val, grKey);
14784 updateGrKey2(val, grKey);
14785 };
14786
14787 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14788 for (var j = 0; j < strVal.length; j++) {
14789 var ch = strVal.charCodeAt(j);
14790 updateGrKey1(ch, grKey);
14791 updateGrKey2(ch, grKey);
14792 }
14793 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14794 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14795 // - raise up small numbers so more significant digits are seen by hashing
14796 // - make small numbers larger than a normal value to avoid collisions
14797 // - works in practice and it's relatively cheap
14798
14799
14800 var N = 2000000000;
14801
14802 var cleanNum = function cleanNum(val) {
14803 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14804 };
14805
14806 for (var _i = 0; _i < propNames.length; _i++) {
14807 var name = propNames[_i];
14808 var parsedProp = overriddenStyles[name];
14809
14810 if (parsedProp == null) {
14811 continue;
14812 }
14813
14814 var propInfo = this.properties[name];
14815 var type = propInfo.type;
14816 var _grKey = propInfo.groupKey;
14817 var normalizedNumberVal = void 0;
14818
14819 if (propInfo.hashOverride != null) {
14820 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14821 } else if (parsedProp.pfValue != null) {
14822 normalizedNumberVal = parsedProp.pfValue;
14823 } // might not be a number if it allows enums
14824
14825
14826 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14827 var haveNormNum = normalizedNumberVal != null;
14828 var haveUnitedNum = numberVal != null;
14829 var haveNum = haveNormNum || haveUnitedNum;
14830 var units = parsedProp.units; // numbers are cheaper to hash than strings
14831 // 1 hash op vs n hash ops (for length n string)
14832
14833 if (type.number && haveNum && !type.multiple) {
14834 var v = haveNormNum ? normalizedNumberVal : numberVal;
14835 updateGrKey(cleanNum(v), _grKey);
14836
14837 if (!haveNormNum && units != null) {
14838 updateGrKeyWStr(units, _grKey);
14839 }
14840 } else {
14841 updateGrKeyWStr(parsedProp.strValue, _grKey);
14842 }
14843 } // overall style key
14844 //
14845
14846
14847 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14848
14849 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14850 var _grKey2 = propGrKeys[_i2];
14851 var grHash = _p.styleKeys[_grKey2];
14852 hash[0] = hashInt(grHash[0], hash[0]);
14853 hash[1] = hashIntAlt(grHash[1], hash[1]);
14854 }
14855
14856 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14857 //
14858
14859 var sk = _p.styleKeys;
14860 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14861 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14862 _p.labelKey = combineHashesArray(labelKeys);
14863 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14864
14865 if (!isNode) {
14866 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14867 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14868 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14869 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14870 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14871 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14872 } // node
14873 //
14874
14875
14876 if (isNode) {
14877 var _p$styleKeys = _p.styleKeys,
14878 nodeBody = _p$styleKeys.nodeBody,
14879 nodeBorder = _p$styleKeys.nodeBorder,
14880 backgroundImage = _p$styleKeys.backgroundImage,
14881 compound = _p$styleKeys.compound,
14882 pie = _p$styleKeys.pie;
14883 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14884 return k != null;
14885 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14886 _p.nodeKey = combineHashesArray(nodeKeys);
14887 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14888 }
14889
14890 return oldStyleKey !== _p.styleKey;
14891};
14892
14893styfn.clearStyleHints = function (ele) {
14894 var _p = ele._private;
14895 _p.styleKeys = {};
14896 _p.styleKey = null;
14897 _p.labelKey = null;
14898 _p.labelStyleKey = null;
14899 _p.sourceLabelKey = null;
14900 _p.sourceLabelStyleKey = null;
14901 _p.targetLabelKey = null;
14902 _p.targetLabelStyleKey = null;
14903 _p.nodeKey = null;
14904 _p.hasPie = null;
14905}; // apply a property to the style (for internal use)
14906// returns whether application was successful
14907//
14908// now, this function flattens the property, and here's how:
14909//
14910// for parsedProp:{ bypass: true, deleteBypass: true }
14911// no property is generated, instead the bypass property in the
14912// element's style is replaced by what's pointed to by the `bypassed`
14913// field in the bypass property (i.e. restoring the property the
14914// bypass was overriding)
14915//
14916// for parsedProp:{ mapped: truthy }
14917// the generated flattenedProp:{ mapping: prop }
14918//
14919// for parsedProp:{ bypass: true }
14920// the generated flattenedProp:{ bypassed: parsedProp }
14921
14922
14923styfn.applyParsedProperty = function (ele, parsedProp) {
14924 var self = this;
14925 var prop = parsedProp;
14926 var style = ele._private.style;
14927 var flatProp;
14928 var types = self.types;
14929 var type = self.properties[prop.name].type;
14930 var propIsBypass = prop.bypass;
14931 var origProp = style[prop.name];
14932 var origPropIsBypass = origProp && origProp.bypass;
14933 var _p = ele._private;
14934 var flatPropMapping = 'mapping';
14935
14936 var getVal = function getVal(p) {
14937 if (p == null) {
14938 return null;
14939 } else if (p.pfValue != null) {
14940 return p.pfValue;
14941 } else {
14942 return p.value;
14943 }
14944 };
14945
14946 var checkTriggers = function checkTriggers() {
14947 var fromVal = getVal(origProp);
14948 var toVal = getVal(prop);
14949 self.checkTriggers(ele, prop.name, fromVal, toVal);
14950 }; // edge sanity checks to prevent the client from making serious mistakes
14951
14952
14953 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
14954 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
14955 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
14956 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
14957 }
14958
14959 if (prop["delete"]) {
14960 // delete the property and use the default value on falsey value
14961 style[prop.name] = undefined;
14962 checkTriggers();
14963 return true;
14964 }
14965
14966 if (prop.deleteBypassed) {
14967 // delete the property that the
14968 if (!origProp) {
14969 checkTriggers();
14970 return true; // can't delete if no prop
14971 } else if (origProp.bypass) {
14972 // delete bypassed
14973 origProp.bypassed = undefined;
14974 checkTriggers();
14975 return true;
14976 } else {
14977 return false; // we're unsuccessful deleting the bypassed
14978 }
14979 } // check if we need to delete the current bypass
14980
14981
14982 if (prop.deleteBypass) {
14983 // then this property is just here to indicate we need to delete
14984 if (!origProp) {
14985 checkTriggers();
14986 return true; // property is already not defined
14987 } else if (origProp.bypass) {
14988 // then replace the bypass property with the original
14989 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
14990 style[prop.name] = origProp.bypassed;
14991 checkTriggers();
14992 return true;
14993 } else {
14994 return false; // we're unsuccessful deleting the bypass
14995 }
14996 }
14997
14998 var printMappingErr = function printMappingErr() {
14999 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');
15000 }; // put the property in the style objects
15001
15002
15003 switch (prop.mapped) {
15004 // flatten the property if mapped
15005 case types.mapData:
15006 {
15007 // flatten the field (e.g. data.foo.bar)
15008 var fields = prop.field.split('.');
15009 var fieldVal = _p.data;
15010
15011 for (var i = 0; i < fields.length && fieldVal; i++) {
15012 var field = fields[i];
15013 fieldVal = fieldVal[field];
15014 }
15015
15016 if (fieldVal == null) {
15017 printMappingErr();
15018 return false;
15019 }
15020
15021 var percent;
15022
15023 if (!number(fieldVal)) {
15024 // then don't apply and fall back on the existing style
15025 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15026 return false;
15027 } else {
15028 var fieldWidth = prop.fieldMax - prop.fieldMin;
15029
15030 if (fieldWidth === 0) {
15031 // safety check -- not strictly necessary as no props of zero range should be passed here
15032 percent = 0;
15033 } else {
15034 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15035 }
15036 } // make sure to bound percent value
15037
15038
15039 if (percent < 0) {
15040 percent = 0;
15041 } else if (percent > 1) {
15042 percent = 1;
15043 }
15044
15045 if (type.color) {
15046 var r1 = prop.valueMin[0];
15047 var r2 = prop.valueMax[0];
15048 var g1 = prop.valueMin[1];
15049 var g2 = prop.valueMax[1];
15050 var b1 = prop.valueMin[2];
15051 var b2 = prop.valueMax[2];
15052 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15053 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15054 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)];
15055 flatProp = {
15056 // colours are simple, so just create the flat property instead of expensive string parsing
15057 bypass: prop.bypass,
15058 // we're a bypass if the mapping property is a bypass
15059 name: prop.name,
15060 value: clr,
15061 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15062 };
15063 } else if (type.number) {
15064 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15065 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15066 } else {
15067 return false; // can only map to colours and numbers
15068 }
15069
15070 if (!flatProp) {
15071 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15072 printMappingErr();
15073 return false;
15074 }
15075
15076 flatProp.mapping = prop; // keep a reference to the mapping
15077
15078 prop = flatProp; // the flattened (mapped) property is the one we want
15079
15080 break;
15081 }
15082 // direct mapping
15083
15084 case types.data:
15085 {
15086 // flatten the field (e.g. data.foo.bar)
15087 var _fields = prop.field.split('.');
15088
15089 var _fieldVal = _p.data;
15090
15091 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15092 var _field = _fields[_i3];
15093 _fieldVal = _fieldVal[_field];
15094 }
15095
15096 if (_fieldVal != null) {
15097 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15098 }
15099
15100 if (!flatProp) {
15101 // if we can't flatten the property, then don't apply and fall back on the existing style
15102 printMappingErr();
15103 return false;
15104 }
15105
15106 flatProp.mapping = prop; // keep a reference to the mapping
15107
15108 prop = flatProp; // the flattened (mapped) property is the one we want
15109
15110 break;
15111 }
15112
15113 case types.fn:
15114 {
15115 var fn = prop.value;
15116 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15117
15118 prop.prevFnValue = fnRetVal;
15119
15120 if (fnRetVal == null) {
15121 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15122 return false;
15123 }
15124
15125 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15126
15127 if (!flatProp) {
15128 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15129 return false;
15130 }
15131
15132 flatProp.mapping = copy(prop); // keep a reference to the mapping
15133
15134 prop = flatProp; // the flattened (mapped) property is the one we want
15135
15136 break;
15137 }
15138
15139 case undefined:
15140 break;
15141 // just set the property
15142
15143 default:
15144 return false;
15145 // not a valid mapping
15146 } // if the property is a bypass property, then link the resultant property to the original one
15147
15148
15149 if (propIsBypass) {
15150 if (origPropIsBypass) {
15151 // then this bypass overrides the existing one
15152 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15153 } else {
15154 // then link the orig prop to the new bypass
15155 prop.bypassed = origProp;
15156 }
15157
15158 style[prop.name] = prop; // and set
15159 } else {
15160 // prop is not bypass
15161 if (origPropIsBypass) {
15162 // then keep the orig prop (since it's a bypass) and link to the new prop
15163 origProp.bypassed = prop;
15164 } else {
15165 // then just replace the old prop with the new one
15166 style[prop.name] = prop;
15167 }
15168 }
15169
15170 checkTriggers();
15171 return true;
15172};
15173
15174styfn.cleanElements = function (eles, keepBypasses) {
15175 for (var i = 0; i < eles.length; i++) {
15176 var ele = eles[i];
15177 this.clearStyleHints(ele);
15178 ele.dirtyCompoundBoundsCache();
15179 ele.dirtyBoundingBoxCache();
15180
15181 if (!keepBypasses) {
15182 ele._private.style = {};
15183 } else {
15184 var style = ele._private.style;
15185 var propNames = Object.keys(style);
15186
15187 for (var j = 0; j < propNames.length; j++) {
15188 var propName = propNames[j];
15189 var eleProp = style[propName];
15190
15191 if (eleProp != null) {
15192 if (eleProp.bypass) {
15193 eleProp.bypassed = null;
15194 } else {
15195 style[propName] = null;
15196 }
15197 }
15198 }
15199 }
15200 }
15201}; // updates the visual style for all elements (useful for manual style modification after init)
15202
15203
15204styfn.update = function () {
15205 var cy = this._private.cy;
15206 var eles = cy.mutableElements();
15207 eles.updateStyle();
15208}; // diffProps : { name => { prev, next } }
15209
15210
15211styfn.updateTransitions = function (ele, diffProps) {
15212 var self = this;
15213 var _p = ele._private;
15214 var props = ele.pstyle('transition-property').value;
15215 var duration = ele.pstyle('transition-duration').pfValue;
15216 var delay = ele.pstyle('transition-delay').pfValue;
15217
15218 if (props.length > 0 && duration > 0) {
15219 var style = {}; // build up the style to animate towards
15220
15221 var anyPrev = false;
15222
15223 for (var i = 0; i < props.length; i++) {
15224 var prop = props[i];
15225 var styProp = ele.pstyle(prop);
15226 var diffProp = diffProps[prop];
15227
15228 if (!diffProp) {
15229 continue;
15230 }
15231
15232 var prevProp = diffProp.prev;
15233 var fromProp = prevProp;
15234 var toProp = diffProp.next != null ? diffProp.next : styProp;
15235 var diff = false;
15236 var initVal = void 0;
15237 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15238
15239 if (!fromProp) {
15240 continue;
15241 } // consider px values
15242
15243
15244 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15245 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15246
15247 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15248 } else if (number(fromProp.value) && number(toProp.value)) {
15249 diff = toProp.value - fromProp.value; // nonzero is truthy
15250
15251 initVal = fromProp.value + initDt * diff; // consider colour values
15252 } else if (array(fromProp.value) && array(toProp.value)) {
15253 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15254 initVal = fromProp.strValue;
15255 } // the previous value is good for an animation only if it's different
15256
15257
15258 if (diff) {
15259 style[prop] = toProp.strValue; // to val
15260
15261 this.applyBypass(ele, prop, initVal); // from val
15262
15263 anyPrev = true;
15264 }
15265 } // end if props allow ani
15266 // can't transition if there's nothing previous to transition from
15267
15268
15269 if (!anyPrev) {
15270 return;
15271 }
15272
15273 _p.transitioning = true;
15274 new Promise$1(function (resolve) {
15275 if (delay > 0) {
15276 ele.delayAnimation(delay).play().promise().then(resolve);
15277 } else {
15278 resolve();
15279 }
15280 }).then(function () {
15281 return ele.animation({
15282 style: style,
15283 duration: duration,
15284 easing: ele.pstyle('transition-timing-function').value,
15285 queue: false
15286 }).play().promise();
15287 }).then(function () {
15288 // if( !isBypass ){
15289 self.removeBypasses(ele, props);
15290 ele.emitAndNotify('style'); // }
15291
15292 _p.transitioning = false;
15293 });
15294 } else if (_p.transitioning) {
15295 this.removeBypasses(ele, props);
15296 ele.emitAndNotify('style');
15297 _p.transitioning = false;
15298 }
15299};
15300
15301styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15302 var prop = this.properties[name];
15303 var triggerCheck = getTrigger(prop);
15304
15305 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15306 onTrigger(prop);
15307 }
15308};
15309
15310styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15311 var _this = this;
15312
15313 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15314 return prop.triggersZOrder;
15315 }, function () {
15316 _this._private.cy.notify('zorder', ele);
15317 });
15318};
15319
15320styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15321 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15322 return prop.triggersBounds;
15323 }, function (prop) {
15324 ele.dirtyCompoundBoundsCache();
15325 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15326 // then dirty the pll edge bb cache as well
15327
15328 if ( // only for beziers -- so performance of other edges isn't affected
15329 name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') && prop.triggersBoundsOfParallelBeziers) {
15330 ele.parallelEdges().forEach(function (pllEdge) {
15331 if (pllEdge.isBundledBezier()) {
15332 pllEdge.dirtyBoundingBoxCache();
15333 }
15334 });
15335 }
15336 });
15337};
15338
15339styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15340 ele.dirtyStyleCache();
15341 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15342 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15343};
15344
15345var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15346// returns true iff application was successful for at least 1 specified property
15347
15348styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15349 var self = this;
15350 var props = [];
15351 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15352
15353 if (name === '*' || name === '**') {
15354 // apply to all property names
15355 if (value !== undefined) {
15356 for (var i = 0; i < self.properties.length; i++) {
15357 var prop = self.properties[i];
15358 var _name = prop.name;
15359 var parsedProp = this.parse(_name, value, true);
15360
15361 if (parsedProp) {
15362 props.push(parsedProp);
15363 }
15364 }
15365 }
15366 } else if (string(name)) {
15367 // then parse the single property
15368 var _parsedProp = this.parse(name, value, true);
15369
15370 if (_parsedProp) {
15371 props.push(_parsedProp);
15372 }
15373 } else if (plainObject(name)) {
15374 // then parse each property
15375 var specifiedProps = name;
15376 updateTransitions = value;
15377 var names = Object.keys(specifiedProps);
15378
15379 for (var _i = 0; _i < names.length; _i++) {
15380 var _name2 = names[_i];
15381 var _value = specifiedProps[_name2];
15382
15383 if (_value === undefined) {
15384 // try camel case name too
15385 _value = specifiedProps[dash2camel(_name2)];
15386 }
15387
15388 if (_value !== undefined) {
15389 var _parsedProp2 = this.parse(_name2, _value, true);
15390
15391 if (_parsedProp2) {
15392 props.push(_parsedProp2);
15393 }
15394 }
15395 }
15396 } else {
15397 // can't do anything without well defined properties
15398 return false;
15399 } // we've failed if there are no valid properties
15400
15401
15402 if (props.length === 0) {
15403 return false;
15404 } // now, apply the bypass properties on the elements
15405
15406
15407 var ret = false; // return true if at least one succesful bypass applied
15408
15409 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15410 // for each ele
15411 var ele = eles[_i2];
15412 var diffProps = {};
15413 var diffProp = void 0;
15414
15415 for (var j = 0; j < props.length; j++) {
15416 // for each prop
15417 var _prop = props[j];
15418
15419 if (updateTransitions) {
15420 var prevProp = ele.pstyle(_prop.name);
15421 diffProp = diffProps[_prop.name] = {
15422 prev: prevProp
15423 };
15424 }
15425
15426 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15427
15428 if (updateTransitions) {
15429 diffProp.next = ele.pstyle(_prop.name);
15430 }
15431 } // for props
15432
15433
15434 if (ret) {
15435 this.updateStyleHints(ele);
15436 }
15437
15438 if (updateTransitions) {
15439 this.updateTransitions(ele, diffProps, isBypass);
15440 }
15441 } // for eles
15442
15443
15444 return ret;
15445}; // only useful in specific cases like animation
15446
15447
15448styfn$1.overrideBypass = function (eles, name, value) {
15449 name = camel2dash(name);
15450
15451 for (var i = 0; i < eles.length; i++) {
15452 var ele = eles[i];
15453 var prop = ele._private.style[name];
15454 var type = this.properties[name].type;
15455 var isColor = type.color;
15456 var isMulti = type.mutiple;
15457 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15458
15459 if (!prop || !prop.bypass) {
15460 // need a bypass if one doesn't exist
15461 this.applyBypass(ele, name, value);
15462 } else {
15463 prop.value = value;
15464
15465 if (prop.pfValue != null) {
15466 prop.pfValue = value;
15467 }
15468
15469 if (isColor) {
15470 prop.strValue = 'rgb(' + value.join(',') + ')';
15471 } else if (isMulti) {
15472 prop.strValue = value.join(' ');
15473 } else {
15474 prop.strValue = '' + value;
15475 }
15476
15477 this.updateStyleHints(ele);
15478 }
15479
15480 this.checkTriggers(ele, name, oldValue, value);
15481 }
15482};
15483
15484styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15485 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15486};
15487
15488styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15489 var isBypass = true;
15490
15491 for (var j = 0; j < eles.length; j++) {
15492 var ele = eles[j];
15493 var diffProps = {};
15494
15495 for (var i = 0; i < props.length; i++) {
15496 var name = props[i];
15497 var prop = this.properties[name];
15498 var prevProp = ele.pstyle(prop.name);
15499
15500 if (!prevProp || !prevProp.bypass) {
15501 // if a bypass doesn't exist for the prop, nothing needs to be removed
15502 continue;
15503 }
15504
15505 var value = ''; // empty => remove bypass
15506
15507 var parsedProp = this.parse(name, value, true);
15508 var diffProp = diffProps[prop.name] = {
15509 prev: prevProp
15510 };
15511 this.applyParsedProperty(ele, parsedProp);
15512 diffProp.next = ele.pstyle(prop.name);
15513 } // for props
15514
15515
15516 this.updateStyleHints(ele);
15517
15518 if (updateTransitions) {
15519 this.updateTransitions(ele, diffProps, isBypass);
15520 }
15521 } // for eles
15522
15523};
15524
15525var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15526
15527styfn$2.getEmSizeInPixels = function () {
15528 var px = this.containerCss('font-size');
15529
15530 if (px != null) {
15531 return parseFloat(px);
15532 } else {
15533 return 1; // for headless
15534 }
15535}; // gets css property from the core container
15536
15537
15538styfn$2.containerCss = function (propName) {
15539 var cy = this._private.cy;
15540 var domElement = cy.container();
15541
15542 if (window$1 && domElement && window$1.getComputedStyle) {
15543 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15544 }
15545};
15546
15547var styfn$3 = {}; // gets the rendered style for an element
15548
15549styfn$3.getRenderedStyle = function (ele, prop) {
15550 if (prop) {
15551 return this.getStylePropertyValue(ele, prop, true);
15552 } else {
15553 return this.getRawStyle(ele, true);
15554 }
15555}; // gets the raw style for an element
15556
15557
15558styfn$3.getRawStyle = function (ele, isRenderedVal) {
15559 var self = this;
15560 ele = ele[0]; // insure it's an element
15561
15562 if (ele) {
15563 var rstyle = {};
15564
15565 for (var i = 0; i < self.properties.length; i++) {
15566 var prop = self.properties[i];
15567 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15568
15569 if (val != null) {
15570 rstyle[prop.name] = val;
15571 rstyle[dash2camel(prop.name)] = val;
15572 }
15573 }
15574
15575 return rstyle;
15576 }
15577};
15578
15579styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15580 var pstyle = ele.pstyle(property)[subproperty][index];
15581 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15582};
15583
15584styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15585 var self = this;
15586 ele = ele[0]; // insure it's an element
15587
15588 if (ele) {
15589 var prop = self.properties[propName];
15590
15591 if (prop.alias) {
15592 prop = prop.pointsTo;
15593 }
15594
15595 var type = prop.type;
15596 var styleProp = ele.pstyle(prop.name);
15597
15598 if (styleProp) {
15599 var value = styleProp.value,
15600 units = styleProp.units,
15601 strValue = styleProp.strValue;
15602
15603 if (isRenderedVal && type.number && value != null && number(value)) {
15604 var zoom = ele.cy().zoom();
15605
15606 var getRenderedValue = function getRenderedValue(val) {
15607 return val * zoom;
15608 };
15609
15610 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15611 return getRenderedValue(val) + units;
15612 };
15613
15614 var isArrayValue = array(value);
15615 var haveUnits = isArrayValue ? units.every(function (u) {
15616 return u != null;
15617 }) : units != null;
15618
15619 if (haveUnits) {
15620 if (isArrayValue) {
15621 return value.map(function (v, i) {
15622 return getValueStringWithUnits(v, units[i]);
15623 }).join(' ');
15624 } else {
15625 return getValueStringWithUnits(value, units);
15626 }
15627 } else {
15628 if (isArrayValue) {
15629 return value.map(function (v) {
15630 return string(v) ? v : '' + getRenderedValue(v);
15631 }).join(' ');
15632 } else {
15633 return '' + getRenderedValue(value);
15634 }
15635 }
15636 } else if (strValue != null) {
15637 return strValue;
15638 }
15639 }
15640
15641 return null;
15642 }
15643};
15644
15645styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15646 var rstyle = {};
15647
15648 for (var i = 0; i < aniProps.length; i++) {
15649 var aniProp = aniProps[i];
15650 var name = aniProp.name;
15651 var styleProp = ele.pstyle(name);
15652
15653 if (styleProp !== undefined) {
15654 // then make a prop of it
15655 if (plainObject(styleProp)) {
15656 styleProp = this.parse(name, styleProp.strValue);
15657 } else {
15658 styleProp = this.parse(name, styleProp);
15659 }
15660 }
15661
15662 if (styleProp) {
15663 rstyle[name] = styleProp;
15664 }
15665 }
15666
15667 return rstyle;
15668};
15669
15670styfn$3.getPropsList = function (propsObj) {
15671 var self = this;
15672 var rstyle = [];
15673 var style = propsObj;
15674 var props = self.properties;
15675
15676 if (style) {
15677 var names = Object.keys(style);
15678
15679 for (var i = 0; i < names.length; i++) {
15680 var name = names[i];
15681 var val = style[name];
15682 var prop = props[name] || props[camel2dash(name)];
15683 var styleProp = this.parse(prop.name, val);
15684
15685 if (styleProp) {
15686 rstyle.push(styleProp);
15687 }
15688 }
15689 }
15690
15691 return rstyle;
15692};
15693
15694styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15695 var hash = seed.slice();
15696 var name, val, strVal, chVal;
15697 var i, j;
15698
15699 for (i = 0; i < propNames.length; i++) {
15700 name = propNames[i];
15701 val = ele.pstyle(name, false);
15702
15703 if (val == null) {
15704 continue;
15705 } else if (val.pfValue != null) {
15706 hash[0] = hashInt(chVal, hash[0]);
15707 hash[1] = hashIntAlt(chVal, hash[1]);
15708 } else {
15709 strVal = val.strValue;
15710
15711 for (j = 0; j < strVal.length; j++) {
15712 chVal = strVal.charCodeAt(j);
15713 hash[0] = hashInt(chVal, hash[0]);
15714 hash[1] = hashIntAlt(chVal, hash[1]);
15715 }
15716 }
15717 }
15718
15719 return hash;
15720};
15721
15722styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15723
15724var styfn$4 = {};
15725
15726styfn$4.appendFromJson = function (json) {
15727 var style = this;
15728
15729 for (var i = 0; i < json.length; i++) {
15730 var context = json[i];
15731 var selector = context.selector;
15732 var props = context.style || context.css;
15733 var names = Object.keys(props);
15734 style.selector(selector); // apply selector
15735
15736 for (var j = 0; j < names.length; j++) {
15737 var name = names[j];
15738 var value = props[name];
15739 style.css(name, value); // apply property
15740 }
15741 }
15742
15743 return style;
15744}; // accessible cy.style() function
15745
15746
15747styfn$4.fromJson = function (json) {
15748 var style = this;
15749 style.resetToDefault();
15750 style.appendFromJson(json);
15751 return style;
15752}; // get json from cy.style() api
15753
15754
15755styfn$4.json = function () {
15756 var json = [];
15757
15758 for (var i = this.defaultLength; i < this.length; i++) {
15759 var cxt = this[i];
15760 var selector = cxt.selector;
15761 var props = cxt.properties;
15762 var css = {};
15763
15764 for (var j = 0; j < props.length; j++) {
15765 var prop = props[j];
15766 css[prop.name] = prop.strValue;
15767 }
15768
15769 json.push({
15770 selector: !selector ? 'core' : selector.toString(),
15771 style: css
15772 });
15773 }
15774
15775 return json;
15776};
15777
15778var styfn$5 = {};
15779
15780styfn$5.appendFromString = function (string) {
15781 var self = this;
15782 var style = this;
15783 var remaining = '' + string;
15784 var selAndBlockStr;
15785 var blockRem;
15786 var propAndValStr; // remove comments from the style string
15787
15788 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15789
15790 function removeSelAndBlockFromRemaining() {
15791 // remove the parsed selector and block from the remaining text to parse
15792 if (remaining.length > selAndBlockStr.length) {
15793 remaining = remaining.substr(selAndBlockStr.length);
15794 } else {
15795 remaining = '';
15796 }
15797 }
15798
15799 function removePropAndValFromRem() {
15800 // remove the parsed property and value from the remaining block text to parse
15801 if (blockRem.length > propAndValStr.length) {
15802 blockRem = blockRem.substr(propAndValStr.length);
15803 } else {
15804 blockRem = '';
15805 }
15806 }
15807
15808 for (;;) {
15809 var nothingLeftToParse = remaining.match(/^\s*$/);
15810
15811 if (nothingLeftToParse) {
15812 break;
15813 }
15814
15815 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15816
15817 if (!selAndBlock) {
15818 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15819 break;
15820 }
15821
15822 selAndBlockStr = selAndBlock[0]; // parse the selector
15823
15824 var selectorStr = selAndBlock[1];
15825
15826 if (selectorStr !== 'core') {
15827 var selector = new Selector(selectorStr);
15828
15829 if (selector.invalid) {
15830 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15831
15832 removeSelAndBlockFromRemaining();
15833 continue;
15834 }
15835 } // parse the block of properties and values
15836
15837
15838 var blockStr = selAndBlock[2];
15839 var invalidBlock = false;
15840 blockRem = blockStr;
15841 var props = [];
15842
15843 for (;;) {
15844 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15845
15846 if (_nothingLeftToParse) {
15847 break;
15848 }
15849
15850 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15851
15852 if (!propAndVal) {
15853 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15854 invalidBlock = true;
15855 break;
15856 }
15857
15858 propAndValStr = propAndVal[0];
15859 var propStr = propAndVal[1];
15860 var valStr = propAndVal[2];
15861 var prop = self.properties[propStr];
15862
15863 if (!prop) {
15864 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15865
15866 removePropAndValFromRem();
15867 continue;
15868 }
15869
15870 var parsedProp = style.parse(propStr, valStr);
15871
15872 if (!parsedProp) {
15873 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15874
15875 removePropAndValFromRem();
15876 continue;
15877 }
15878
15879 props.push({
15880 name: propStr,
15881 val: valStr
15882 });
15883 removePropAndValFromRem();
15884 }
15885
15886 if (invalidBlock) {
15887 removeSelAndBlockFromRemaining();
15888 break;
15889 } // put the parsed block in the style
15890
15891
15892 style.selector(selectorStr);
15893
15894 for (var i = 0; i < props.length; i++) {
15895 var _prop = props[i];
15896 style.css(_prop.name, _prop.val);
15897 }
15898
15899 removeSelAndBlockFromRemaining();
15900 }
15901
15902 return style;
15903};
15904
15905styfn$5.fromString = function (string) {
15906 var style = this;
15907 style.resetToDefault();
15908 style.appendFromString(string);
15909 return style;
15910};
15911
15912var styfn$6 = {};
15913
15914(function () {
15915 var number = number$1;
15916 var rgba = rgbaNoBackRefs;
15917 var hsla = hslaNoBackRefs;
15918 var hex3$1 = hex3;
15919 var hex6$1 = hex6;
15920
15921 var data = function data(prefix) {
15922 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15923 };
15924
15925 var mapData = function mapData(prefix) {
15926 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15927 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15928 };
15929
15930 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15931
15932 styfn$6.types = {
15933 time: {
15934 number: true,
15935 min: 0,
15936 units: 's|ms',
15937 implicitUnits: 'ms'
15938 },
15939 percent: {
15940 number: true,
15941 min: 0,
15942 max: 100,
15943 units: '%',
15944 implicitUnits: '%'
15945 },
15946 percentages: {
15947 number: true,
15948 min: 0,
15949 max: 100,
15950 units: '%',
15951 implicitUnits: '%',
15952 multiple: true
15953 },
15954 zeroOneNumber: {
15955 number: true,
15956 min: 0,
15957 max: 1,
15958 unitless: true
15959 },
15960 zeroOneNumbers: {
15961 number: true,
15962 min: 0,
15963 max: 1,
15964 unitless: true,
15965 multiple: true
15966 },
15967 nOneOneNumber: {
15968 number: true,
15969 min: -1,
15970 max: 1,
15971 unitless: true
15972 },
15973 nonNegativeInt: {
15974 number: true,
15975 min: 0,
15976 integer: true,
15977 unitless: true
15978 },
15979 position: {
15980 enums: ['parent', 'origin']
15981 },
15982 nodeSize: {
15983 number: true,
15984 min: 0,
15985 enums: ['label']
15986 },
15987 number: {
15988 number: true,
15989 unitless: true
15990 },
15991 numbers: {
15992 number: true,
15993 unitless: true,
15994 multiple: true
15995 },
15996 positiveNumber: {
15997 number: true,
15998 unitless: true,
15999 min: 0,
16000 strictMin: true
16001 },
16002 size: {
16003 number: true,
16004 min: 0
16005 },
16006 bidirectionalSize: {
16007 number: true
16008 },
16009 // allows negative
16010 bidirectionalSizeMaybePercent: {
16011 number: true,
16012 allowPercent: true
16013 },
16014 // allows negative
16015 bidirectionalSizes: {
16016 number: true,
16017 multiple: true
16018 },
16019 // allows negative
16020 sizeMaybePercent: {
16021 number: true,
16022 min: 0,
16023 allowPercent: true
16024 },
16025 axisDirection: {
16026 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16027 },
16028 paddingRelativeTo: {
16029 enums: ['width', 'height', 'average', 'min', 'max']
16030 },
16031 bgWH: {
16032 number: true,
16033 min: 0,
16034 allowPercent: true,
16035 enums: ['auto'],
16036 multiple: true
16037 },
16038 bgPos: {
16039 number: true,
16040 allowPercent: true,
16041 multiple: true
16042 },
16043 bgRelativeTo: {
16044 enums: ['inner', 'include-padding'],
16045 multiple: true
16046 },
16047 bgRepeat: {
16048 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16049 multiple: true
16050 },
16051 bgFit: {
16052 enums: ['none', 'contain', 'cover'],
16053 multiple: true
16054 },
16055 bgCrossOrigin: {
16056 enums: ['anonymous', 'use-credentials'],
16057 multiple: true
16058 },
16059 bgClip: {
16060 enums: ['none', 'node'],
16061 multiple: true
16062 },
16063 color: {
16064 color: true
16065 },
16066 colors: {
16067 color: true,
16068 multiple: true
16069 },
16070 fill: {
16071 enums: ['solid', 'linear-gradient', 'radial-gradient']
16072 },
16073 bool: {
16074 enums: ['yes', 'no']
16075 },
16076 lineStyle: {
16077 enums: ['solid', 'dotted', 'dashed']
16078 },
16079 lineCap: {
16080 enums: ['butt', 'round', 'square']
16081 },
16082 borderStyle: {
16083 enums: ['solid', 'dotted', 'dashed', 'double']
16084 },
16085 curveStyle: {
16086 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16087 },
16088 fontFamily: {
16089 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16090 },
16091 fontStyle: {
16092 enums: ['italic', 'normal', 'oblique']
16093 },
16094 fontWeight: {
16095 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16096 },
16097 textDecoration: {
16098 enums: ['none', 'underline', 'overline', 'line-through']
16099 },
16100 textTransform: {
16101 enums: ['none', 'uppercase', 'lowercase']
16102 },
16103 textWrap: {
16104 enums: ['none', 'wrap', 'ellipsis']
16105 },
16106 textOverflowWrap: {
16107 enums: ['whitespace', 'anywhere']
16108 },
16109 textBackgroundShape: {
16110 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16111 },
16112 nodeShape: {
16113 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']
16114 },
16115 compoundIncludeLabels: {
16116 enums: ['include', 'exclude']
16117 },
16118 arrowShape: {
16119 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16120 },
16121 arrowFill: {
16122 enums: ['filled', 'hollow']
16123 },
16124 display: {
16125 enums: ['element', 'none']
16126 },
16127 visibility: {
16128 enums: ['hidden', 'visible']
16129 },
16130 zCompoundDepth: {
16131 enums: ['bottom', 'orphan', 'auto', 'top']
16132 },
16133 zIndexCompare: {
16134 enums: ['auto', 'manual']
16135 },
16136 valign: {
16137 enums: ['top', 'center', 'bottom']
16138 },
16139 halign: {
16140 enums: ['left', 'center', 'right']
16141 },
16142 justification: {
16143 enums: ['left', 'center', 'right', 'auto']
16144 },
16145 text: {
16146 string: true
16147 },
16148 data: {
16149 mapping: true,
16150 regex: data('data')
16151 },
16152 layoutData: {
16153 mapping: true,
16154 regex: data('layoutData')
16155 },
16156 scratch: {
16157 mapping: true,
16158 regex: data('scratch')
16159 },
16160 mapData: {
16161 mapping: true,
16162 regex: mapData('mapData')
16163 },
16164 mapLayoutData: {
16165 mapping: true,
16166 regex: mapData('mapLayoutData')
16167 },
16168 mapScratch: {
16169 mapping: true,
16170 regex: mapData('mapScratch')
16171 },
16172 fn: {
16173 mapping: true,
16174 fn: true
16175 },
16176 url: {
16177 regexes: urlRegexes,
16178 singleRegexMatchValue: true
16179 },
16180 urls: {
16181 regexes: urlRegexes,
16182 singleRegexMatchValue: true,
16183 multiple: true
16184 },
16185 propList: {
16186 propList: true
16187 },
16188 angle: {
16189 number: true,
16190 units: 'deg|rad',
16191 implicitUnits: 'rad'
16192 },
16193 textRotation: {
16194 number: true,
16195 units: 'deg|rad',
16196 implicitUnits: 'rad',
16197 enums: ['none', 'autorotate']
16198 },
16199 polygonPointList: {
16200 number: true,
16201 multiple: true,
16202 evenMultiple: true,
16203 min: -1,
16204 max: 1,
16205 unitless: true
16206 },
16207 edgeDistances: {
16208 enums: ['intersection', 'node-position']
16209 },
16210 edgeEndpoint: {
16211 number: true,
16212 multiple: true,
16213 units: '%|px|em|deg|rad',
16214 implicitUnits: 'px',
16215 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16216 singleEnum: true,
16217 validate: function validate(valArr, unitsArr) {
16218 switch (valArr.length) {
16219 case 2:
16220 // can be % or px only
16221 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16222
16223 case 1:
16224 // can be enum, deg, or rad only
16225 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16226
16227 default:
16228 return false;
16229 }
16230 }
16231 },
16232 easing: {
16233 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16234 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']
16235 },
16236 gradientDirection: {
16237 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']
16238 },
16239 boundsExpansion: {
16240 number: true,
16241 multiple: true,
16242 min: 0,
16243 validate: function validate(valArr) {
16244 var length = valArr.length;
16245 return length === 1 || length === 2 || length === 4;
16246 }
16247 }
16248 };
16249 var diff = {
16250 zeroNonZero: function zeroNonZero(val1, val2) {
16251 if ((val1 == null || val2 == null) && val1 !== val2) {
16252 return true; // null cases could represent any value
16253 }
16254
16255 if (val1 == 0 && val2 != 0) {
16256 return true;
16257 } else if (val1 != 0 && val2 == 0) {
16258 return true;
16259 } else {
16260 return false;
16261 }
16262 },
16263 any: function any(val1, val2) {
16264 return val1 != val2;
16265 },
16266 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16267 var empty1 = emptyString(str1);
16268 var empty2 = emptyString(str2);
16269 return empty1 && !empty2 || !empty1 && empty2;
16270 }
16271 }; // define visual style properties
16272 //
16273 // - n.b. adding a new group of props may require updates to updateStyleHints()
16274 // - adding new props to an existing group gets handled automatically
16275
16276 var t = styfn$6.types;
16277 var mainLabel = [{
16278 name: 'label',
16279 type: t.text,
16280 triggersBounds: diff.any,
16281 triggersZOrder: diff.emptyNonEmpty
16282 }, {
16283 name: 'text-rotation',
16284 type: t.textRotation,
16285 triggersBounds: diff.any
16286 }, {
16287 name: 'text-margin-x',
16288 type: t.bidirectionalSize,
16289 triggersBounds: diff.any
16290 }, {
16291 name: 'text-margin-y',
16292 type: t.bidirectionalSize,
16293 triggersBounds: diff.any
16294 }];
16295 var sourceLabel = [{
16296 name: 'source-label',
16297 type: t.text,
16298 triggersBounds: diff.any
16299 }, {
16300 name: 'source-text-rotation',
16301 type: t.textRotation,
16302 triggersBounds: diff.any
16303 }, {
16304 name: 'source-text-margin-x',
16305 type: t.bidirectionalSize,
16306 triggersBounds: diff.any
16307 }, {
16308 name: 'source-text-margin-y',
16309 type: t.bidirectionalSize,
16310 triggersBounds: diff.any
16311 }, {
16312 name: 'source-text-offset',
16313 type: t.size,
16314 triggersBounds: diff.any
16315 }];
16316 var targetLabel = [{
16317 name: 'target-label',
16318 type: t.text,
16319 triggersBounds: diff.any
16320 }, {
16321 name: 'target-text-rotation',
16322 type: t.textRotation,
16323 triggersBounds: diff.any
16324 }, {
16325 name: 'target-text-margin-x',
16326 type: t.bidirectionalSize,
16327 triggersBounds: diff.any
16328 }, {
16329 name: 'target-text-margin-y',
16330 type: t.bidirectionalSize,
16331 triggersBounds: diff.any
16332 }, {
16333 name: 'target-text-offset',
16334 type: t.size,
16335 triggersBounds: diff.any
16336 }];
16337 var labelDimensions = [{
16338 name: 'font-family',
16339 type: t.fontFamily,
16340 triggersBounds: diff.any
16341 }, {
16342 name: 'font-style',
16343 type: t.fontStyle,
16344 triggersBounds: diff.any
16345 }, {
16346 name: 'font-weight',
16347 type: t.fontWeight,
16348 triggersBounds: diff.any
16349 }, {
16350 name: 'font-size',
16351 type: t.size,
16352 triggersBounds: diff.any
16353 }, {
16354 name: 'text-transform',
16355 type: t.textTransform,
16356 triggersBounds: diff.any
16357 }, {
16358 name: 'text-wrap',
16359 type: t.textWrap,
16360 triggersBounds: diff.any
16361 }, {
16362 name: 'text-overflow-wrap',
16363 type: t.textOverflowWrap,
16364 triggersBounds: diff.any
16365 }, {
16366 name: 'text-max-width',
16367 type: t.size,
16368 triggersBounds: diff.any
16369 }, {
16370 name: 'text-outline-width',
16371 type: t.size,
16372 triggersBounds: diff.any
16373 }, {
16374 name: 'line-height',
16375 type: t.positiveNumber,
16376 triggersBounds: diff.any
16377 }];
16378 var commonLabel = [{
16379 name: 'text-valign',
16380 type: t.valign,
16381 triggersBounds: diff.any
16382 }, {
16383 name: 'text-halign',
16384 type: t.halign,
16385 triggersBounds: diff.any
16386 }, {
16387 name: 'color',
16388 type: t.color
16389 }, {
16390 name: 'text-outline-color',
16391 type: t.color
16392 }, {
16393 name: 'text-outline-opacity',
16394 type: t.zeroOneNumber
16395 }, {
16396 name: 'text-background-color',
16397 type: t.color
16398 }, {
16399 name: 'text-background-opacity',
16400 type: t.zeroOneNumber
16401 }, {
16402 name: 'text-background-padding',
16403 type: t.size,
16404 triggersBounds: diff.any
16405 }, {
16406 name: 'text-border-opacity',
16407 type: t.zeroOneNumber
16408 }, {
16409 name: 'text-border-color',
16410 type: t.color
16411 }, {
16412 name: 'text-border-width',
16413 type: t.size,
16414 triggersBounds: diff.any
16415 }, {
16416 name: 'text-border-style',
16417 type: t.borderStyle,
16418 triggersBounds: diff.any
16419 }, {
16420 name: 'text-background-shape',
16421 type: t.textBackgroundShape,
16422 triggersBounds: diff.any
16423 }, {
16424 name: 'text-justification',
16425 type: t.justification
16426 }];
16427 var behavior = [{
16428 name: 'events',
16429 type: t.bool
16430 }, {
16431 name: 'text-events',
16432 type: t.bool
16433 }];
16434 var visibility = [{
16435 name: 'display',
16436 type: t.display,
16437 triggersZOrder: diff.any,
16438 triggersBounds: diff.any,
16439 triggersBoundsOfParallelBeziers: true
16440 }, {
16441 name: 'visibility',
16442 type: t.visibility,
16443 triggersZOrder: diff.any
16444 }, {
16445 name: 'opacity',
16446 type: t.zeroOneNumber,
16447 triggersZOrder: diff.zeroNonZero
16448 }, {
16449 name: 'text-opacity',
16450 type: t.zeroOneNumber
16451 }, {
16452 name: 'min-zoomed-font-size',
16453 type: t.size
16454 }, {
16455 name: 'z-compound-depth',
16456 type: t.zCompoundDepth,
16457 triggersZOrder: diff.any
16458 }, {
16459 name: 'z-index-compare',
16460 type: t.zIndexCompare,
16461 triggersZOrder: diff.any
16462 }, {
16463 name: 'z-index',
16464 type: t.nonNegativeInt,
16465 triggersZOrder: diff.any
16466 }];
16467 var overlay = [{
16468 name: 'overlay-padding',
16469 type: t.size,
16470 triggersBounds: diff.any
16471 }, {
16472 name: 'overlay-color',
16473 type: t.color
16474 }, {
16475 name: 'overlay-opacity',
16476 type: t.zeroOneNumber,
16477 triggersBounds: diff.zeroNonZero
16478 }];
16479 var transition = [{
16480 name: 'transition-property',
16481 type: t.propList
16482 }, {
16483 name: 'transition-duration',
16484 type: t.time
16485 }, {
16486 name: 'transition-delay',
16487 type: t.time
16488 }, {
16489 name: 'transition-timing-function',
16490 type: t.easing
16491 }];
16492
16493 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16494 if (parsedProp.value === 'label') {
16495 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16496 } else {
16497 return parsedProp.pfValue;
16498 }
16499 };
16500
16501 var nodeBody = [{
16502 name: 'height',
16503 type: t.nodeSize,
16504 triggersBounds: diff.any,
16505 hashOverride: nodeSizeHashOverride
16506 }, {
16507 name: 'width',
16508 type: t.nodeSize,
16509 triggersBounds: diff.any,
16510 hashOverride: nodeSizeHashOverride
16511 }, {
16512 name: 'shape',
16513 type: t.nodeShape,
16514 triggersBounds: diff.any
16515 }, {
16516 name: 'shape-polygon-points',
16517 type: t.polygonPointList,
16518 triggersBounds: diff.any
16519 }, {
16520 name: 'background-color',
16521 type: t.color
16522 }, {
16523 name: 'background-fill',
16524 type: t.fill
16525 }, {
16526 name: 'background-opacity',
16527 type: t.zeroOneNumber
16528 }, {
16529 name: 'background-blacken',
16530 type: t.nOneOneNumber
16531 }, {
16532 name: 'background-gradient-stop-colors',
16533 type: t.colors
16534 }, {
16535 name: 'background-gradient-stop-positions',
16536 type: t.percentages
16537 }, {
16538 name: 'background-gradient-direction',
16539 type: t.gradientDirection
16540 }, {
16541 name: 'padding',
16542 type: t.sizeMaybePercent,
16543 triggersBounds: diff.any
16544 }, {
16545 name: 'padding-relative-to',
16546 type: t.paddingRelativeTo,
16547 triggersBounds: diff.any
16548 }, {
16549 name: 'bounds-expansion',
16550 type: t.boundsExpansion,
16551 triggersBounds: diff.any
16552 }];
16553 var nodeBorder = [{
16554 name: 'border-color',
16555 type: t.color
16556 }, {
16557 name: 'border-opacity',
16558 type: t.zeroOneNumber
16559 }, {
16560 name: 'border-width',
16561 type: t.size,
16562 triggersBounds: diff.any
16563 }, {
16564 name: 'border-style',
16565 type: t.borderStyle
16566 }];
16567 var backgroundImage = [{
16568 name: 'background-image',
16569 type: t.urls
16570 }, {
16571 name: 'background-image-crossorigin',
16572 type: t.bgCrossOrigin
16573 }, {
16574 name: 'background-image-opacity',
16575 type: t.zeroOneNumbers
16576 }, {
16577 name: 'background-position-x',
16578 type: t.bgPos
16579 }, {
16580 name: 'background-position-y',
16581 type: t.bgPos
16582 }, {
16583 name: 'background-width-relative-to',
16584 type: t.bgRelativeTo
16585 }, {
16586 name: 'background-height-relative-to',
16587 type: t.bgRelativeTo
16588 }, {
16589 name: 'background-repeat',
16590 type: t.bgRepeat
16591 }, {
16592 name: 'background-fit',
16593 type: t.bgFit
16594 }, {
16595 name: 'background-clip',
16596 type: t.bgClip
16597 }, {
16598 name: 'background-width',
16599 type: t.bgWH
16600 }, {
16601 name: 'background-height',
16602 type: t.bgWH
16603 }, {
16604 name: 'background-offset-x',
16605 type: t.bgPos
16606 }, {
16607 name: 'background-offset-y',
16608 type: t.bgPos
16609 }];
16610 var compound = [{
16611 name: 'position',
16612 type: t.position,
16613 triggersBounds: diff.any
16614 }, {
16615 name: 'compound-sizing-wrt-labels',
16616 type: t.compoundIncludeLabels,
16617 triggersBounds: diff.any
16618 }, {
16619 name: 'min-width',
16620 type: t.size,
16621 triggersBounds: diff.any
16622 }, {
16623 name: 'min-width-bias-left',
16624 type: t.sizeMaybePercent,
16625 triggersBounds: diff.any
16626 }, {
16627 name: 'min-width-bias-right',
16628 type: t.sizeMaybePercent,
16629 triggersBounds: diff.any
16630 }, {
16631 name: 'min-height',
16632 type: t.size,
16633 triggersBounds: diff.any
16634 }, {
16635 name: 'min-height-bias-top',
16636 type: t.sizeMaybePercent,
16637 triggersBounds: diff.any
16638 }, {
16639 name: 'min-height-bias-bottom',
16640 type: t.sizeMaybePercent,
16641 triggersBounds: diff.any
16642 }];
16643 var edgeLine = [{
16644 name: 'line-style',
16645 type: t.lineStyle
16646 }, {
16647 name: 'line-color',
16648 type: t.color
16649 }, {
16650 name: 'line-fill',
16651 type: t.fill
16652 }, {
16653 name: 'line-cap',
16654 type: t.lineCap
16655 }, {
16656 name: 'line-dash-pattern',
16657 type: t.numbers
16658 }, {
16659 name: 'line-dash-offset',
16660 type: t.number
16661 }, {
16662 name: 'line-gradient-stop-colors',
16663 type: t.colors
16664 }, {
16665 name: 'line-gradient-stop-positions',
16666 type: t.percentages
16667 }, {
16668 name: 'curve-style',
16669 type: t.curveStyle,
16670 triggersBounds: diff.any,
16671 triggersBoundsOfParallelBeziers: true
16672 }, {
16673 name: 'haystack-radius',
16674 type: t.zeroOneNumber,
16675 triggersBounds: diff.any
16676 }, {
16677 name: 'source-endpoint',
16678 type: t.edgeEndpoint,
16679 triggersBounds: diff.any
16680 }, {
16681 name: 'target-endpoint',
16682 type: t.edgeEndpoint,
16683 triggersBounds: diff.any
16684 }, {
16685 name: 'control-point-step-size',
16686 type: t.size,
16687 triggersBounds: diff.any
16688 }, {
16689 name: 'control-point-distances',
16690 type: t.bidirectionalSizes,
16691 triggersBounds: diff.any
16692 }, {
16693 name: 'control-point-weights',
16694 type: t.numbers,
16695 triggersBounds: diff.any
16696 }, {
16697 name: 'segment-distances',
16698 type: t.bidirectionalSizes,
16699 triggersBounds: diff.any
16700 }, {
16701 name: 'segment-weights',
16702 type: t.numbers,
16703 triggersBounds: diff.any
16704 }, {
16705 name: 'taxi-turn',
16706 type: t.bidirectionalSizeMaybePercent,
16707 triggersBounds: diff.any
16708 }, {
16709 name: 'taxi-turn-min-distance',
16710 type: t.size,
16711 triggersBounds: diff.any
16712 }, {
16713 name: 'taxi-direction',
16714 type: t.axisDirection,
16715 triggersBounds: diff.any
16716 }, {
16717 name: 'edge-distances',
16718 type: t.edgeDistances,
16719 triggersBounds: diff.any
16720 }, {
16721 name: 'arrow-scale',
16722 type: t.positiveNumber,
16723 triggersBounds: diff.any
16724 }, {
16725 name: 'loop-direction',
16726 type: t.angle,
16727 triggersBounds: diff.any
16728 }, {
16729 name: 'loop-sweep',
16730 type: t.angle,
16731 triggersBounds: diff.any
16732 }, {
16733 name: 'source-distance-from-node',
16734 type: t.size,
16735 triggersBounds: diff.any
16736 }, {
16737 name: 'target-distance-from-node',
16738 type: t.size,
16739 triggersBounds: diff.any
16740 }];
16741 var ghost = [{
16742 name: 'ghost',
16743 type: t.bool,
16744 triggersBounds: diff.any
16745 }, {
16746 name: 'ghost-offset-x',
16747 type: t.bidirectionalSize,
16748 triggersBounds: diff.any
16749 }, {
16750 name: 'ghost-offset-y',
16751 type: t.bidirectionalSize,
16752 triggersBounds: diff.any
16753 }, {
16754 name: 'ghost-opacity',
16755 type: t.zeroOneNumber
16756 }];
16757 var core = [{
16758 name: 'selection-box-color',
16759 type: t.color
16760 }, {
16761 name: 'selection-box-opacity',
16762 type: t.zeroOneNumber
16763 }, {
16764 name: 'selection-box-border-color',
16765 type: t.color
16766 }, {
16767 name: 'selection-box-border-width',
16768 type: t.size
16769 }, {
16770 name: 'active-bg-color',
16771 type: t.color
16772 }, {
16773 name: 'active-bg-opacity',
16774 type: t.zeroOneNumber
16775 }, {
16776 name: 'active-bg-size',
16777 type: t.size
16778 }, {
16779 name: 'outside-texture-bg-color',
16780 type: t.color
16781 }, {
16782 name: 'outside-texture-bg-opacity',
16783 type: t.zeroOneNumber
16784 }]; // pie backgrounds for nodes
16785
16786 var pie = [];
16787 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16788
16789 pie.push({
16790 name: 'pie-size',
16791 type: t.sizeMaybePercent
16792 });
16793
16794 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16795 pie.push({
16796 name: 'pie-' + i + '-background-color',
16797 type: t.color
16798 });
16799 pie.push({
16800 name: 'pie-' + i + '-background-size',
16801 type: t.percent
16802 });
16803 pie.push({
16804 name: 'pie-' + i + '-background-opacity',
16805 type: t.zeroOneNumber
16806 });
16807 } // edge arrows
16808
16809
16810 var edgeArrow = [];
16811 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16812 [{
16813 name: 'arrow-shape',
16814 type: t.arrowShape,
16815 triggersBounds: diff.any
16816 }, {
16817 name: 'arrow-color',
16818 type: t.color
16819 }, {
16820 name: 'arrow-fill',
16821 type: t.arrowFill
16822 }].forEach(function (prop) {
16823 arrowPrefixes.forEach(function (prefix) {
16824 var name = prefix + '-' + prop.name;
16825 var type = prop.type,
16826 triggersBounds = prop.triggersBounds;
16827 edgeArrow.push({
16828 name: name,
16829 type: type,
16830 triggersBounds: triggersBounds
16831 });
16832 });
16833 }, {});
16834 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16835 var propGroups = styfn$6.propertyGroups = {
16836 // common to all eles
16837 behavior: behavior,
16838 transition: transition,
16839 visibility: visibility,
16840 overlay: overlay,
16841 ghost: ghost,
16842 // labels
16843 commonLabel: commonLabel,
16844 labelDimensions: labelDimensions,
16845 mainLabel: mainLabel,
16846 sourceLabel: sourceLabel,
16847 targetLabel: targetLabel,
16848 // node props
16849 nodeBody: nodeBody,
16850 nodeBorder: nodeBorder,
16851 backgroundImage: backgroundImage,
16852 pie: pie,
16853 compound: compound,
16854 // edge props
16855 edgeLine: edgeLine,
16856 edgeArrow: edgeArrow,
16857 core: core
16858 };
16859 var propGroupNames = styfn$6.propertyGroupNames = {};
16860 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16861 propGroupKeys.forEach(function (key) {
16862 propGroupNames[key] = propGroups[key].map(function (prop) {
16863 return prop.name;
16864 });
16865 propGroups[key].forEach(function (prop) {
16866 return prop.groupKey = key;
16867 });
16868 }); // define aliases
16869
16870 var aliases = styfn$6.aliases = [{
16871 name: 'content',
16872 pointsTo: 'label'
16873 }, {
16874 name: 'control-point-distance',
16875 pointsTo: 'control-point-distances'
16876 }, {
16877 name: 'control-point-weight',
16878 pointsTo: 'control-point-weights'
16879 }, {
16880 name: 'edge-text-rotation',
16881 pointsTo: 'text-rotation'
16882 }, {
16883 name: 'padding-left',
16884 pointsTo: 'padding'
16885 }, {
16886 name: 'padding-right',
16887 pointsTo: 'padding'
16888 }, {
16889 name: 'padding-top',
16890 pointsTo: 'padding'
16891 }, {
16892 name: 'padding-bottom',
16893 pointsTo: 'padding'
16894 }]; // list of property names
16895
16896 styfn$6.propertyNames = props.map(function (p) {
16897 return p.name;
16898 }); // allow access of properties by name ( e.g. style.properties.height )
16899
16900 for (var _i = 0; _i < props.length; _i++) {
16901 var prop = props[_i];
16902 props[prop.name] = prop; // allow lookup by name
16903 } // map aliases
16904
16905
16906 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
16907 var alias = aliases[_i2];
16908 var pointsToProp = props[alias.pointsTo];
16909 var aliasProp = {
16910 name: alias.name,
16911 alias: true,
16912 pointsTo: pointsToProp
16913 }; // add alias prop for parsing
16914
16915 props.push(aliasProp);
16916 props[alias.name] = aliasProp; // allow lookup by name
16917 }
16918})();
16919
16920styfn$6.getDefaultProperty = function (name) {
16921 return this.getDefaultProperties()[name];
16922};
16923
16924styfn$6.getDefaultProperties = function () {
16925 var _p = this._private;
16926
16927 if (_p.defaultProperties != null) {
16928 return _p.defaultProperties;
16929 }
16930
16931 var rawProps = extend({
16932 // core props
16933 'selection-box-color': '#ddd',
16934 'selection-box-opacity': 0.65,
16935 'selection-box-border-color': '#aaa',
16936 'selection-box-border-width': 1,
16937 'active-bg-color': 'black',
16938 'active-bg-opacity': 0.15,
16939 'active-bg-size': 30,
16940 'outside-texture-bg-color': '#000',
16941 'outside-texture-bg-opacity': 0.125,
16942 // common node/edge props
16943 'events': 'yes',
16944 'text-events': 'no',
16945 'text-valign': 'top',
16946 'text-halign': 'center',
16947 'text-justification': 'auto',
16948 'line-height': 1,
16949 'color': '#000',
16950 'text-outline-color': '#000',
16951 'text-outline-width': 0,
16952 'text-outline-opacity': 1,
16953 'text-opacity': 1,
16954 'text-decoration': 'none',
16955 'text-transform': 'none',
16956 'text-wrap': 'none',
16957 'text-overflow-wrap': 'whitespace',
16958 'text-max-width': 9999,
16959 'text-background-color': '#000',
16960 'text-background-opacity': 0,
16961 'text-background-shape': 'rectangle',
16962 'text-background-padding': 0,
16963 'text-border-opacity': 0,
16964 'text-border-width': 0,
16965 'text-border-style': 'solid',
16966 'text-border-color': '#000',
16967 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
16968 'font-style': 'normal',
16969 'font-weight': 'normal',
16970 'font-size': 16,
16971 'min-zoomed-font-size': 0,
16972 'text-rotation': 'none',
16973 'source-text-rotation': 'none',
16974 'target-text-rotation': 'none',
16975 'visibility': 'visible',
16976 'display': 'element',
16977 'opacity': 1,
16978 'z-compound-depth': 'auto',
16979 'z-index-compare': 'auto',
16980 'z-index': 0,
16981 'label': '',
16982 'text-margin-x': 0,
16983 'text-margin-y': 0,
16984 'source-label': '',
16985 'source-text-offset': 0,
16986 'source-text-margin-x': 0,
16987 'source-text-margin-y': 0,
16988 'target-label': '',
16989 'target-text-offset': 0,
16990 'target-text-margin-x': 0,
16991 'target-text-margin-y': 0,
16992 'overlay-opacity': 0,
16993 'overlay-color': '#000',
16994 'overlay-padding': 10,
16995 'transition-property': 'none',
16996 'transition-duration': 0,
16997 'transition-delay': 0,
16998 'transition-timing-function': 'linear',
16999 // node props
17000 'background-blacken': 0,
17001 'background-color': '#999',
17002 'background-fill': 'solid',
17003 'background-opacity': 1,
17004 'background-image': 'none',
17005 'background-image-crossorigin': 'anonymous',
17006 'background-image-opacity': 1,
17007 'background-position-x': '50%',
17008 'background-position-y': '50%',
17009 'background-offset-x': 0,
17010 'background-offset-y': 0,
17011 'background-width-relative-to': 'include-padding',
17012 'background-height-relative-to': 'include-padding',
17013 'background-repeat': 'no-repeat',
17014 'background-fit': 'none',
17015 'background-clip': 'node',
17016 'background-width': 'auto',
17017 'background-height': 'auto',
17018 'border-color': '#000',
17019 'border-opacity': 1,
17020 'border-width': 0,
17021 'border-style': 'solid',
17022 'height': 30,
17023 'width': 30,
17024 'shape': 'ellipse',
17025 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17026 'bounds-expansion': 0,
17027 // node gradient
17028 'background-gradient-direction': 'to-bottom',
17029 'background-gradient-stop-colors': '#999',
17030 'background-gradient-stop-positions': '0%',
17031 // ghost props
17032 'ghost': 'no',
17033 'ghost-offset-y': 0,
17034 'ghost-offset-x': 0,
17035 'ghost-opacity': 0,
17036 // compound props
17037 'padding': 0,
17038 'padding-relative-to': 'width',
17039 'position': 'origin',
17040 'compound-sizing-wrt-labels': 'include',
17041 'min-width': 0,
17042 'min-width-bias-left': 0,
17043 'min-width-bias-right': 0,
17044 'min-height': 0,
17045 'min-height-bias-top': 0,
17046 'min-height-bias-bottom': 0
17047 }, {
17048 // node pie bg
17049 'pie-size': '100%'
17050 }, [{
17051 name: 'pie-{{i}}-background-color',
17052 value: 'black'
17053 }, {
17054 name: 'pie-{{i}}-background-size',
17055 value: '0%'
17056 }, {
17057 name: 'pie-{{i}}-background-opacity',
17058 value: 1
17059 }].reduce(function (css, prop) {
17060 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17061 var name = prop.name.replace('{{i}}', i);
17062 var val = prop.value;
17063 css[name] = val;
17064 }
17065
17066 return css;
17067 }, {}), {
17068 // edge props
17069 'line-style': 'solid',
17070 'line-color': '#999',
17071 'line-fill': 'solid',
17072 'line-cap': 'butt',
17073 'line-gradient-stop-colors': '#999',
17074 'line-gradient-stop-positions': '0%',
17075 'control-point-step-size': 40,
17076 'control-point-weights': 0.5,
17077 'segment-weights': 0.5,
17078 'segment-distances': 20,
17079 'taxi-turn': '50%',
17080 'taxi-turn-min-distance': 10,
17081 'taxi-direction': 'auto',
17082 'edge-distances': 'intersection',
17083 'curve-style': 'haystack',
17084 'haystack-radius': 0,
17085 'arrow-scale': 1,
17086 'loop-direction': '-45deg',
17087 'loop-sweep': '-90deg',
17088 'source-distance-from-node': 0,
17089 'target-distance-from-node': 0,
17090 'source-endpoint': 'outside-to-node',
17091 'target-endpoint': 'outside-to-node',
17092 'line-dash-pattern': [6, 3],
17093 'line-dash-offset': 0
17094 }, [{
17095 name: 'arrow-shape',
17096 value: 'none'
17097 }, {
17098 name: 'arrow-color',
17099 value: '#999'
17100 }, {
17101 name: 'arrow-fill',
17102 value: 'filled'
17103 }].reduce(function (css, prop) {
17104 styfn$6.arrowPrefixes.forEach(function (prefix) {
17105 var name = prefix + '-' + prop.name;
17106 var val = prop.value;
17107 css[name] = val;
17108 });
17109 return css;
17110 }, {}));
17111 var parsedProps = {};
17112
17113 for (var i = 0; i < this.properties.length; i++) {
17114 var prop = this.properties[i];
17115
17116 if (prop.pointsTo) {
17117 continue;
17118 }
17119
17120 var name = prop.name;
17121 var val = rawProps[name];
17122 var parsedProp = this.parse(name, val);
17123 parsedProps[name] = parsedProp;
17124 }
17125
17126 _p.defaultProperties = parsedProps;
17127 return _p.defaultProperties;
17128};
17129
17130styfn$6.addDefaultStylesheet = function () {
17131 this.selector(':parent').css({
17132 'shape': 'rectangle',
17133 'padding': 10,
17134 'background-color': '#eee',
17135 'border-color': '#ccc',
17136 'border-width': 1
17137 }).selector('edge').css({
17138 'width': 3
17139 }).selector(':loop').css({
17140 'curve-style': 'bezier'
17141 }).selector('edge:compound').css({
17142 'curve-style': 'bezier',
17143 'source-endpoint': 'outside-to-line',
17144 'target-endpoint': 'outside-to-line'
17145 }).selector(':selected').css({
17146 'background-color': '#0169D9',
17147 'line-color': '#0169D9',
17148 'source-arrow-color': '#0169D9',
17149 'target-arrow-color': '#0169D9',
17150 'mid-source-arrow-color': '#0169D9',
17151 'mid-target-arrow-color': '#0169D9'
17152 }).selector(':parent:selected').css({
17153 'background-color': '#CCE1F9',
17154 'border-color': '#aec8e5'
17155 }).selector(':active').css({
17156 'overlay-color': 'black',
17157 'overlay-padding': 10,
17158 'overlay-opacity': 0.25
17159 });
17160 this.defaultLength = this.length;
17161};
17162
17163var styfn$7 = {}; // a caching layer for property parsing
17164
17165styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17166 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17167
17168 if (fn(value)) {
17169 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17170 }
17171
17172 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17173 var bypassKey = propIsBypass ? 't' : 'f';
17174 var valueKey = '' + value;
17175 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17176 var propCache = self.propCache = self.propCache || [];
17177 var ret;
17178
17179 if (!(ret = propCache[argHash])) {
17180 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17181 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17182 // - mappings can't be shared b/c mappings are per-element
17183
17184
17185 if (propIsBypass || propIsFlat === 'mapping') {
17186 // need a copy since props are mutated later in their lifecycles
17187 ret = copy(ret);
17188
17189 if (ret) {
17190 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17191 }
17192 }
17193
17194 return ret;
17195};
17196
17197styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17198 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17199
17200 if (!prop && value != null) {
17201 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17202 }
17203
17204 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17205 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17206 }
17207
17208 return prop;
17209}; // parse a property; return null on invalid; return parsed property otherwise
17210// fields :
17211// - name : the name of the property
17212// - value : the parsed, native-typed value of the property
17213// - strValue : a string value that represents the property value in valid css
17214// - bypass : true iff the property is a bypass property
17215
17216
17217styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17218 var self = this;
17219 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17220
17221 var property = self.properties[name];
17222 var passedValue = value;
17223 var types = self.types;
17224
17225 if (!property) {
17226 return null;
17227 } // return null on property of unknown name
17228
17229
17230 if (value === undefined) {
17231 return null;
17232 } // can't assign undefined
17233 // the property may be an alias
17234
17235
17236 if (property.alias) {
17237 property = property.pointsTo;
17238 name = property.name;
17239 }
17240
17241 var valueIsString = string(value);
17242
17243 if (valueIsString) {
17244 // trim the value to make parsing easier
17245 value = value.trim();
17246 }
17247
17248 var type = property.type;
17249
17250 if (!type) {
17251 return null;
17252 } // no type, no luck
17253 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17254
17255
17256 if (propIsBypass && (value === '' || value === null)) {
17257 return {
17258 name: name,
17259 value: value,
17260 bypass: true,
17261 deleteBypass: true
17262 };
17263 } // check if value is a function used as a mapper
17264
17265
17266 if (fn(value)) {
17267 return {
17268 name: name,
17269 value: value,
17270 strValue: 'fn',
17271 mapped: types.fn,
17272 bypass: propIsBypass
17273 };
17274 } // check if value is mapped
17275
17276
17277 var data, mapData;
17278
17279 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))) {
17280 if (propIsBypass) {
17281 return false;
17282 } // mappers not allowed in bypass
17283
17284
17285 var mapped = types.data;
17286 return {
17287 name: name,
17288 value: data,
17289 strValue: '' + value,
17290 mapped: mapped,
17291 field: data[1],
17292 bypass: propIsBypass
17293 };
17294 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17295 if (propIsBypass) {
17296 return false;
17297 } // mappers not allowed in bypass
17298
17299
17300 if (type.multiple) {
17301 return false;
17302 } // impossible to map to num
17303
17304
17305 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17306
17307 if (!(type.color || type.number)) {
17308 return false;
17309 }
17310
17311 var valueMin = this.parse(name, mapData[4]); // parse to validate
17312
17313 if (!valueMin || valueMin.mapped) {
17314 return false;
17315 } // can't be invalid or mapped
17316
17317
17318 var valueMax = this.parse(name, mapData[5]); // parse to validate
17319
17320 if (!valueMax || valueMax.mapped) {
17321 return false;
17322 } // can't be invalid or mapped
17323 // check if valueMin and valueMax are the same
17324
17325
17326 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17327 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17328 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17329 } else if (type.color) {
17330 var c1 = valueMin.value;
17331 var c2 = valueMax.value;
17332 var same = c1[0] === c2[0] // red
17333 && c1[1] === c2[1] // green
17334 && c1[2] === c2[2] // blue
17335 && ( // optional alpha
17336 c1[3] === c2[3] // same alpha outright
17337 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17338 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17339 );
17340
17341 if (same) {
17342 return false;
17343 } // can't make a mapper without a range
17344
17345 }
17346
17347 return {
17348 name: name,
17349 value: mapData,
17350 strValue: '' + value,
17351 mapped: _mapped,
17352 field: mapData[1],
17353 fieldMin: parseFloat(mapData[2]),
17354 // min & max are numeric
17355 fieldMax: parseFloat(mapData[3]),
17356 valueMin: valueMin.value,
17357 valueMax: valueMax.value,
17358 bypass: propIsBypass
17359 };
17360 }
17361
17362 if (type.multiple && propIsFlat !== 'multiple') {
17363 var vals;
17364
17365 if (valueIsString) {
17366 vals = value.split(/\s+/);
17367 } else if (array(value)) {
17368 vals = value;
17369 } else {
17370 vals = [value];
17371 }
17372
17373 if (type.evenMultiple && vals.length % 2 !== 0) {
17374 return null;
17375 }
17376
17377 var valArr = [];
17378 var unitsArr = [];
17379 var pfValArr = [];
17380 var strVal = '';
17381 var hasEnum = false;
17382
17383 for (var i = 0; i < vals.length; i++) {
17384 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17385 hasEnum = hasEnum || string(p.value);
17386 valArr.push(p.value);
17387 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17388 unitsArr.push(p.units);
17389 strVal += (i > 0 ? ' ' : '') + p.strValue;
17390 }
17391
17392 if (type.validate && !type.validate(valArr, unitsArr)) {
17393 return null;
17394 }
17395
17396 if (type.singleEnum && hasEnum) {
17397 if (valArr.length === 1 && string(valArr[0])) {
17398 return {
17399 name: name,
17400 value: valArr[0],
17401 strValue: valArr[0],
17402 bypass: propIsBypass
17403 };
17404 } else {
17405 return null;
17406 }
17407 }
17408
17409 return {
17410 name: name,
17411 value: valArr,
17412 pfValue: pfValArr,
17413 strValue: strVal,
17414 bypass: propIsBypass,
17415 units: unitsArr
17416 };
17417 } // several types also allow enums
17418
17419
17420 var checkEnums = function checkEnums() {
17421 for (var _i = 0; _i < type.enums.length; _i++) {
17422 var en = type.enums[_i];
17423
17424 if (en === value) {
17425 return {
17426 name: name,
17427 value: value,
17428 strValue: '' + value,
17429 bypass: propIsBypass
17430 };
17431 }
17432 }
17433
17434 return null;
17435 }; // check the type and return the appropriate object
17436
17437
17438 if (type.number) {
17439 var units;
17440 var implicitUnits = 'px'; // not set => px
17441
17442 if (type.units) {
17443 // use specified units if set
17444 units = type.units;
17445 }
17446
17447 if (type.implicitUnits) {
17448 implicitUnits = type.implicitUnits;
17449 }
17450
17451 if (!type.unitless) {
17452 if (valueIsString) {
17453 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17454
17455 if (units) {
17456 unitsRegex = units;
17457 } // only allow explicit units if so set
17458
17459
17460 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17461
17462 if (match) {
17463 value = match[1];
17464 units = match[2] || implicitUnits;
17465 }
17466 } else if (!units || type.implicitUnits) {
17467 units = implicitUnits; // implicitly px if unspecified
17468 }
17469 }
17470
17471 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17472
17473 if (isNaN(value) && type.enums === undefined) {
17474 return null;
17475 } // check if this number type also accepts special keywords in place of numbers
17476 // (i.e. `left`, `auto`, etc)
17477
17478
17479 if (isNaN(value) && type.enums !== undefined) {
17480 value = passedValue;
17481 return checkEnums();
17482 } // check if value must be an integer
17483
17484
17485 if (type.integer && !integer(value)) {
17486 return null;
17487 } // check value is within range
17488
17489
17490 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17491 return null;
17492 }
17493
17494 var ret = {
17495 name: name,
17496 value: value,
17497 strValue: '' + value + (units ? units : ''),
17498 units: units,
17499 bypass: propIsBypass
17500 }; // normalise value in pixels
17501
17502 if (type.unitless || units !== 'px' && units !== 'em') {
17503 ret.pfValue = value;
17504 } else {
17505 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17506 } // normalise value in ms
17507
17508
17509 if (units === 'ms' || units === 's') {
17510 ret.pfValue = units === 'ms' ? value : 1000 * value;
17511 } // normalise value in rad
17512
17513
17514 if (units === 'deg' || units === 'rad') {
17515 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17516 } // normalize value in %
17517
17518
17519 if (units === '%') {
17520 ret.pfValue = value / 100;
17521 }
17522
17523 return ret;
17524 } else if (type.propList) {
17525 var props = [];
17526 var propsStr = '' + value;
17527
17528 if (propsStr === 'none') ; else {
17529 // go over each prop
17530 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17531
17532 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17533 var propName = propsSplit[_i2].trim();
17534
17535 if (self.properties[propName]) {
17536 props.push(propName);
17537 } else {
17538 warn('`' + propName + '` is not a valid property name');
17539 }
17540 }
17541
17542 if (props.length === 0) {
17543 return null;
17544 }
17545 }
17546
17547 return {
17548 name: name,
17549 value: props,
17550 strValue: props.length === 0 ? 'none' : props.join(' '),
17551 bypass: propIsBypass
17552 };
17553 } else if (type.color) {
17554 var tuple = color2tuple(value);
17555
17556 if (!tuple) {
17557 return null;
17558 }
17559
17560 return {
17561 name: name,
17562 value: tuple,
17563 pfValue: tuple,
17564 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17565 // n.b. no spaces b/c of multiple support
17566 bypass: propIsBypass
17567 };
17568 } else if (type.regex || type.regexes) {
17569 // first check enums
17570 if (type.enums) {
17571 var enumProp = checkEnums();
17572
17573 if (enumProp) {
17574 return enumProp;
17575 }
17576 }
17577
17578 var regexes = type.regexes ? type.regexes : [type.regex];
17579
17580 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17581 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17582
17583 var m = regex.exec(value);
17584
17585 if (m) {
17586 // regex matches
17587 return {
17588 name: name,
17589 value: type.singleRegexMatchValue ? m[1] : m,
17590 strValue: '' + value,
17591 bypass: propIsBypass
17592 };
17593 }
17594 }
17595
17596 return null; // didn't match any
17597 } else if (type.string) {
17598 // just return
17599 return {
17600 name: name,
17601 value: '' + value,
17602 strValue: '' + value,
17603 bypass: propIsBypass
17604 };
17605 } else if (type.enums) {
17606 // check enums last because it's a combo type in others
17607 return checkEnums();
17608 } else {
17609 return null; // not a type we can handle
17610 }
17611};
17612
17613var Style = function Style(cy) {
17614 if (!(this instanceof Style)) {
17615 return new Style(cy);
17616 }
17617
17618 if (!core(cy)) {
17619 error('A style must have a core reference');
17620 return;
17621 }
17622
17623 this._private = {
17624 cy: cy,
17625 coreStyle: {}
17626 };
17627 this.length = 0;
17628 this.resetToDefault();
17629};
17630
17631var styfn$8 = Style.prototype;
17632
17633styfn$8.instanceString = function () {
17634 return 'style';
17635}; // remove all contexts
17636
17637
17638styfn$8.clear = function () {
17639 for (var i = 0; i < this.length; i++) {
17640 this[i] = undefined;
17641 }
17642
17643 this.length = 0;
17644 var _p = this._private;
17645 _p.newStyle = true;
17646 return this; // chaining
17647};
17648
17649styfn$8.resetToDefault = function () {
17650 this.clear();
17651 this.addDefaultStylesheet();
17652 return this;
17653}; // builds a style object for the 'core' selector
17654
17655
17656styfn$8.core = function (propName) {
17657 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17658}; // create a new context from the specified selector string and switch to that context
17659
17660
17661styfn$8.selector = function (selectorStr) {
17662 // 'core' is a special case and does not need a selector
17663 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17664 var i = this.length++; // new context means new index
17665
17666 this[i] = {
17667 selector: selector,
17668 properties: [],
17669 mappedProperties: [],
17670 index: i
17671 };
17672 return this; // chaining
17673}; // add one or many css rules to the current context
17674
17675
17676styfn$8.css = function () {
17677 var self = this;
17678 var args = arguments;
17679
17680 if (args.length === 1) {
17681 var map = args[0];
17682
17683 for (var i = 0; i < self.properties.length; i++) {
17684 var prop = self.properties[i];
17685 var mapVal = map[prop.name];
17686
17687 if (mapVal === undefined) {
17688 mapVal = map[dash2camel(prop.name)];
17689 }
17690
17691 if (mapVal !== undefined) {
17692 this.cssRule(prop.name, mapVal);
17693 }
17694 }
17695 } else if (args.length === 2) {
17696 this.cssRule(args[0], args[1]);
17697 } // do nothing if args are invalid
17698
17699
17700 return this; // chaining
17701};
17702
17703styfn$8.style = styfn$8.css; // add a single css rule to the current context
17704
17705styfn$8.cssRule = function (name, value) {
17706 // name-value pair
17707 var property = this.parse(name, value); // add property to current context if valid
17708
17709 if (property) {
17710 var i = this.length - 1;
17711 this[i].properties.push(property);
17712 this[i].properties[property.name] = property; // allow access by name as well
17713
17714 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17715 this._private.hasPie = true;
17716 }
17717
17718 if (property.mapped) {
17719 this[i].mappedProperties.push(property);
17720 } // add to core style if necessary
17721
17722
17723 var currentSelectorIsCore = !this[i].selector;
17724
17725 if (currentSelectorIsCore) {
17726 this._private.coreStyle[property.name] = property;
17727 }
17728 }
17729
17730 return this; // chaining
17731};
17732
17733styfn$8.append = function (style) {
17734 if (stylesheet(style)) {
17735 style.appendToStyle(this);
17736 } else if (array(style)) {
17737 this.appendFromJson(style);
17738 } else if (string(style)) {
17739 this.appendFromString(style);
17740 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17741
17742
17743 return this;
17744}; // static function
17745
17746
17747Style.fromJson = function (cy, json) {
17748 var style = new Style(cy);
17749 style.fromJson(json);
17750 return style;
17751};
17752
17753Style.fromString = function (cy, string) {
17754 return new Style(cy).fromString(string);
17755};
17756
17757[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17758 extend(styfn$8, props);
17759});
17760Style.types = styfn$8.types;
17761Style.properties = styfn$8.properties;
17762Style.propertyGroups = styfn$8.propertyGroups;
17763Style.propertyGroupNames = styfn$8.propertyGroupNames;
17764Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17765
17766var corefn$7 = {
17767 style: function style(newStyle) {
17768 if (newStyle) {
17769 var s = this.setStyle(newStyle);
17770 s.update();
17771 }
17772
17773 return this._private.style;
17774 },
17775 setStyle: function setStyle(style) {
17776 var _p = this._private;
17777
17778 if (stylesheet(style)) {
17779 _p.style = style.generateStyle(this);
17780 } else if (array(style)) {
17781 _p.style = Style.fromJson(this, style);
17782 } else if (string(style)) {
17783 _p.style = Style.fromString(this, style);
17784 } else {
17785 _p.style = Style(this);
17786 }
17787
17788 return _p.style;
17789 }
17790};
17791
17792var defaultSelectionType = 'single';
17793var corefn$8 = {
17794 autolock: function autolock(bool) {
17795 if (bool !== undefined) {
17796 this._private.autolock = bool ? true : false;
17797 } else {
17798 return this._private.autolock;
17799 }
17800
17801 return this; // chaining
17802 },
17803 autoungrabify: function autoungrabify(bool) {
17804 if (bool !== undefined) {
17805 this._private.autoungrabify = bool ? true : false;
17806 } else {
17807 return this._private.autoungrabify;
17808 }
17809
17810 return this; // chaining
17811 },
17812 autounselectify: function autounselectify(bool) {
17813 if (bool !== undefined) {
17814 this._private.autounselectify = bool ? true : false;
17815 } else {
17816 return this._private.autounselectify;
17817 }
17818
17819 return this; // chaining
17820 },
17821 selectionType: function selectionType(selType) {
17822 var _p = this._private;
17823
17824 if (_p.selectionType == null) {
17825 _p.selectionType = defaultSelectionType;
17826 }
17827
17828 if (selType !== undefined) {
17829 if (selType === 'additive' || selType === 'single') {
17830 _p.selectionType = selType;
17831 }
17832 } else {
17833 return _p.selectionType;
17834 }
17835
17836 return this;
17837 },
17838 panningEnabled: function panningEnabled(bool) {
17839 if (bool !== undefined) {
17840 this._private.panningEnabled = bool ? true : false;
17841 } else {
17842 return this._private.panningEnabled;
17843 }
17844
17845 return this; // chaining
17846 },
17847 userPanningEnabled: function userPanningEnabled(bool) {
17848 if (bool !== undefined) {
17849 this._private.userPanningEnabled = bool ? true : false;
17850 } else {
17851 return this._private.userPanningEnabled;
17852 }
17853
17854 return this; // chaining
17855 },
17856 zoomingEnabled: function zoomingEnabled(bool) {
17857 if (bool !== undefined) {
17858 this._private.zoomingEnabled = bool ? true : false;
17859 } else {
17860 return this._private.zoomingEnabled;
17861 }
17862
17863 return this; // chaining
17864 },
17865 userZoomingEnabled: function userZoomingEnabled(bool) {
17866 if (bool !== undefined) {
17867 this._private.userZoomingEnabled = bool ? true : false;
17868 } else {
17869 return this._private.userZoomingEnabled;
17870 }
17871
17872 return this; // chaining
17873 },
17874 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17875 if (bool !== undefined) {
17876 this._private.boxSelectionEnabled = bool ? true : false;
17877 } else {
17878 return this._private.boxSelectionEnabled;
17879 }
17880
17881 return this; // chaining
17882 },
17883 pan: function pan() {
17884 var args = arguments;
17885 var pan = this._private.pan;
17886 var dim, val, dims, x, y;
17887
17888 switch (args.length) {
17889 case 0:
17890 // .pan()
17891 return pan;
17892
17893 case 1:
17894 if (string(args[0])) {
17895 // .pan('x')
17896 dim = args[0];
17897 return pan[dim];
17898 } else if (plainObject(args[0])) {
17899 // .pan({ x: 0, y: 100 })
17900 if (!this._private.panningEnabled) {
17901 return this;
17902 }
17903
17904 dims = args[0];
17905 x = dims.x;
17906 y = dims.y;
17907
17908 if (number(x)) {
17909 pan.x = x;
17910 }
17911
17912 if (number(y)) {
17913 pan.y = y;
17914 }
17915
17916 this.emit('pan viewport');
17917 }
17918
17919 break;
17920
17921 case 2:
17922 // .pan('x', 100)
17923 if (!this._private.panningEnabled) {
17924 return this;
17925 }
17926
17927 dim = args[0];
17928 val = args[1];
17929
17930 if ((dim === 'x' || dim === 'y') && number(val)) {
17931 pan[dim] = val;
17932 }
17933
17934 this.emit('pan viewport');
17935 break;
17936 // invalid
17937 }
17938
17939 this.notify('viewport');
17940 return this; // chaining
17941 },
17942 panBy: function panBy(arg0, arg1) {
17943 var args = arguments;
17944 var pan = this._private.pan;
17945 var dim, val, dims, x, y;
17946
17947 if (!this._private.panningEnabled) {
17948 return this;
17949 }
17950
17951 switch (args.length) {
17952 case 1:
17953 if (plainObject(arg0)) {
17954 // .panBy({ x: 0, y: 100 })
17955 dims = args[0];
17956 x = dims.x;
17957 y = dims.y;
17958
17959 if (number(x)) {
17960 pan.x += x;
17961 }
17962
17963 if (number(y)) {
17964 pan.y += y;
17965 }
17966
17967 this.emit('pan viewport');
17968 }
17969
17970 break;
17971
17972 case 2:
17973 // .panBy('x', 100)
17974 dim = arg0;
17975 val = arg1;
17976
17977 if ((dim === 'x' || dim === 'y') && number(val)) {
17978 pan[dim] += val;
17979 }
17980
17981 this.emit('pan viewport');
17982 break;
17983 // invalid
17984 }
17985
17986 this.notify('viewport');
17987 return this; // chaining
17988 },
17989 fit: function fit(elements, padding) {
17990 var viewportState = this.getFitViewport(elements, padding);
17991
17992 if (viewportState) {
17993 var _p = this._private;
17994 _p.zoom = viewportState.zoom;
17995 _p.pan = viewportState.pan;
17996 this.emit('pan zoom viewport');
17997 this.notify('viewport');
17998 }
17999
18000 return this; // chaining
18001 },
18002 getFitViewport: function getFitViewport(elements, padding) {
18003 if (number(elements) && padding === undefined) {
18004 // elements is optional
18005 padding = elements;
18006 elements = undefined;
18007 }
18008
18009 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18010 return;
18011 }
18012
18013 var bb;
18014
18015 if (string(elements)) {
18016 var sel = elements;
18017 elements = this.$(sel);
18018 } else if (boundingBox(elements)) {
18019 // assume bb
18020 var bbe = elements;
18021 bb = {
18022 x1: bbe.x1,
18023 y1: bbe.y1,
18024 x2: bbe.x2,
18025 y2: bbe.y2
18026 };
18027 bb.w = bb.x2 - bb.x1;
18028 bb.h = bb.y2 - bb.y1;
18029 } else if (!elementOrCollection(elements)) {
18030 elements = this.mutableElements();
18031 }
18032
18033 if (elementOrCollection(elements) && elements.empty()) {
18034 return;
18035 } // can't fit to nothing
18036
18037
18038 bb = bb || elements.boundingBox();
18039 var w = this.width();
18040 var h = this.height();
18041 var zoom;
18042 padding = number(padding) ? padding : 0;
18043
18044 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18045 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18046
18047 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18048 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18049 var pan = {
18050 // now pan to middle
18051 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18052 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18053 };
18054 return {
18055 zoom: zoom,
18056 pan: pan
18057 };
18058 }
18059
18060 return;
18061 },
18062 zoomRange: function zoomRange(min, max) {
18063 var _p = this._private;
18064
18065 if (max == null) {
18066 var opts = min;
18067 min = opts.min;
18068 max = opts.max;
18069 }
18070
18071 if (number(min) && number(max) && min <= max) {
18072 _p.minZoom = min;
18073 _p.maxZoom = max;
18074 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18075 _p.minZoom = min;
18076 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18077 _p.maxZoom = max;
18078 }
18079
18080 return this;
18081 },
18082 minZoom: function minZoom(zoom) {
18083 if (zoom === undefined) {
18084 return this._private.minZoom;
18085 } else {
18086 return this.zoomRange({
18087 min: zoom
18088 });
18089 }
18090 },
18091 maxZoom: function maxZoom(zoom) {
18092 if (zoom === undefined) {
18093 return this._private.maxZoom;
18094 } else {
18095 return this.zoomRange({
18096 max: zoom
18097 });
18098 }
18099 },
18100 getZoomedViewport: function getZoomedViewport(params) {
18101 var _p = this._private;
18102 var currentPan = _p.pan;
18103 var currentZoom = _p.zoom;
18104 var pos; // in rendered px
18105
18106 var zoom;
18107 var bail = false;
18108
18109 if (!_p.zoomingEnabled) {
18110 // zooming disabled
18111 bail = true;
18112 }
18113
18114 if (number(params)) {
18115 // then set the zoom
18116 zoom = params;
18117 } else if (plainObject(params)) {
18118 // then zoom about a point
18119 zoom = params.level;
18120
18121 if (params.position != null) {
18122 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18123 } else if (params.renderedPosition != null) {
18124 pos = params.renderedPosition;
18125 }
18126
18127 if (pos != null && !_p.panningEnabled) {
18128 // panning disabled
18129 bail = true;
18130 }
18131 } // crop zoom
18132
18133
18134 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18135 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18136
18137 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18138 return null;
18139 }
18140
18141 if (pos != null) {
18142 // set zoom about position
18143 var pan1 = currentPan;
18144 var zoom1 = currentZoom;
18145 var zoom2 = zoom;
18146 var pan2 = {
18147 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18148 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18149 };
18150 return {
18151 zoomed: true,
18152 panned: true,
18153 zoom: zoom2,
18154 pan: pan2
18155 };
18156 } else {
18157 // just set the zoom
18158 return {
18159 zoomed: true,
18160 panned: false,
18161 zoom: zoom,
18162 pan: currentPan
18163 };
18164 }
18165 },
18166 zoom: function zoom(params) {
18167 if (params === undefined) {
18168 // get
18169 return this._private.zoom;
18170 } else {
18171 // set
18172 var vp = this.getZoomedViewport(params);
18173 var _p = this._private;
18174
18175 if (vp == null || !vp.zoomed) {
18176 return this;
18177 }
18178
18179 _p.zoom = vp.zoom;
18180
18181 if (vp.panned) {
18182 _p.pan.x = vp.pan.x;
18183 _p.pan.y = vp.pan.y;
18184 }
18185
18186 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18187 this.notify('viewport');
18188 return this; // chaining
18189 }
18190 },
18191 viewport: function viewport(opts) {
18192 var _p = this._private;
18193 var zoomDefd = true;
18194 var panDefd = true;
18195 var events = []; // to trigger
18196
18197 var zoomFailed = false;
18198 var panFailed = false;
18199
18200 if (!opts) {
18201 return this;
18202 }
18203
18204 if (!number(opts.zoom)) {
18205 zoomDefd = false;
18206 }
18207
18208 if (!plainObject(opts.pan)) {
18209 panDefd = false;
18210 }
18211
18212 if (!zoomDefd && !panDefd) {
18213 return this;
18214 }
18215
18216 if (zoomDefd) {
18217 var z = opts.zoom;
18218
18219 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18220 zoomFailed = true;
18221 } else {
18222 _p.zoom = z;
18223 events.push('zoom');
18224 }
18225 }
18226
18227 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18228 var p = opts.pan;
18229
18230 if (number(p.x)) {
18231 _p.pan.x = p.x;
18232 panFailed = false;
18233 }
18234
18235 if (number(p.y)) {
18236 _p.pan.y = p.y;
18237 panFailed = false;
18238 }
18239
18240 if (!panFailed) {
18241 events.push('pan');
18242 }
18243 }
18244
18245 if (events.length > 0) {
18246 events.push('viewport');
18247 this.emit(events.join(' '));
18248 this.notify('viewport');
18249 }
18250
18251 return this; // chaining
18252 },
18253 center: function center(elements) {
18254 var pan = this.getCenterPan(elements);
18255
18256 if (pan) {
18257 this._private.pan = pan;
18258 this.emit('pan viewport');
18259 this.notify('viewport');
18260 }
18261
18262 return this; // chaining
18263 },
18264 getCenterPan: function getCenterPan(elements, zoom) {
18265 if (!this._private.panningEnabled) {
18266 return;
18267 }
18268
18269 if (string(elements)) {
18270 var selector = elements;
18271 elements = this.mutableElements().filter(selector);
18272 } else if (!elementOrCollection(elements)) {
18273 elements = this.mutableElements();
18274 }
18275
18276 if (elements.length === 0) {
18277 return;
18278 } // can't centre pan to nothing
18279
18280
18281 var bb = elements.boundingBox();
18282 var w = this.width();
18283 var h = this.height();
18284 zoom = zoom === undefined ? this._private.zoom : zoom;
18285 var pan = {
18286 // middle
18287 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18288 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18289 };
18290 return pan;
18291 },
18292 reset: function reset() {
18293 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18294 return this;
18295 }
18296
18297 this.viewport({
18298 pan: {
18299 x: 0,
18300 y: 0
18301 },
18302 zoom: 1
18303 });
18304 return this; // chaining
18305 },
18306 invalidateSize: function invalidateSize() {
18307 this._private.sizeCache = null;
18308 },
18309 size: function size() {
18310 var _p = this._private;
18311 var container = _p.container;
18312 return _p.sizeCache = _p.sizeCache || (container ? function () {
18313 var style = window$1.getComputedStyle(container);
18314
18315 var val = function val(name) {
18316 return parseFloat(style.getPropertyValue(name));
18317 };
18318
18319 return {
18320 width: container.clientWidth - val('padding-left') - val('padding-right'),
18321 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18322 };
18323 }() : {
18324 // fallback if no container (not 0 b/c can be used for dividing etc)
18325 width: 1,
18326 height: 1
18327 });
18328 },
18329 width: function width() {
18330 return this.size().width;
18331 },
18332 height: function height() {
18333 return this.size().height;
18334 },
18335 extent: function extent() {
18336 var pan = this._private.pan;
18337 var zoom = this._private.zoom;
18338 var rb = this.renderedExtent();
18339 var b = {
18340 x1: (rb.x1 - pan.x) / zoom,
18341 x2: (rb.x2 - pan.x) / zoom,
18342 y1: (rb.y1 - pan.y) / zoom,
18343 y2: (rb.y2 - pan.y) / zoom
18344 };
18345 b.w = b.x2 - b.x1;
18346 b.h = b.y2 - b.y1;
18347 return b;
18348 },
18349 renderedExtent: function renderedExtent() {
18350 var width = this.width();
18351 var height = this.height();
18352 return {
18353 x1: 0,
18354 y1: 0,
18355 x2: width,
18356 y2: height,
18357 w: width,
18358 h: height
18359 };
18360 }
18361}; // aliases
18362
18363corefn$8.centre = corefn$8.center; // backwards compatibility
18364
18365corefn$8.autolockNodes = corefn$8.autolock;
18366corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18367
18368var fn$6 = {
18369 data: define$3.data({
18370 field: 'data',
18371 bindingEvent: 'data',
18372 allowBinding: true,
18373 allowSetting: true,
18374 settingEvent: 'data',
18375 settingTriggersEvent: true,
18376 triggerFnName: 'trigger',
18377 allowGetting: true
18378 }),
18379 removeData: define$3.removeData({
18380 field: 'data',
18381 event: 'data',
18382 triggerFnName: 'trigger',
18383 triggerEvent: true
18384 }),
18385 scratch: define$3.data({
18386 field: 'scratch',
18387 bindingEvent: 'scratch',
18388 allowBinding: true,
18389 allowSetting: true,
18390 settingEvent: 'scratch',
18391 settingTriggersEvent: true,
18392 triggerFnName: 'trigger',
18393 allowGetting: true
18394 }),
18395 removeScratch: define$3.removeData({
18396 field: 'scratch',
18397 event: 'scratch',
18398 triggerFnName: 'trigger',
18399 triggerEvent: true
18400 })
18401}; // aliases
18402
18403fn$6.attr = fn$6.data;
18404fn$6.removeAttr = fn$6.removeData;
18405
18406var Core = function Core(opts) {
18407 var cy = this;
18408 opts = extend({}, opts);
18409 var container = opts.container; // allow for passing a wrapped jquery object
18410 // e.g. cytoscape({ container: $('#cy') })
18411
18412 if (container && !htmlElement(container) && htmlElement(container[0])) {
18413 container = container[0];
18414 }
18415
18416 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18417
18418 reg = reg || {};
18419
18420 if (reg && reg.cy) {
18421 reg.cy.destroy();
18422 reg = {}; // old instance => replace reg completely
18423 }
18424
18425 var readies = reg.readies = reg.readies || [];
18426
18427 if (container) {
18428 container._cyreg = reg;
18429 } // make sure container assoc'd reg points to this cy
18430
18431
18432 reg.cy = cy;
18433 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18434 var options = opts;
18435 options.layout = extend({
18436 name: head ? 'grid' : 'null'
18437 }, options.layout);
18438 options.renderer = extend({
18439 name: head ? 'canvas' : 'null'
18440 }, options.renderer);
18441
18442 var defVal = function defVal(def, val, altVal) {
18443 if (val !== undefined) {
18444 return val;
18445 } else if (altVal !== undefined) {
18446 return altVal;
18447 } else {
18448 return def;
18449 }
18450 };
18451
18452 var _p = this._private = {
18453 container: container,
18454 // html dom ele container
18455 ready: false,
18456 // whether ready has been triggered
18457 options: options,
18458 // cached options
18459 elements: new Collection(this),
18460 // elements in the graph
18461 listeners: [],
18462 // list of listeners
18463 aniEles: new Collection(this),
18464 // elements being animated
18465 data: {},
18466 // data for the core
18467 scratch: {},
18468 // scratch object for core
18469 layout: null,
18470 renderer: null,
18471 destroyed: false,
18472 // whether destroy was called
18473 notificationsEnabled: true,
18474 // whether notifications are sent to the renderer
18475 minZoom: 1e-50,
18476 maxZoom: 1e50,
18477 zoomingEnabled: defVal(true, options.zoomingEnabled),
18478 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18479 panningEnabled: defVal(true, options.panningEnabled),
18480 userPanningEnabled: defVal(true, options.userPanningEnabled),
18481 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18482 autolock: defVal(false, options.autolock, options.autolockNodes),
18483 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18484 autounselectify: defVal(false, options.autounselectify),
18485 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18486 zoom: number(options.zoom) ? options.zoom : 1,
18487 pan: {
18488 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18489 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18490 },
18491 animation: {
18492 // object for currently-running animations
18493 current: [],
18494 queue: []
18495 },
18496 hasCompoundNodes: false
18497 };
18498
18499 this.createEmitter(); // set selection type
18500
18501 this.selectionType(options.selectionType); // init zoom bounds
18502
18503 this.zoomRange({
18504 min: options.minZoom,
18505 max: options.maxZoom
18506 });
18507
18508 var loadExtData = function loadExtData(extData, next) {
18509 var anyIsPromise = extData.some(promise);
18510
18511 if (anyIsPromise) {
18512 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18513 } else {
18514 next(extData); // exec synchronously for convenience
18515 }
18516 }; // start with the default stylesheet so we have something before loading an external stylesheet
18517
18518
18519 if (_p.styleEnabled) {
18520 cy.setStyle([]);
18521 } // create the renderer
18522
18523
18524 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18525
18526 cy.initRenderer(rendererOptions);
18527
18528 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18529 cy.notifications(false); // remove old elements
18530
18531 var oldEles = cy.mutableElements();
18532
18533 if (oldEles.length > 0) {
18534 oldEles.remove();
18535 }
18536
18537 if (elements != null) {
18538 if (plainObject(elements) || array(elements)) {
18539 cy.add(elements);
18540 }
18541 }
18542
18543 cy.one('layoutready', function (e) {
18544 cy.notifications(true);
18545 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18546
18547 cy.one('load', onload);
18548 cy.emitAndNotify('load');
18549 }).one('layoutstop', function () {
18550 cy.one('done', ondone);
18551 cy.emit('done');
18552 });
18553 var layoutOpts = extend({}, cy._private.options.layout);
18554 layoutOpts.eles = cy.elements();
18555 cy.layout(layoutOpts).run();
18556 };
18557
18558 loadExtData([options.style, options.elements], function (thens) {
18559 var initStyle = thens[0];
18560 var initEles = thens[1]; // init style
18561
18562 if (_p.styleEnabled) {
18563 cy.style().append(initStyle);
18564 } // initial load
18565
18566
18567 setElesAndLayout(initEles, function () {
18568 // onready
18569 cy.startAnimationLoop();
18570 _p.ready = true; // if a ready callback is specified as an option, the bind it
18571
18572 if (fn(options.ready)) {
18573 cy.on('ready', options.ready);
18574 } // bind all the ready handlers registered before creating this instance
18575
18576
18577 for (var i = 0; i < readies.length; i++) {
18578 var fn$1 = readies[i];
18579 cy.on('ready', fn$1);
18580 }
18581
18582 if (reg) {
18583 reg.readies = [];
18584 } // 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
18585
18586
18587 cy.emit('ready');
18588 }, options.done);
18589 });
18590};
18591
18592var corefn$9 = Core.prototype; // short alias
18593
18594extend(corefn$9, {
18595 instanceString: function instanceString() {
18596 return 'core';
18597 },
18598 isReady: function isReady() {
18599 return this._private.ready;
18600 },
18601 destroyed: function destroyed() {
18602 return this._private.destroyed;
18603 },
18604 ready: function ready(fn) {
18605 if (this.isReady()) {
18606 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18607 } else {
18608 this.on('ready', fn);
18609 }
18610
18611 return this;
18612 },
18613 destroy: function destroy() {
18614 var cy = this;
18615 if (cy.destroyed()) return;
18616 cy.stopAnimationLoop();
18617 cy.destroyRenderer();
18618 this.emit('destroy');
18619 cy._private.destroyed = true;
18620 return cy;
18621 },
18622 hasElementWithId: function hasElementWithId(id) {
18623 return this._private.elements.hasElementWithId(id);
18624 },
18625 getElementById: function getElementById(id) {
18626 return this._private.elements.getElementById(id);
18627 },
18628 hasCompoundNodes: function hasCompoundNodes() {
18629 return this._private.hasCompoundNodes;
18630 },
18631 headless: function headless() {
18632 return this._private.renderer.isHeadless();
18633 },
18634 styleEnabled: function styleEnabled() {
18635 return this._private.styleEnabled;
18636 },
18637 addToPool: function addToPool(eles) {
18638 this._private.elements.merge(eles);
18639
18640 return this; // chaining
18641 },
18642 removeFromPool: function removeFromPool(eles) {
18643 this._private.elements.unmerge(eles);
18644
18645 return this;
18646 },
18647 container: function container() {
18648 return this._private.container || null;
18649 },
18650 mount: function mount(container) {
18651 if (container == null) {
18652 return;
18653 }
18654
18655 var cy = this;
18656 var _p = cy._private;
18657 var options = _p.options;
18658
18659 if (!htmlElement(container) && htmlElement(container[0])) {
18660 container = container[0];
18661 }
18662
18663 cy.stopAnimationLoop();
18664 cy.destroyRenderer();
18665 _p.container = container;
18666 _p.styleEnabled = true;
18667 cy.invalidateSize();
18668 cy.initRenderer(extend({}, options, options.renderer, {
18669 // allow custom renderer name to be re-used, otherwise use canvas
18670 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18671 }));
18672 cy.startAnimationLoop();
18673 cy.style(options.style);
18674 cy.emit('mount');
18675 return cy;
18676 },
18677 unmount: function unmount() {
18678 var cy = this;
18679 cy.stopAnimationLoop();
18680 cy.destroyRenderer();
18681 cy.initRenderer({
18682 name: 'null'
18683 });
18684 cy.emit('unmount');
18685 return cy;
18686 },
18687 options: function options() {
18688 return copy(this._private.options);
18689 },
18690 json: function json(obj) {
18691 var cy = this;
18692 var _p = cy._private;
18693 var eles = cy.mutableElements();
18694
18695 var getFreshRef = function getFreshRef(ele) {
18696 return cy.getElementById(ele.id());
18697 };
18698
18699 if (plainObject(obj)) {
18700 // set
18701 cy.startBatch();
18702
18703 if (obj.elements) {
18704 var idInJson = {};
18705
18706 var updateEles = function updateEles(jsons, gr) {
18707 var toAdd = [];
18708 var toMod = [];
18709
18710 for (var i = 0; i < jsons.length; i++) {
18711 var json = jsons[i];
18712 var id = '' + json.data.id; // id must be string
18713
18714 var ele = cy.getElementById(id);
18715 idInJson[id] = true;
18716
18717 if (ele.length !== 0) {
18718 // existing element should be updated
18719 toMod.push({
18720 ele: ele,
18721 json: json
18722 });
18723 } else {
18724 // otherwise should be added
18725 if (gr) {
18726 json.group = gr;
18727 toAdd.push(json);
18728 } else {
18729 toAdd.push(json);
18730 }
18731 }
18732 }
18733
18734 cy.add(toAdd);
18735
18736 for (var _i = 0; _i < toMod.length; _i++) {
18737 var _toMod$_i = toMod[_i],
18738 _ele = _toMod$_i.ele,
18739 _json = _toMod$_i.json;
18740
18741 _ele.json(_json);
18742 }
18743 };
18744
18745 if (array(obj.elements)) {
18746 // elements: []
18747 updateEles(obj.elements);
18748 } else {
18749 // elements: { nodes: [], edges: [] }
18750 var grs = ['nodes', 'edges'];
18751
18752 for (var i = 0; i < grs.length; i++) {
18753 var gr = grs[i];
18754 var elements = obj.elements[gr];
18755
18756 if (array(elements)) {
18757 updateEles(elements, gr);
18758 }
18759 }
18760 }
18761
18762 var parentsToRemove = cy.collection();
18763 eles.filter(function (ele) {
18764 return !idInJson[ele.id()];
18765 }).forEach(function (ele) {
18766 if (ele.isParent()) {
18767 parentsToRemove.merge(ele);
18768 } else {
18769 ele.remove();
18770 }
18771 }); // so that children are not removed w/parent
18772
18773 parentsToRemove.forEach(function (ele) {
18774 return ele.children().move({
18775 parent: null
18776 });
18777 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18778
18779 parentsToRemove.forEach(function (ele) {
18780 return getFreshRef(ele).remove();
18781 });
18782 }
18783
18784 if (obj.style) {
18785 cy.style(obj.style);
18786 }
18787
18788 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18789 cy.zoom(obj.zoom);
18790 }
18791
18792 if (obj.pan) {
18793 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18794 cy.pan(obj.pan);
18795 }
18796 }
18797
18798 if (obj.data) {
18799 cy.data(obj.data);
18800 }
18801
18802 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
18803
18804 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18805 var f = fields[_i2];
18806
18807 if (obj[f] != null) {
18808 cy[f](obj[f]);
18809 }
18810 }
18811
18812 cy.endBatch();
18813 return this; // chaining
18814 } else {
18815 // get
18816 var flat = !!obj;
18817 var json = {};
18818
18819 if (flat) {
18820 json.elements = this.elements().map(function (ele) {
18821 return ele.json();
18822 });
18823 } else {
18824 json.elements = {};
18825 eles.forEach(function (ele) {
18826 var group = ele.group();
18827
18828 if (!json.elements[group]) {
18829 json.elements[group] = [];
18830 }
18831
18832 json.elements[group].push(ele.json());
18833 });
18834 }
18835
18836 if (this._private.styleEnabled) {
18837 json.style = cy.style().json();
18838 }
18839
18840 json.data = copy(cy.data());
18841 var options = _p.options;
18842 json.zoomingEnabled = _p.zoomingEnabled;
18843 json.userZoomingEnabled = _p.userZoomingEnabled;
18844 json.zoom = _p.zoom;
18845 json.minZoom = _p.minZoom;
18846 json.maxZoom = _p.maxZoom;
18847 json.panningEnabled = _p.panningEnabled;
18848 json.userPanningEnabled = _p.userPanningEnabled;
18849 json.pan = copy(_p.pan);
18850 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18851 json.renderer = copy(options.renderer);
18852 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18853 json.textureOnViewport = options.textureOnViewport;
18854 json.wheelSensitivity = options.wheelSensitivity;
18855 json.motionBlur = options.motionBlur;
18856 return json;
18857 }
18858 }
18859});
18860corefn$9.$id = corefn$9.getElementById;
18861[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18862 extend(corefn$9, props);
18863});
18864
18865/* eslint-disable no-unused-vars */
18866
18867var defaults$9 = {
18868 fit: true,
18869 // whether to fit the viewport to the graph
18870 directed: false,
18871 // whether the tree is directed downwards (or edges can point in any direction if false)
18872 padding: 30,
18873 // padding on fit
18874 circle: false,
18875 // put depths in concentric circles if true, put depths top down if false
18876 grid: false,
18877 // whether to create an even grid into which the DAG is placed (circle:false only)
18878 spacingFactor: 1.75,
18879 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
18880 boundingBox: undefined,
18881 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
18882 avoidOverlap: true,
18883 // prevents node overlap, may overflow boundingBox if not enough space
18884 nodeDimensionsIncludeLabels: false,
18885 // Excludes the label when calculating node bounding boxes for the layout algorithm
18886 roots: undefined,
18887 // the roots of the trees
18888 maximal: false,
18889 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
18890 animate: false,
18891 // whether to transition the node positions
18892 animationDuration: 500,
18893 // duration of animation in ms if enabled
18894 animationEasing: undefined,
18895 // easing of animation if enabled,
18896 animateFilter: function animateFilter(node, i) {
18897 return true;
18898 },
18899 // 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
18900 ready: undefined,
18901 // callback on layoutready
18902 stop: undefined,
18903 // callback on layoutstop
18904 transform: function transform(node, position) {
18905 return position;
18906 } // transform a given node position. Useful for changing flow direction in discrete layouts
18907
18908};
18909/* eslint-enable */
18910
18911var getInfo = function getInfo(ele) {
18912 return ele.scratch('breadthfirst');
18913};
18914
18915var setInfo = function setInfo(ele, obj) {
18916 return ele.scratch('breadthfirst', obj);
18917};
18918
18919function BreadthFirstLayout(options) {
18920 this.options = extend({}, defaults$9, options);
18921}
18922
18923BreadthFirstLayout.prototype.run = function () {
18924 var params = this.options;
18925 var options = params;
18926 var cy = params.cy;
18927 var eles = options.eles;
18928 var nodes = eles.nodes().filter(function (n) {
18929 return !n.isParent();
18930 });
18931 var graph = eles;
18932 var directed = options.directed;
18933 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
18934
18935 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
18936 x1: 0,
18937 y1: 0,
18938 w: cy.width(),
18939 h: cy.height()
18940 });
18941 var roots;
18942
18943 if (elementOrCollection(options.roots)) {
18944 roots = options.roots;
18945 } else if (array(options.roots)) {
18946 var rootsArray = [];
18947
18948 for (var i = 0; i < options.roots.length; i++) {
18949 var id = options.roots[i];
18950 var ele = cy.getElementById(id);
18951 rootsArray.push(ele);
18952 }
18953
18954 roots = cy.collection(rootsArray);
18955 } else if (string(options.roots)) {
18956 roots = cy.$(options.roots);
18957 } else {
18958 if (directed) {
18959 roots = nodes.roots();
18960 } else {
18961 var components = eles.components();
18962 roots = cy.collection();
18963
18964 var _loop = function _loop(_i) {
18965 var comp = components[_i];
18966 var maxDegree = comp.maxDegree(false);
18967 var compRoots = comp.filter(function (ele) {
18968 return ele.degree(false) === maxDegree;
18969 });
18970 roots = roots.add(compRoots);
18971 };
18972
18973 for (var _i = 0; _i < components.length; _i++) {
18974 _loop(_i);
18975 }
18976 }
18977 }
18978
18979 var depths = [];
18980 var foundByBfs = {};
18981
18982 var addToDepth = function addToDepth(ele, d) {
18983 if (depths[d] == null) {
18984 depths[d] = [];
18985 }
18986
18987 var i = depths[d].length;
18988 depths[d].push(ele);
18989 setInfo(ele, {
18990 index: i,
18991 depth: d
18992 });
18993 };
18994
18995 var changeDepth = function changeDepth(ele, newDepth) {
18996 var _getInfo = getInfo(ele),
18997 depth = _getInfo.depth,
18998 index = _getInfo.index;
18999
19000 depths[depth][index] = null;
19001 addToDepth(ele, newDepth);
19002 }; // find the depths of the nodes
19003
19004
19005 graph.bfs({
19006 roots: roots,
19007 directed: options.directed,
19008 visit: function visit(node, edge, pNode, i, depth) {
19009 var ele = node[0];
19010 var id = ele.id();
19011 addToDepth(ele, depth);
19012 foundByBfs[id] = true;
19013 }
19014 }); // check for nodes not found by bfs
19015
19016 var orphanNodes = [];
19017
19018 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19019 var _ele = nodes[_i2];
19020
19021 if (foundByBfs[_ele.id()]) {
19022 continue;
19023 } else {
19024 orphanNodes.push(_ele);
19025 }
19026 } // assign the nodes a depth and index
19027
19028
19029 var assignDepthsAt = function assignDepthsAt(i) {
19030 var eles = depths[i];
19031
19032 for (var j = 0; j < eles.length; j++) {
19033 var _ele2 = eles[j];
19034
19035 if (_ele2 == null) {
19036 eles.splice(j, 1);
19037 j--;
19038 continue;
19039 }
19040
19041 setInfo(_ele2, {
19042 depth: i,
19043 index: j
19044 });
19045 }
19046 };
19047
19048 var assignDepths = function assignDepths() {
19049 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19050 assignDepthsAt(_i3);
19051 }
19052 };
19053
19054 var adjustMaximally = function adjustMaximally(ele, shifted) {
19055 var eInfo = getInfo(ele);
19056 var incomers = ele.incomers().filter(function (el) {
19057 return el.isNode() && eles.has(el);
19058 });
19059 var maxDepth = -1;
19060 var id = ele.id();
19061
19062 for (var k = 0; k < incomers.length; k++) {
19063 var incmr = incomers[k];
19064 var iInfo = getInfo(incmr);
19065 maxDepth = Math.max(maxDepth, iInfo.depth);
19066 }
19067
19068 if (eInfo.depth <= maxDepth) {
19069 if (shifted[id]) {
19070 return null;
19071 }
19072
19073 changeDepth(ele, maxDepth + 1);
19074 shifted[id] = true;
19075 return true;
19076 }
19077
19078 return false;
19079 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19080
19081
19082 if (directed && maximal) {
19083 var Q = [];
19084 var shifted = {};
19085
19086 var enqueue = function enqueue(n) {
19087 return Q.push(n);
19088 };
19089
19090 var dequeue = function dequeue() {
19091 return Q.shift();
19092 };
19093
19094 nodes.forEach(function (n) {
19095 return Q.push(n);
19096 });
19097
19098 while (Q.length > 0) {
19099 var _ele3 = dequeue();
19100
19101 var didShift = adjustMaximally(_ele3, shifted);
19102
19103 if (didShift) {
19104 _ele3.outgoers().filter(function (el) {
19105 return el.isNode() && eles.has(el);
19106 }).forEach(enqueue);
19107 } else if (didShift === null) {
19108 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19109 break; // exit on failure
19110 }
19111 }
19112 }
19113
19114 assignDepths(); // clear holes
19115 // find min distance we need to leave between nodes
19116
19117 var minDistance = 0;
19118
19119 if (options.avoidOverlap) {
19120 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19121 var n = nodes[_i4];
19122 var nbb = n.layoutDimensions(options);
19123 var w = nbb.w;
19124 var h = nbb.h;
19125 minDistance = Math.max(minDistance, w, h);
19126 }
19127 } // get the weighted percent for an element based on its connectivity to other levels
19128
19129
19130 var cachedWeightedPercent = {};
19131
19132 var getWeightedPercent = function getWeightedPercent(ele) {
19133 if (cachedWeightedPercent[ele.id()]) {
19134 return cachedWeightedPercent[ele.id()];
19135 }
19136
19137 var eleDepth = getInfo(ele).depth;
19138 var neighbors = ele.neighborhood();
19139 var percent = 0;
19140 var samples = 0;
19141
19142 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19143 var neighbor = neighbors[_i5];
19144
19145 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19146 continue;
19147 }
19148
19149 var bf = getInfo(neighbor);
19150 var index = bf.index;
19151 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19152
19153 if (index == null || depth == null) {
19154 continue;
19155 }
19156
19157 var nDepth = depths[depth].length;
19158
19159 if (depth < eleDepth) {
19160 // only get influenced by elements above
19161 percent += index / nDepth;
19162 samples++;
19163 }
19164 }
19165
19166 samples = Math.max(1, samples);
19167 percent = percent / samples;
19168
19169 if (samples === 0) {
19170 // put lone nodes at the start
19171 percent = 0;
19172 }
19173
19174 cachedWeightedPercent[ele.id()] = percent;
19175 return percent;
19176 }; // rearrange the indices in each depth level based on connectivity
19177
19178
19179 var sortFn = function sortFn(a, b) {
19180 var apct = getWeightedPercent(a);
19181 var bpct = getWeightedPercent(b);
19182 var diff = apct - bpct;
19183
19184 if (diff === 0) {
19185 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19186 } else {
19187 return diff;
19188 }
19189 }; // sort each level to make connected nodes closer
19190
19191
19192 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19193 depths[_i6].sort(sortFn);
19194
19195 assignDepthsAt(_i6);
19196 } // assign orphan nodes to a new top-level depth
19197
19198
19199 var orphanDepth = [];
19200
19201 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19202 orphanDepth.push(orphanNodes[_i7]);
19203 }
19204
19205 depths.unshift(orphanDepth);
19206 assignDepths();
19207 var biggestDepthSize = 0;
19208
19209 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19210 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19211 }
19212
19213 var center = {
19214 x: bb.x1 + bb.w / 2,
19215 y: bb.x1 + bb.h / 2
19216 };
19217 var maxDepthSize = depths.reduce(function (max, eles) {
19218 return Math.max(max, eles.length);
19219 }, 0);
19220
19221 var getPosition = function getPosition(ele) {
19222 var _getInfo2 = getInfo(ele),
19223 depth = _getInfo2.depth,
19224 index = _getInfo2.index;
19225
19226 var depthSize = depths[depth].length;
19227 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19228 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19229 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19230 radiusStepSize = Math.max(radiusStepSize, minDistance);
19231
19232 if (!options.circle) {
19233 var epos = {
19234 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19235 y: (depth + 1) * distanceY
19236 };
19237 return epos;
19238 } else {
19239 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19240 var theta = 2 * Math.PI / depths[depth].length * index;
19241
19242 if (depth === 0 && depths[0].length === 1) {
19243 radius = 1;
19244 }
19245
19246 return {
19247 x: center.x + radius * Math.cos(theta),
19248 y: center.y + radius * Math.sin(theta)
19249 };
19250 }
19251 };
19252
19253 nodes.layoutPositions(this, options, getPosition);
19254 return this; // chaining
19255};
19256
19257var defaults$a = {
19258 fit: true,
19259 // whether to fit the viewport to the graph
19260 padding: 30,
19261 // the padding on fit
19262 boundingBox: undefined,
19263 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19264 avoidOverlap: true,
19265 // prevents node overlap, may overflow boundingBox and radius if not enough space
19266 nodeDimensionsIncludeLabels: false,
19267 // Excludes the label when calculating node bounding boxes for the layout algorithm
19268 spacingFactor: undefined,
19269 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19270 radius: undefined,
19271 // the radius of the circle
19272 startAngle: 3 / 2 * Math.PI,
19273 // where nodes start in radians
19274 sweep: undefined,
19275 // how many radians should be between the first and last node (defaults to full circle)
19276 clockwise: true,
19277 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19278 sort: undefined,
19279 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19280 animate: false,
19281 // whether to transition the node positions
19282 animationDuration: 500,
19283 // duration of animation in ms if enabled
19284 animationEasing: undefined,
19285 // easing of animation if enabled
19286 animateFilter: function animateFilter(node, i) {
19287 return true;
19288 },
19289 // 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
19290 ready: undefined,
19291 // callback on layoutready
19292 stop: undefined,
19293 // callback on layoutstop
19294 transform: function transform(node, position) {
19295 return position;
19296 } // transform a given node position. Useful for changing flow direction in discrete layouts
19297
19298};
19299
19300function CircleLayout(options) {
19301 this.options = extend({}, defaults$a, options);
19302}
19303
19304CircleLayout.prototype.run = function () {
19305 var params = this.options;
19306 var options = params;
19307 var cy = params.cy;
19308 var eles = options.eles;
19309 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19310 var nodes = eles.nodes().not(':parent');
19311
19312 if (options.sort) {
19313 nodes = nodes.sort(options.sort);
19314 }
19315
19316 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19317 x1: 0,
19318 y1: 0,
19319 w: cy.width(),
19320 h: cy.height()
19321 });
19322 var center = {
19323 x: bb.x1 + bb.w / 2,
19324 y: bb.y1 + bb.h / 2
19325 };
19326 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19327 var dTheta = sweep / Math.max(1, nodes.length - 1);
19328 var r;
19329 var minDistance = 0;
19330
19331 for (var i = 0; i < nodes.length; i++) {
19332 var n = nodes[i];
19333 var nbb = n.layoutDimensions(options);
19334 var w = nbb.w;
19335 var h = nbb.h;
19336 minDistance = Math.max(minDistance, w, h);
19337 }
19338
19339 if (number(options.radius)) {
19340 r = options.radius;
19341 } else if (nodes.length <= 1) {
19342 r = 0;
19343 } else {
19344 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19345 } // calculate the radius
19346
19347
19348 if (nodes.length > 1 && options.avoidOverlap) {
19349 // but only if more than one node (can't overlap)
19350 minDistance *= 1.75; // just to have some nice spacing
19351
19352 var dcos = Math.cos(dTheta) - Math.cos(0);
19353 var dsin = Math.sin(dTheta) - Math.sin(0);
19354 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19355
19356 r = Math.max(rMin, r);
19357 }
19358
19359 var getPos = function getPos(ele, i) {
19360 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19361 var rx = r * Math.cos(theta);
19362 var ry = r * Math.sin(theta);
19363 var pos = {
19364 x: center.x + rx,
19365 y: center.y + ry
19366 };
19367 return pos;
19368 };
19369
19370 nodes.layoutPositions(this, options, getPos);
19371 return this; // chaining
19372};
19373
19374var defaults$b = {
19375 fit: true,
19376 // whether to fit the viewport to the graph
19377 padding: 30,
19378 // the padding on fit
19379 startAngle: 3 / 2 * Math.PI,
19380 // where nodes start in radians
19381 sweep: undefined,
19382 // how many radians should be between the first and last node (defaults to full circle)
19383 clockwise: true,
19384 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19385 equidistant: false,
19386 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19387 minNodeSpacing: 10,
19388 // min spacing between outside of nodes (used for radius adjustment)
19389 boundingBox: undefined,
19390 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19391 avoidOverlap: true,
19392 // prevents node overlap, may overflow boundingBox if not enough space
19393 nodeDimensionsIncludeLabels: false,
19394 // Excludes the label when calculating node bounding boxes for the layout algorithm
19395 height: undefined,
19396 // height of layout area (overrides container height)
19397 width: undefined,
19398 // width of layout area (overrides container width)
19399 spacingFactor: undefined,
19400 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19401 concentric: function concentric(node) {
19402 // returns numeric value for each node, placing higher nodes in levels towards the centre
19403 return node.degree();
19404 },
19405 levelWidth: function levelWidth(nodes) {
19406 // the variation of concentric values in each level
19407 return nodes.maxDegree() / 4;
19408 },
19409 animate: false,
19410 // whether to transition the node positions
19411 animationDuration: 500,
19412 // duration of animation in ms if enabled
19413 animationEasing: undefined,
19414 // easing of animation if enabled
19415 animateFilter: function animateFilter(node, i) {
19416 return true;
19417 },
19418 // 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
19419 ready: undefined,
19420 // callback on layoutready
19421 stop: undefined,
19422 // callback on layoutstop
19423 transform: function transform(node, position) {
19424 return position;
19425 } // transform a given node position. Useful for changing flow direction in discrete layouts
19426
19427};
19428
19429function ConcentricLayout(options) {
19430 this.options = extend({}, defaults$b, options);
19431}
19432
19433ConcentricLayout.prototype.run = function () {
19434 var params = this.options;
19435 var options = params;
19436 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19437 var cy = params.cy;
19438 var eles = options.eles;
19439 var nodes = eles.nodes().not(':parent');
19440 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19441 x1: 0,
19442 y1: 0,
19443 w: cy.width(),
19444 h: cy.height()
19445 });
19446 var center = {
19447 x: bb.x1 + bb.w / 2,
19448 y: bb.y1 + bb.h / 2
19449 };
19450 var nodeValues = []; // { node, value }
19451
19452 var maxNodeSize = 0;
19453
19454 for (var i = 0; i < nodes.length; i++) {
19455 var node = nodes[i];
19456 var value = void 0; // calculate the node value
19457
19458 value = options.concentric(node);
19459 nodeValues.push({
19460 value: value,
19461 node: node
19462 }); // for style mapping
19463
19464 node._private.scratch.concentric = value;
19465 } // in case we used the `concentric` in style
19466
19467
19468 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19469
19470 for (var _i = 0; _i < nodes.length; _i++) {
19471 var _node = nodes[_i];
19472
19473 var nbb = _node.layoutDimensions(options);
19474
19475 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19476 } // sort node values in descreasing order
19477
19478
19479 nodeValues.sort(function (a, b) {
19480 return b.value - a.value;
19481 });
19482 var levelWidth = options.levelWidth(nodes); // put the values into levels
19483
19484 var levels = [[]];
19485 var currentLevel = levels[0];
19486
19487 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19488 var val = nodeValues[_i2];
19489
19490 if (currentLevel.length > 0) {
19491 var diff = Math.abs(currentLevel[0].value - val.value);
19492
19493 if (diff >= levelWidth) {
19494 currentLevel = [];
19495 levels.push(currentLevel);
19496 }
19497 }
19498
19499 currentLevel.push(val);
19500 } // create positions from levels
19501
19502
19503 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19504
19505 if (!options.avoidOverlap) {
19506 // then strictly constrain to bb
19507 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19508 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19509 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19510 minDist = Math.min(minDist, rStep);
19511 } // find the metrics for each level
19512
19513
19514 var r = 0;
19515
19516 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19517 var level = levels[_i3];
19518 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19519 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19520
19521 if (level.length > 1 && options.avoidOverlap) {
19522 // but only if more than one node (can't overlap)
19523 var dcos = Math.cos(dTheta) - Math.cos(0);
19524 var dsin = Math.sin(dTheta) - Math.sin(0);
19525 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19526
19527 r = Math.max(rMin, r);
19528 }
19529
19530 level.r = r;
19531 r += minDist;
19532 }
19533
19534 if (options.equidistant) {
19535 var rDeltaMax = 0;
19536 var _r = 0;
19537
19538 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19539 var _level = levels[_i4];
19540 var rDelta = _level.r - _r;
19541 rDeltaMax = Math.max(rDeltaMax, rDelta);
19542 }
19543
19544 _r = 0;
19545
19546 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19547 var _level2 = levels[_i5];
19548
19549 if (_i5 === 0) {
19550 _r = _level2.r;
19551 }
19552
19553 _level2.r = _r;
19554 _r += rDeltaMax;
19555 }
19556 } // calculate the node positions
19557
19558
19559 var pos = {}; // id => position
19560
19561 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19562 var _level3 = levels[_i6];
19563 var _dTheta = _level3.dTheta;
19564 var _r2 = _level3.r;
19565
19566 for (var j = 0; j < _level3.length; j++) {
19567 var _val = _level3[j];
19568 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19569 var p = {
19570 x: center.x + _r2 * Math.cos(theta),
19571 y: center.y + _r2 * Math.sin(theta)
19572 };
19573 pos[_val.node.id()] = p;
19574 }
19575 } // position the nodes
19576
19577
19578 nodes.layoutPositions(this, options, function (ele) {
19579 var id = ele.id();
19580 return pos[id];
19581 });
19582 return this; // chaining
19583};
19584
19585/*
19586The CoSE layout was written by Gerardo Huck.
19587https://www.linkedin.com/in/gerardohuck/
19588
19589Based on the following article:
19590http://dl.acm.org/citation.cfm?id=1498047
19591
19592Modifications tracked on Github.
19593*/
19594var DEBUG;
19595/**
19596 * @brief : default layout options
19597 */
19598
19599var defaults$c = {
19600 // Called on `layoutready`
19601 ready: function ready() {},
19602 // Called on `layoutstop`
19603 stop: function stop() {},
19604 // Whether to animate while running the layout
19605 // true : Animate continuously as the layout is running
19606 // false : Just show the end result
19607 // 'end' : Animate with the end result, from the initial positions to the end positions
19608 animate: true,
19609 // Easing of the animation for animate:'end'
19610 animationEasing: undefined,
19611 // The duration of the animation for animate:'end'
19612 animationDuration: undefined,
19613 // A function that determines whether the node should be animated
19614 // All nodes animated by default on animate enabled
19615 // Non-animated nodes are positioned immediately when the layout starts
19616 animateFilter: function animateFilter(node, i) {
19617 return true;
19618 },
19619 // The layout animates only after this many milliseconds for animate:true
19620 // (prevents flashing on fast runs)
19621 animationThreshold: 250,
19622 // Number of iterations between consecutive screen positions update
19623 refresh: 20,
19624 // Whether to fit the network view after when done
19625 fit: true,
19626 // Padding on fit
19627 padding: 30,
19628 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19629 boundingBox: undefined,
19630 // Excludes the label when calculating node bounding boxes for the layout algorithm
19631 nodeDimensionsIncludeLabels: false,
19632 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19633 randomize: false,
19634 // Extra spacing between components in non-compound graphs
19635 componentSpacing: 40,
19636 // Node repulsion (non overlapping) multiplier
19637 nodeRepulsion: function nodeRepulsion(node) {
19638 return 2048;
19639 },
19640 // Node repulsion (overlapping) multiplier
19641 nodeOverlap: 4,
19642 // Ideal edge (non nested) length
19643 idealEdgeLength: function idealEdgeLength(edge) {
19644 return 32;
19645 },
19646 // Divisor to compute edge forces
19647 edgeElasticity: function edgeElasticity(edge) {
19648 return 32;
19649 },
19650 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19651 nestingFactor: 1.2,
19652 // Gravity force (constant)
19653 gravity: 1,
19654 // Maximum number of iterations to perform
19655 numIter: 1000,
19656 // Initial temperature (maximum node displacement)
19657 initialTemp: 1000,
19658 // Cooling factor (how the temperature is reduced between consecutive iterations
19659 coolingFactor: 0.99,
19660 // Lower temperature threshold (below this point the layout will end)
19661 minTemp: 1.0
19662};
19663/**
19664 * @brief : constructor
19665 * @arg options : object containing layout options
19666 */
19667
19668function CoseLayout(options) {
19669 this.options = extend({}, defaults$c, options);
19670 this.options.layout = this;
19671}
19672/**
19673 * @brief : runs the layout
19674 */
19675
19676
19677CoseLayout.prototype.run = function () {
19678 var options = this.options;
19679 var cy = options.cy;
19680 var layout = this;
19681 layout.stopped = false;
19682
19683 if (options.animate === true || options.animate === false) {
19684 layout.emit({
19685 type: 'layoutstart',
19686 layout: layout
19687 });
19688 } // Set DEBUG - Global variable
19689
19690
19691 if (true === options.debug) {
19692 DEBUG = true;
19693 } else {
19694 DEBUG = false;
19695 } // Initialize layout info
19696
19697
19698 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19699
19700 if (DEBUG) {
19701 printLayoutInfo(layoutInfo);
19702 } // If required, randomize node positions
19703
19704
19705 if (options.randomize) {
19706 randomizePositions(layoutInfo);
19707 }
19708
19709 var startTime = performanceNow();
19710
19711 var refresh = function refresh() {
19712 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19713
19714 if (true === options.fit) {
19715 cy.fit(options.padding);
19716 }
19717 };
19718
19719 var mainLoop = function mainLoop(i) {
19720 if (layout.stopped || i >= options.numIter) {
19721 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19722 return false;
19723 } // Do one step in the phisical simulation
19724
19725
19726 step$1(layoutInfo, options); // Update temperature
19727
19728 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19729
19730 if (layoutInfo.temperature < options.minTemp) {
19731 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19732 return false;
19733 }
19734
19735 return true;
19736 };
19737
19738 var done = function done() {
19739 if (options.animate === true || options.animate === false) {
19740 refresh(); // Layout has finished
19741
19742 layout.one('layoutstop', options.stop);
19743 layout.emit({
19744 type: 'layoutstop',
19745 layout: layout
19746 });
19747 } else {
19748 var nodes = options.eles.nodes();
19749 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19750 nodes.layoutPositions(layout, options, getScaledPos);
19751 }
19752 };
19753
19754 var i = 0;
19755 var loopRet = true;
19756
19757 if (options.animate === true) {
19758 var frame = function frame() {
19759 var f = 0;
19760
19761 while (loopRet && f < options.refresh) {
19762 loopRet = mainLoop(i);
19763 i++;
19764 f++;
19765 }
19766
19767 if (!loopRet) {
19768 // it's done
19769 separateComponents(layoutInfo, options);
19770 done();
19771 } else {
19772 var now = performanceNow();
19773
19774 if (now - startTime >= options.animationThreshold) {
19775 refresh();
19776 }
19777
19778 requestAnimationFrame(frame);
19779 }
19780 };
19781
19782 frame();
19783 } else {
19784 while (loopRet) {
19785 loopRet = mainLoop(i);
19786 i++;
19787 }
19788
19789 separateComponents(layoutInfo, options);
19790 done();
19791 }
19792
19793 return this; // chaining
19794};
19795/**
19796 * @brief : called on continuous layouts to stop them before they finish
19797 */
19798
19799
19800CoseLayout.prototype.stop = function () {
19801 this.stopped = true;
19802
19803 if (this.thread) {
19804 this.thread.stop();
19805 }
19806
19807 this.emit('layoutstop');
19808 return this; // chaining
19809};
19810
19811CoseLayout.prototype.destroy = function () {
19812 if (this.thread) {
19813 this.thread.stop();
19814 }
19815
19816 return this; // chaining
19817};
19818/**
19819 * @brief : Creates an object which is contains all the data
19820 * used in the layout process
19821 * @arg cy : cytoscape.js object
19822 * @return : layoutInfo object initialized
19823 */
19824
19825
19826var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19827 // Shortcut
19828 var edges = options.eles.edges();
19829 var nodes = options.eles.nodes();
19830 var layoutInfo = {
19831 isCompound: cy.hasCompoundNodes(),
19832 layoutNodes: [],
19833 idToIndex: {},
19834 nodeSize: nodes.size(),
19835 graphSet: [],
19836 indexToGraph: [],
19837 layoutEdges: [],
19838 edgeSize: edges.size(),
19839 temperature: options.initialTemp,
19840 clientWidth: cy.width(),
19841 clientHeight: cy.width(),
19842 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19843 x1: 0,
19844 y1: 0,
19845 w: cy.width(),
19846 h: cy.height()
19847 })
19848 };
19849 var components = options.eles.components();
19850 var id2cmptId = {};
19851
19852 for (var i = 0; i < components.length; i++) {
19853 var component = components[i];
19854
19855 for (var j = 0; j < component.length; j++) {
19856 var node = component[j];
19857 id2cmptId[node.id()] = i;
19858 }
19859 } // Iterate over all nodes, creating layout nodes
19860
19861
19862 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19863 var n = nodes[i];
19864 var nbb = n.layoutDimensions(options);
19865 var tempNode = {};
19866 tempNode.isLocked = n.locked();
19867 tempNode.id = n.data('id');
19868 tempNode.parentId = n.data('parent');
19869 tempNode.cmptId = id2cmptId[n.id()];
19870 tempNode.children = [];
19871 tempNode.positionX = n.position('x');
19872 tempNode.positionY = n.position('y');
19873 tempNode.offsetX = 0;
19874 tempNode.offsetY = 0;
19875 tempNode.height = nbb.w;
19876 tempNode.width = nbb.h;
19877 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
19878 tempNode.minX = tempNode.positionX - tempNode.width / 2;
19879 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
19880 tempNode.minY = tempNode.positionY - tempNode.height / 2;
19881 tempNode.padLeft = parseFloat(n.style('padding'));
19882 tempNode.padRight = parseFloat(n.style('padding'));
19883 tempNode.padTop = parseFloat(n.style('padding'));
19884 tempNode.padBottom = parseFloat(n.style('padding')); // forces
19885
19886 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
19887
19888 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
19889
19890 layoutInfo.idToIndex[tempNode.id] = i;
19891 } // Inline implementation of a queue, used for traversing the graph in BFS order
19892
19893
19894 var queue = [];
19895 var start = 0; // Points to the start the queue
19896
19897 var end = -1; // Points to the end of the queue
19898
19899 var tempGraph = []; // Second pass to add child information and
19900 // initialize queue for hierarchical traversal
19901
19902 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19903 var n = layoutInfo.layoutNodes[i];
19904 var p_id = n.parentId; // Check if node n has a parent node
19905
19906 if (null != p_id) {
19907 // Add node Id to parent's list of children
19908 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
19909 } else {
19910 // If a node doesn't have a parent, then it's in the root graph
19911 queue[++end] = n.id;
19912 tempGraph.push(n.id);
19913 }
19914 } // Add root graph to graphSet
19915
19916
19917 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
19918
19919 while (start <= end) {
19920 // Get the node to visit and remove it from queue
19921 var node_id = queue[start++];
19922 var node_ix = layoutInfo.idToIndex[node_id];
19923 var node = layoutInfo.layoutNodes[node_ix];
19924 var children = node.children;
19925
19926 if (children.length > 0) {
19927 // Add children nodes as a new graph to graph set
19928 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
19929
19930 for (var i = 0; i < children.length; i++) {
19931 queue[++end] = children[i];
19932 }
19933 }
19934 } // Create indexToGraph map
19935
19936
19937 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19938 var graph = layoutInfo.graphSet[i];
19939
19940 for (var j = 0; j < graph.length; j++) {
19941 var index = layoutInfo.idToIndex[graph[j]];
19942 layoutInfo.indexToGraph[index] = i;
19943 }
19944 } // Iterate over all edges, creating Layout Edges
19945
19946
19947 for (var i = 0; i < layoutInfo.edgeSize; i++) {
19948 var e = edges[i];
19949 var tempEdge = {};
19950 tempEdge.id = e.data('id');
19951 tempEdge.sourceId = e.data('source');
19952 tempEdge.targetId = e.data('target'); // Compute ideal length
19953
19954 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
19955 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
19956
19957 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
19958 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
19959 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
19960 var targetGraph = layoutInfo.indexToGraph[targetIx];
19961
19962 if (sourceGraph != targetGraph) {
19963 // Find lowest common graph ancestor
19964 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
19965
19966 var lcaGraph = layoutInfo.graphSet[lca];
19967 var depth = 0; // Source depth
19968
19969 var tempNode = layoutInfo.layoutNodes[sourceIx];
19970
19971 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19972 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19973 depth++;
19974 } // Target depth
19975
19976
19977 tempNode = layoutInfo.layoutNodes[targetIx];
19978
19979 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19980 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19981 depth++;
19982 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
19983 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
19984 // ". Depth: " + depth);
19985 // Update idealLength
19986
19987
19988 idealLength *= depth * options.nestingFactor;
19989 }
19990
19991 tempEdge.idealLength = idealLength;
19992 tempEdge.elasticity = elasticity;
19993 layoutInfo.layoutEdges.push(tempEdge);
19994 } // Finally, return layoutInfo object
19995
19996
19997 return layoutInfo;
19998};
19999/**
20000 * @brief : This function finds the index of the lowest common
20001 * graph ancestor between 2 nodes in the subtree
20002 * (from the graph hierarchy induced tree) whose
20003 * root is graphIx
20004 *
20005 * @arg node1: node1's ID
20006 * @arg node2: node2's ID
20007 * @arg layoutInfo: layoutInfo object
20008 *
20009 */
20010
20011
20012var findLCA = function findLCA(node1, node2, layoutInfo) {
20013 // Find their common ancester, starting from the root graph
20014 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20015
20016 if (2 > res.count) {
20017 // If aux function couldn't find the common ancester,
20018 // then it is the root graph
20019 return 0;
20020 } else {
20021 return res.graph;
20022 }
20023};
20024/**
20025 * @brief : Auxiliary function used for LCA computation
20026 *
20027 * @arg node1 : node1's ID
20028 * @arg node2 : node2's ID
20029 * @arg graphIx : subgraph index
20030 * @arg layoutInfo : layoutInfo object
20031 *
20032 * @return : object of the form {count: X, graph: Y}, where:
20033 * X is the number of ancesters (max: 2) found in
20034 * graphIx (and it's subgraphs),
20035 * Y is the graph index of the lowest graph containing
20036 * all X nodes
20037 */
20038
20039
20040var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20041 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20042
20043 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20044 return {
20045 count: 2,
20046 graph: graphIx
20047 };
20048 } // Make recursive calls for all subgraphs
20049
20050
20051 var c = 0;
20052
20053 for (var i = 0; i < graph.length; i++) {
20054 var nodeId = graph[i];
20055 var nodeIx = layoutInfo.idToIndex[nodeId];
20056 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20057
20058 if (0 === children.length) {
20059 continue;
20060 }
20061
20062 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20063 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20064
20065 if (0 === result.count) {
20066 // Neither node1 nor node2 are present in this subgraph
20067 continue;
20068 } else if (1 === result.count) {
20069 // One of (node1, node2) is present in this subgraph
20070 c++;
20071
20072 if (2 === c) {
20073 // We've already found both nodes, no need to keep searching
20074 break;
20075 }
20076 } else {
20077 // Both nodes are present in this subgraph
20078 return result;
20079 }
20080 }
20081
20082 return {
20083 count: c,
20084 graph: graphIx
20085 };
20086};
20087/**
20088 * @brief: printsLayoutInfo into js console
20089 * Only used for debbuging
20090 */
20091
20092
20093if (false) {
20094 var printLayoutInfo;
20095}
20096/**
20097 * @brief : Randomizes the position of all nodes
20098 */
20099
20100
20101var randomizePositions = function randomizePositions(layoutInfo, cy) {
20102 var width = layoutInfo.clientWidth;
20103 var height = layoutInfo.clientHeight;
20104
20105 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20106 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20107
20108 if (0 === n.children.length && !n.isLocked) {
20109 n.positionX = Math.random() * width;
20110 n.positionY = Math.random() * height;
20111 }
20112 }
20113};
20114
20115var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20116 var bb = layoutInfo.boundingBox;
20117 var coseBB = {
20118 x1: Infinity,
20119 x2: -Infinity,
20120 y1: Infinity,
20121 y2: -Infinity
20122 };
20123
20124 if (options.boundingBox) {
20125 nodes.forEach(function (node) {
20126 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20127 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20128 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20129 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20130 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20131 });
20132 coseBB.w = coseBB.x2 - coseBB.x1;
20133 coseBB.h = coseBB.y2 - coseBB.y1;
20134 }
20135
20136 return function (ele, i) {
20137 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20138
20139 if (options.boundingBox) {
20140 // then add extra bounding box constraint
20141 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20142 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20143 return {
20144 x: bb.x1 + pctX * bb.w,
20145 y: bb.y1 + pctY * bb.h
20146 };
20147 } else {
20148 return {
20149 x: lnode.positionX,
20150 y: lnode.positionY
20151 };
20152 }
20153 };
20154};
20155/**
20156 * @brief : Updates the positions of nodes in the network
20157 * @arg layoutInfo : LayoutInfo object
20158 * @arg cy : Cytoscape object
20159 * @arg options : Layout options
20160 */
20161
20162
20163var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20164 // var s = 'Refreshing positions';
20165 // logDebug(s);
20166 var layout = options.layout;
20167 var nodes = options.eles.nodes();
20168 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20169 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20170
20171 if (true !== layoutInfo.ready) {
20172 // s = 'Triggering layoutready';
20173 // logDebug(s);
20174 layoutInfo.ready = true;
20175 layout.one('layoutready', options.ready);
20176 layout.emit({
20177 type: 'layoutready',
20178 layout: this
20179 });
20180 }
20181};
20182/**
20183 * @brief : Logs a debug message in JS console, if DEBUG is ON
20184 */
20185// var logDebug = function(text) {
20186// if (DEBUG) {
20187// console.debug(text);
20188// }
20189// };
20190
20191/**
20192 * @brief : Performs one iteration of the physical simulation
20193 * @arg layoutInfo : LayoutInfo object already initialized
20194 * @arg cy : Cytoscape object
20195 * @arg options : Layout options
20196 */
20197
20198
20199var step$1 = function step(layoutInfo, options, _step) {
20200 // var s = "\n\n###############################";
20201 // s += "\nSTEP: " + step;
20202 // s += "\n###############################\n";
20203 // logDebug(s);
20204 // Calculate node repulsions
20205 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20206
20207 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20208
20209 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20210
20211 propagateForces(layoutInfo); // Update positions based on calculated forces
20212
20213 updatePositions(layoutInfo);
20214};
20215/**
20216 * @brief : Computes the node repulsion forces
20217 */
20218
20219
20220var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20221 // Go through each of the graphs in graphSet
20222 // Nodes only repel each other if they belong to the same graph
20223 // var s = 'calculateNodeForces';
20224 // logDebug(s);
20225 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20226 var graph = layoutInfo.graphSet[i];
20227 var numNodes = graph.length; // s = "Set: " + graph.toString();
20228 // logDebug(s);
20229 // Now get all the pairs of nodes
20230 // Only get each pair once, (A, B) = (B, A)
20231
20232 for (var j = 0; j < numNodes; j++) {
20233 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20234
20235 for (var k = j + 1; k < numNodes; k++) {
20236 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20237 nodeRepulsion(node1, node2, layoutInfo, options);
20238 }
20239 }
20240 }
20241};
20242
20243var randomDistance = function randomDistance(max) {
20244 return -max + 2 * max * Math.random();
20245};
20246/**
20247 * @brief : Compute the node repulsion forces between a pair of nodes
20248 */
20249
20250
20251var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20252 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20253 var cmptId1 = node1.cmptId;
20254 var cmptId2 = node2.cmptId;
20255
20256 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20257 return;
20258 } // Get direction of line connecting both node centers
20259
20260
20261 var directionX = node2.positionX - node1.positionX;
20262 var directionY = node2.positionY - node1.positionY;
20263 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20264 // If both centers are the same, apply a random force
20265
20266 if (0 === directionX && 0 === directionY) {
20267 directionX = randomDistance(maxRandDist);
20268 directionY = randomDistance(maxRandDist);
20269 }
20270
20271 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20272
20273 if (overlap > 0) {
20274 // s += "\nNodes DO overlap.";
20275 // s += "\nOverlap: " + overlap;
20276 // If nodes overlap, repulsion force is proportional
20277 // to the overlap
20278 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20279
20280 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20281
20282 var forceX = force * directionX / distance;
20283 var forceY = force * directionY / distance;
20284 } else {
20285 // s += "\nNodes do NOT overlap.";
20286 // If there's no overlap, force is inversely proportional
20287 // to squared distance
20288 // Get clipping points for both nodes
20289 var point1 = findClippingPoint(node1, directionX, directionY);
20290 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20291
20292 var distanceX = point2.x - point1.x;
20293 var distanceY = point2.y - point1.y;
20294 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20295 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20296 // Compute the module and components of the force vector
20297
20298 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20299 var forceX = force * distanceX / distance;
20300 var forceY = force * distanceY / distance;
20301 } // Apply force
20302
20303
20304 if (!node1.isLocked) {
20305 node1.offsetX -= forceX;
20306 node1.offsetY -= forceY;
20307 }
20308
20309 if (!node2.isLocked) {
20310 node2.offsetX += forceX;
20311 node2.offsetY += forceY;
20312 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20313 // logDebug(s);
20314
20315
20316 return;
20317};
20318/**
20319 * @brief : Determines whether two nodes overlap or not
20320 * @return : Amount of overlapping (0 => no overlap)
20321 */
20322
20323
20324var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20325 if (dX > 0) {
20326 var overlapX = node1.maxX - node2.minX;
20327 } else {
20328 var overlapX = node2.maxX - node1.minX;
20329 }
20330
20331 if (dY > 0) {
20332 var overlapY = node1.maxY - node2.minY;
20333 } else {
20334 var overlapY = node2.maxY - node1.minY;
20335 }
20336
20337 if (overlapX >= 0 && overlapY >= 0) {
20338 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20339 } else {
20340 return 0;
20341 }
20342};
20343/**
20344 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20345 * the rectangular bounding box of it's source/target node
20346 */
20347
20348
20349var findClippingPoint = function findClippingPoint(node, dX, dY) {
20350 // Shorcuts
20351 var X = node.positionX;
20352 var Y = node.positionY;
20353 var H = node.height || 1;
20354 var W = node.width || 1;
20355 var dirSlope = dY / dX;
20356 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20357 // " . Height: " + H + ", Width: " + W +
20358 // "\nDirection " + dX + ", " + dY;
20359 //
20360 // Compute intersection
20361
20362 var res = {}; // Case: Vertical direction (up)
20363
20364 if (0 === dX && 0 < dY) {
20365 res.x = X; // s += "\nUp direction";
20366
20367 res.y = Y + H / 2;
20368 return res;
20369 } // Case: Vertical direction (down)
20370
20371
20372 if (0 === dX && 0 > dY) {
20373 res.x = X;
20374 res.y = Y + H / 2; // s += "\nDown direction";
20375
20376 return res;
20377 } // Case: Intersects the right border
20378
20379
20380 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20381 res.x = X + W / 2;
20382 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20383
20384 return res;
20385 } // Case: Intersects the left border
20386
20387
20388 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20389 res.x = X - W / 2;
20390 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20391
20392 return res;
20393 } // Case: Intersects the top border
20394
20395
20396 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20397 res.x = X + H * dX / 2 / dY;
20398 res.y = Y + H / 2; // s += "\nTop border";
20399
20400 return res;
20401 } // Case: Intersects the bottom border
20402
20403
20404 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20405 res.x = X - H * dX / 2 / dY;
20406 res.y = Y - H / 2; // s += "\nBottom border";
20407
20408 return res;
20409 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20410 // logDebug(s);
20411
20412
20413 return res;
20414};
20415/**
20416 * @brief : Calculates all edge forces
20417 */
20418
20419
20420var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20421 // Iterate over all edges
20422 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20423 // Get edge, source & target nodes
20424 var edge = layoutInfo.layoutEdges[i];
20425 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20426 var source = layoutInfo.layoutNodes[sourceIx];
20427 var targetIx = layoutInfo.idToIndex[edge.targetId];
20428 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20429
20430 var directionX = target.positionX - source.positionX;
20431 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20432 // A random force has already been applied as node repulsion
20433
20434 if (0 === directionX && 0 === directionY) {
20435 continue;
20436 } // Get clipping points for both nodes
20437
20438
20439 var point1 = findClippingPoint(source, directionX, directionY);
20440 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20441 var lx = point2.x - point1.x;
20442 var ly = point2.y - point1.y;
20443 var l = Math.sqrt(lx * lx + ly * ly);
20444 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20445
20446 if (0 !== l) {
20447 var forceX = force * lx / l;
20448 var forceY = force * ly / l;
20449 } else {
20450 var forceX = 0;
20451 var forceY = 0;
20452 } // Add this force to target and source nodes
20453
20454
20455 if (!source.isLocked) {
20456 source.offsetX += forceX;
20457 source.offsetY += forceY;
20458 }
20459
20460 if (!target.isLocked) {
20461 target.offsetX -= forceX;
20462 target.offsetY -= forceY;
20463 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20464 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20465 // logDebug(s);
20466
20467 }
20468};
20469/**
20470 * @brief : Computes gravity forces for all nodes
20471 */
20472
20473
20474var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20475 var distThreshold = 1; // var s = 'calculateGravityForces';
20476 // logDebug(s);
20477
20478 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20479 var graph = layoutInfo.graphSet[i];
20480 var numNodes = graph.length; // s = "Set: " + graph.toString();
20481 // logDebug(s);
20482 // Compute graph center
20483
20484 if (0 === i) {
20485 var centerX = layoutInfo.clientHeight / 2;
20486 var centerY = layoutInfo.clientWidth / 2;
20487 } else {
20488 // Get Parent node for this graph, and use its position as center
20489 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20490 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20491 var centerX = parent.positionX;
20492 var centerY = parent.positionY;
20493 } // s = "Center found at: " + centerX + ", " + centerY;
20494 // logDebug(s);
20495 // Apply force to all nodes in graph
20496
20497
20498 for (var j = 0; j < numNodes; j++) {
20499 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20500
20501 if (node.isLocked) {
20502 continue;
20503 }
20504
20505 var dx = centerX - node.positionX;
20506 var dy = centerY - node.positionY;
20507 var d = Math.sqrt(dx * dx + dy * dy);
20508
20509 if (d > distThreshold) {
20510 var fx = options.gravity * dx / d;
20511 var fy = options.gravity * dy / d;
20512 node.offsetX += fx;
20513 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20514 } // s += ": skypped since it's too close to center";
20515 // logDebug(s);
20516
20517 }
20518 }
20519};
20520/**
20521 * @brief : This function propagates the existing offsets from
20522 * parent nodes to its descendents.
20523 * @arg layoutInfo : layoutInfo Object
20524 * @arg cy : cytoscape Object
20525 * @arg options : Layout options
20526 */
20527
20528
20529var propagateForces = function propagateForces(layoutInfo, options) {
20530 // Inline implementation of a queue, used for traversing the graph in BFS order
20531 var queue = [];
20532 var start = 0; // Points to the start the queue
20533
20534 var end = -1; // Points to the end of the queue
20535 // logDebug('propagateForces');
20536 // Start by visiting the nodes in the root graph
20537
20538 queue.push.apply(queue, layoutInfo.graphSet[0]);
20539 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20540
20541 while (start <= end) {
20542 // Get the node to visit and remove it from queue
20543 var nodeId = queue[start++];
20544 var nodeIndex = layoutInfo.idToIndex[nodeId];
20545 var node = layoutInfo.layoutNodes[nodeIndex];
20546 var children = node.children; // We only need to process the node if it's compound
20547
20548 if (0 < children.length && !node.isLocked) {
20549 var offX = node.offsetX;
20550 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20551 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20552 // s += "\n Children: " + children.toString();
20553 // logDebug(s);
20554
20555 for (var i = 0; i < children.length; i++) {
20556 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20557
20558 childNode.offsetX += offX;
20559 childNode.offsetY += offY; // Add children to queue to be visited
20560
20561 queue[++end] = children[i];
20562 } // Reset parent offsets
20563
20564
20565 node.offsetX = 0;
20566 node.offsetY = 0;
20567 }
20568 }
20569};
20570/**
20571 * @brief : Updates the layout model positions, based on
20572 * the accumulated forces
20573 */
20574
20575
20576var updatePositions = function updatePositions(layoutInfo, options) {
20577 // var s = 'Updating positions';
20578 // logDebug(s);
20579 // Reset boundaries for compound nodes
20580 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20581 var n = layoutInfo.layoutNodes[i];
20582
20583 if (0 < n.children.length) {
20584 // logDebug("Resetting boundaries of compound node: " + n.id);
20585 n.maxX = undefined;
20586 n.minX = undefined;
20587 n.maxY = undefined;
20588 n.minY = undefined;
20589 }
20590 }
20591
20592 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20593 var n = layoutInfo.layoutNodes[i];
20594
20595 if (0 < n.children.length || n.isLocked) {
20596 // No need to set compound or locked node position
20597 // logDebug("Skipping position update of node: " + n.id);
20598 continue;
20599 } // s = "Node: " + n.id + " Previous position: (" +
20600 // n.positionX + ", " + n.positionY + ").";
20601 // Limit displacement in order to improve stability
20602
20603
20604 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20605 n.positionX += tempForce.x;
20606 n.positionY += tempForce.y;
20607 n.offsetX = 0;
20608 n.offsetY = 0;
20609 n.minX = n.positionX - n.width;
20610 n.maxX = n.positionX + n.width;
20611 n.minY = n.positionY - n.height;
20612 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20613 // logDebug(s);
20614 // Update ancestry boudaries
20615
20616 updateAncestryBoundaries(n, layoutInfo);
20617 } // Update size, position of compund nodes
20618
20619
20620 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20621 var n = layoutInfo.layoutNodes[i];
20622
20623 if (0 < n.children.length && !n.isLocked) {
20624 n.positionX = (n.maxX + n.minX) / 2;
20625 n.positionY = (n.maxY + n.minY) / 2;
20626 n.width = n.maxX - n.minX;
20627 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20628 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20629 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20630 // logDebug(s);
20631 }
20632 }
20633};
20634/**
20635 * @brief : Limits a force (forceX, forceY) to be not
20636 * greater (in modulo) than max.
20637 8 Preserves force direction.
20638 */
20639
20640
20641var limitForce = function limitForce(forceX, forceY, max) {
20642 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20643 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20644
20645 if (force > max) {
20646 var res = {
20647 x: max * forceX / force,
20648 y: max * forceY / force
20649 };
20650 } else {
20651 var res = {
20652 x: forceX,
20653 y: forceY
20654 };
20655 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20656 // logDebug(s);
20657
20658
20659 return res;
20660};
20661/**
20662 * @brief : Function used for keeping track of compound node
20663 * sizes, since they should bound all their subnodes.
20664 */
20665
20666
20667var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20668 // var s = "Propagating new position/size of node " + node.id;
20669 var parentId = node.parentId;
20670
20671 if (null == parentId) {
20672 // If there's no parent, we are done
20673 // s += ". No parent node.";
20674 // logDebug(s);
20675 return;
20676 } // Get Parent Node
20677
20678
20679 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20680 var flag = false; // MaxX
20681
20682 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20683 p.maxX = node.maxX + p.padRight;
20684 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20685 } // MinX
20686
20687
20688 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20689 p.minX = node.minX - p.padLeft;
20690 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20691 } // MaxY
20692
20693
20694 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20695 p.maxY = node.maxY + p.padBottom;
20696 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20697 } // MinY
20698
20699
20700 if (null == p.minY || node.minY - p.padTop < p.minY) {
20701 p.minY = node.minY - p.padTop;
20702 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20703 } // If updated boundaries, propagate changes upward
20704
20705
20706 if (flag) {
20707 // logDebug(s);
20708 return updateAncestryBoundaries(p, layoutInfo);
20709 } // s += ". No changes in boundaries/position of parent node " + p.id;
20710 // logDebug(s);
20711
20712
20713 return;
20714};
20715
20716var separateComponents = function separateComponents(layoutInfo, options) {
20717 var nodes = layoutInfo.layoutNodes;
20718 var components = [];
20719
20720 for (var i = 0; i < nodes.length; i++) {
20721 var node = nodes[i];
20722 var cid = node.cmptId;
20723 var component = components[cid] = components[cid] || [];
20724 component.push(node);
20725 }
20726
20727 var totalA = 0;
20728
20729 for (var i = 0; i < components.length; i++) {
20730 var c = components[i];
20731
20732 if (!c) {
20733 continue;
20734 }
20735
20736 c.x1 = Infinity;
20737 c.x2 = -Infinity;
20738 c.y1 = Infinity;
20739 c.y2 = -Infinity;
20740
20741 for (var j = 0; j < c.length; j++) {
20742 var n = c[j];
20743 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20744 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20745 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20746 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20747 }
20748
20749 c.w = c.x2 - c.x1;
20750 c.h = c.y2 - c.y1;
20751 totalA += c.w * c.h;
20752 }
20753
20754 components.sort(function (c1, c2) {
20755 return c2.w * c2.h - c1.w * c1.h;
20756 });
20757 var x = 0;
20758 var y = 0;
20759 var usedW = 0;
20760 var rowH = 0;
20761 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20762
20763 for (var i = 0; i < components.length; i++) {
20764 var c = components[i];
20765
20766 if (!c) {
20767 continue;
20768 }
20769
20770 for (var j = 0; j < c.length; j++) {
20771 var n = c[j];
20772
20773 if (!n.isLocked) {
20774 n.positionX += x - c.x1;
20775 n.positionY += y - c.y1;
20776 }
20777 }
20778
20779 x += c.w + options.componentSpacing;
20780 usedW += c.w + options.componentSpacing;
20781 rowH = Math.max(rowH, c.h);
20782
20783 if (usedW > maxRowW) {
20784 y += rowH + options.componentSpacing;
20785 x = 0;
20786 usedW = 0;
20787 rowH = 0;
20788 }
20789 }
20790};
20791
20792var defaults$d = {
20793 fit: true,
20794 // whether to fit the viewport to the graph
20795 padding: 30,
20796 // padding used on fit
20797 boundingBox: undefined,
20798 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20799 avoidOverlap: true,
20800 // prevents node overlap, may overflow boundingBox if not enough space
20801 avoidOverlapPadding: 10,
20802 // extra spacing around nodes when avoidOverlap: true
20803 nodeDimensionsIncludeLabels: false,
20804 // Excludes the label when calculating node bounding boxes for the layout algorithm
20805 spacingFactor: undefined,
20806 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20807 condense: false,
20808 // uses all available space on false, uses minimal space on true
20809 rows: undefined,
20810 // force num of rows in the grid
20811 cols: undefined,
20812 // force num of columns in the grid
20813 position: function position(node) {},
20814 // returns { row, col } for element
20815 sort: undefined,
20816 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20817 animate: false,
20818 // whether to transition the node positions
20819 animationDuration: 500,
20820 // duration of animation in ms if enabled
20821 animationEasing: undefined,
20822 // easing of animation if enabled
20823 animateFilter: function animateFilter(node, i) {
20824 return true;
20825 },
20826 // 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
20827 ready: undefined,
20828 // callback on layoutready
20829 stop: undefined,
20830 // callback on layoutstop
20831 transform: function transform(node, position) {
20832 return position;
20833 } // transform a given node position. Useful for changing flow direction in discrete layouts
20834
20835};
20836
20837function GridLayout(options) {
20838 this.options = extend({}, defaults$d, options);
20839}
20840
20841GridLayout.prototype.run = function () {
20842 var params = this.options;
20843 var options = params;
20844 var cy = params.cy;
20845 var eles = options.eles;
20846 var nodes = eles.nodes().not(':parent');
20847
20848 if (options.sort) {
20849 nodes = nodes.sort(options.sort);
20850 }
20851
20852 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20853 x1: 0,
20854 y1: 0,
20855 w: cy.width(),
20856 h: cy.height()
20857 });
20858
20859 if (bb.h === 0 || bb.w === 0) {
20860 nodes.layoutPositions(this, options, function (ele) {
20861 return {
20862 x: bb.x1,
20863 y: bb.y1
20864 };
20865 });
20866 } else {
20867 // width/height * splits^2 = cells where splits is number of times to split width
20868 var cells = nodes.size();
20869 var splits = Math.sqrt(cells * bb.h / bb.w);
20870 var rows = Math.round(splits);
20871 var cols = Math.round(bb.w / bb.h * splits);
20872
20873 var small = function small(val) {
20874 if (val == null) {
20875 return Math.min(rows, cols);
20876 } else {
20877 var min = Math.min(rows, cols);
20878
20879 if (min == rows) {
20880 rows = val;
20881 } else {
20882 cols = val;
20883 }
20884 }
20885 };
20886
20887 var large = function large(val) {
20888 if (val == null) {
20889 return Math.max(rows, cols);
20890 } else {
20891 var max = Math.max(rows, cols);
20892
20893 if (max == rows) {
20894 rows = val;
20895 } else {
20896 cols = val;
20897 }
20898 }
20899 };
20900
20901 var oRows = options.rows;
20902 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
20903
20904 if (oRows != null && oCols != null) {
20905 rows = oRows;
20906 cols = oCols;
20907 } else if (oRows != null && oCols == null) {
20908 rows = oRows;
20909 cols = Math.ceil(cells / rows);
20910 } else if (oRows == null && oCols != null) {
20911 cols = oCols;
20912 rows = Math.ceil(cells / cols);
20913 } // otherwise use the automatic values and adjust accordingly
20914 // if rounding was up, see if we can reduce rows or columns
20915 else if (cols * rows > cells) {
20916 var sm = small();
20917 var lg = large(); // reducing the small side takes away the most cells, so try it first
20918
20919 if ((sm - 1) * lg >= cells) {
20920 small(sm - 1);
20921 } else if ((lg - 1) * sm >= cells) {
20922 large(lg - 1);
20923 }
20924 } else {
20925 // if rounding was too low, add rows or columns
20926 while (cols * rows < cells) {
20927 var _sm = small();
20928
20929 var _lg = large(); // try to add to larger side first (adds less in multiplication)
20930
20931
20932 if ((_lg + 1) * _sm >= cells) {
20933 large(_lg + 1);
20934 } else {
20935 small(_sm + 1);
20936 }
20937 }
20938 }
20939
20940 var cellWidth = bb.w / cols;
20941 var cellHeight = bb.h / rows;
20942
20943 if (options.condense) {
20944 cellWidth = 0;
20945 cellHeight = 0;
20946 }
20947
20948 if (options.avoidOverlap) {
20949 for (var i = 0; i < nodes.length; i++) {
20950 var node = nodes[i];
20951 var pos = node._private.position;
20952
20953 if (pos.x == null || pos.y == null) {
20954 // for bb
20955 pos.x = 0;
20956 pos.y = 0;
20957 }
20958
20959 var nbb = node.layoutDimensions(options);
20960 var p = options.avoidOverlapPadding;
20961 var w = nbb.w + p;
20962 var h = nbb.h + p;
20963 cellWidth = Math.max(cellWidth, w);
20964 cellHeight = Math.max(cellHeight, h);
20965 }
20966 }
20967
20968 var cellUsed = {}; // e.g. 'c-0-2' => true
20969
20970 var used = function used(row, col) {
20971 return cellUsed['c-' + row + '-' + col] ? true : false;
20972 };
20973
20974 var use = function use(row, col) {
20975 cellUsed['c-' + row + '-' + col] = true;
20976 }; // to keep track of current cell position
20977
20978
20979 var row = 0;
20980 var col = 0;
20981
20982 var moveToNextCell = function moveToNextCell() {
20983 col++;
20984
20985 if (col >= cols) {
20986 col = 0;
20987 row++;
20988 }
20989 }; // get a cache of all the manual positions
20990
20991
20992 var id2manPos = {};
20993
20994 for (var _i = 0; _i < nodes.length; _i++) {
20995 var _node = nodes[_i];
20996 var rcPos = options.position(_node);
20997
20998 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
20999 // must have at least row or col def'd
21000 var _pos = {
21001 row: rcPos.row,
21002 col: rcPos.col
21003 };
21004
21005 if (_pos.col === undefined) {
21006 // find unused col
21007 _pos.col = 0;
21008
21009 while (used(_pos.row, _pos.col)) {
21010 _pos.col++;
21011 }
21012 } else if (_pos.row === undefined) {
21013 // find unused row
21014 _pos.row = 0;
21015
21016 while (used(_pos.row, _pos.col)) {
21017 _pos.row++;
21018 }
21019 }
21020
21021 id2manPos[_node.id()] = _pos;
21022 use(_pos.row, _pos.col);
21023 }
21024 }
21025
21026 var getPos = function getPos(element, i) {
21027 var x, y;
21028
21029 if (element.locked() || element.isParent()) {
21030 return false;
21031 } // see if we have a manual position set
21032
21033
21034 var rcPos = id2manPos[element.id()];
21035
21036 if (rcPos) {
21037 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21038 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21039 } else {
21040 // otherwise set automatically
21041 while (used(row, col)) {
21042 moveToNextCell();
21043 }
21044
21045 x = col * cellWidth + cellWidth / 2 + bb.x1;
21046 y = row * cellHeight + cellHeight / 2 + bb.y1;
21047 use(row, col);
21048 moveToNextCell();
21049 }
21050
21051 return {
21052 x: x,
21053 y: y
21054 };
21055 };
21056
21057 nodes.layoutPositions(this, options, getPos);
21058 }
21059
21060 return this; // chaining
21061};
21062
21063var defaults$e = {
21064 ready: function ready() {},
21065 // on layoutready
21066 stop: function stop() {} // on layoutstop
21067
21068}; // constructor
21069// options : object containing layout options
21070
21071function NullLayout(options) {
21072 this.options = extend({}, defaults$e, options);
21073} // runs the layout
21074
21075
21076NullLayout.prototype.run = function () {
21077 var options = this.options;
21078 var eles = options.eles; // elements to consider in the layout
21079
21080 var layout = this; // cy is automatically populated for us in the constructor
21081 // (disable eslint for next line as this serves as example layout code to external developers)
21082 // eslint-disable-next-line no-unused-vars
21083
21084 var cy = options.cy;
21085 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21086 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21087
21088 eles.nodes().positions(function () {
21089 return {
21090 x: 0,
21091 y: 0
21092 };
21093 }); // trigger layoutready when each node has had its position set at least once
21094
21095 layout.one('layoutready', options.ready);
21096 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21097
21098 layout.one('layoutstop', options.stop);
21099 layout.emit('layoutstop');
21100 return this; // chaining
21101}; // called on continuous layouts to stop them before they finish
21102
21103
21104NullLayout.prototype.stop = function () {
21105 return this; // chaining
21106};
21107
21108var defaults$f = {
21109 positions: undefined,
21110 // map of (node id) => (position obj); or function(node){ return somPos; }
21111 zoom: undefined,
21112 // the zoom level to set (prob want fit = false if set)
21113 pan: undefined,
21114 // the pan level to set (prob want fit = false if set)
21115 fit: true,
21116 // whether to fit to viewport
21117 padding: 30,
21118 // padding on fit
21119 animate: false,
21120 // whether to transition the node positions
21121 animationDuration: 500,
21122 // duration of animation in ms if enabled
21123 animationEasing: undefined,
21124 // easing of animation if enabled
21125 animateFilter: function animateFilter(node, i) {
21126 return true;
21127 },
21128 // 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
21129 ready: undefined,
21130 // callback on layoutready
21131 stop: undefined,
21132 // callback on layoutstop
21133 transform: function transform(node, position) {
21134 return position;
21135 } // transform a given node position. Useful for changing flow direction in discrete layouts
21136
21137};
21138
21139function PresetLayout(options) {
21140 this.options = extend({}, defaults$f, options);
21141}
21142
21143PresetLayout.prototype.run = function () {
21144 var options = this.options;
21145 var eles = options.eles;
21146 var nodes = eles.nodes();
21147 var posIsFn = fn(options.positions);
21148
21149 function getPosition(node) {
21150 if (options.positions == null) {
21151 return copyPosition(node.position());
21152 }
21153
21154 if (posIsFn) {
21155 return options.positions(node);
21156 }
21157
21158 var pos = options.positions[node._private.data.id];
21159
21160 if (pos == null) {
21161 return null;
21162 }
21163
21164 return pos;
21165 }
21166
21167 nodes.layoutPositions(this, options, function (node, i) {
21168 var position = getPosition(node);
21169
21170 if (node.locked() || position == null) {
21171 return false;
21172 }
21173
21174 return position;
21175 });
21176 return this; // chaining
21177};
21178
21179var defaults$g = {
21180 fit: true,
21181 // whether to fit to viewport
21182 padding: 30,
21183 // fit padding
21184 boundingBox: undefined,
21185 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21186 animate: false,
21187 // whether to transition the node positions
21188 animationDuration: 500,
21189 // duration of animation in ms if enabled
21190 animationEasing: undefined,
21191 // easing of animation if enabled
21192 animateFilter: function animateFilter(node, i) {
21193 return true;
21194 },
21195 // 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
21196 ready: undefined,
21197 // callback on layoutready
21198 stop: undefined,
21199 // callback on layoutstop
21200 transform: function transform(node, position) {
21201 return position;
21202 } // transform a given node position. Useful for changing flow direction in discrete layouts
21203
21204};
21205
21206function RandomLayout(options) {
21207 this.options = extend({}, defaults$g, options);
21208}
21209
21210RandomLayout.prototype.run = function () {
21211 var options = this.options;
21212 var cy = options.cy;
21213 var eles = options.eles;
21214 var nodes = eles.nodes().not(':parent');
21215 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21216 x1: 0,
21217 y1: 0,
21218 w: cy.width(),
21219 h: cy.height()
21220 });
21221
21222 var getPos = function getPos(node, i) {
21223 return {
21224 x: bb.x1 + Math.round(Math.random() * bb.w),
21225 y: bb.y1 + Math.round(Math.random() * bb.h)
21226 };
21227 };
21228
21229 nodes.layoutPositions(this, options, getPos);
21230 return this; // chaining
21231};
21232
21233var layout = [{
21234 name: 'breadthfirst',
21235 impl: BreadthFirstLayout
21236}, {
21237 name: 'circle',
21238 impl: CircleLayout
21239}, {
21240 name: 'concentric',
21241 impl: ConcentricLayout
21242}, {
21243 name: 'cose',
21244 impl: CoseLayout
21245}, {
21246 name: 'grid',
21247 impl: GridLayout
21248}, {
21249 name: 'null',
21250 impl: NullLayout
21251}, {
21252 name: 'preset',
21253 impl: PresetLayout
21254}, {
21255 name: 'random',
21256 impl: RandomLayout
21257}];
21258
21259function NullRenderer(options) {
21260 this.options = options;
21261 this.notifications = 0; // for testing
21262}
21263
21264var noop$1 = function noop() {};
21265
21266var throwImgErr = function throwImgErr() {
21267 throw new Error('A headless instance can not render images');
21268};
21269
21270NullRenderer.prototype = {
21271 recalculateRenderedStyle: noop$1,
21272 notify: function notify() {
21273 this.notifications++;
21274 },
21275 init: noop$1,
21276 isHeadless: function isHeadless() {
21277 return true;
21278 },
21279 png: throwImgErr,
21280 jpg: throwImgErr
21281};
21282
21283var BRp = {};
21284BRp.arrowShapeWidth = 0.3;
21285
21286BRp.registerArrowShapes = function () {
21287 var arrowShapes = this.arrowShapes = {};
21288 var renderer = this; // Contract for arrow shapes:
21289 // 0, 0 is arrow tip
21290 // (0, 1) is direction towards node
21291 // (1, 0) is right
21292 //
21293 // functional api:
21294 // collide: check x, y in shape
21295 // roughCollide: called before collide, no false negatives
21296 // draw: draw
21297 // spacing: dist(arrowTip, nodeBoundary)
21298 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21299
21300 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21301 var x1 = translation.x - size / 2 - padding;
21302 var x2 = translation.x + size / 2 + padding;
21303 var y1 = translation.y - size / 2 - padding;
21304 var y2 = translation.y + size / 2 + padding;
21305 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21306 return inside;
21307 };
21308
21309 var transform = function transform(x, y, size, angle, translation) {
21310 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21311 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21312 var xScaled = xRotated * size;
21313 var yScaled = yRotated * size;
21314 var xTranslated = xScaled + translation.x;
21315 var yTranslated = yScaled + translation.y;
21316 return {
21317 x: xTranslated,
21318 y: yTranslated
21319 };
21320 };
21321
21322 var transformPoints = function transformPoints(pts, size, angle, translation) {
21323 var retPts = [];
21324
21325 for (var i = 0; i < pts.length; i += 2) {
21326 var x = pts[i];
21327 var y = pts[i + 1];
21328 retPts.push(transform(x, y, size, angle, translation));
21329 }
21330
21331 return retPts;
21332 };
21333
21334 var pointsToArr = function pointsToArr(pts) {
21335 var ret = [];
21336
21337 for (var i = 0; i < pts.length; i++) {
21338 var p = pts[i];
21339 ret.push(p.x, p.y);
21340 }
21341
21342 return ret;
21343 };
21344
21345 var standardGap = function standardGap(edge) {
21346 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21347 };
21348
21349 var defineArrowShape = function defineArrowShape(name, defn) {
21350 if (string(defn)) {
21351 defn = arrowShapes[defn];
21352 }
21353
21354 arrowShapes[name] = extend({
21355 name: name,
21356 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21357 collide: function collide(x, y, size, angle, translation, padding) {
21358 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21359 var inside = pointInsidePolygonPoints(x, y, points);
21360 return inside;
21361 },
21362 roughCollide: bbCollide,
21363 draw: function draw(context, size, angle, translation) {
21364 var points = transformPoints(this.points, size, angle, translation);
21365 renderer.arrowShapeImpl('polygon')(context, points);
21366 },
21367 spacing: function spacing(edge) {
21368 return 0;
21369 },
21370 gap: standardGap
21371 }, defn);
21372 };
21373
21374 defineArrowShape('none', {
21375 collide: falsify,
21376 roughCollide: falsify,
21377 draw: noop,
21378 spacing: zeroify,
21379 gap: zeroify
21380 });
21381 defineArrowShape('triangle', {
21382 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21383 });
21384 defineArrowShape('arrow', 'triangle');
21385 defineArrowShape('triangle-backcurve', {
21386 points: arrowShapes['triangle'].points,
21387 controlPoint: [0, -0.15],
21388 roughCollide: bbCollide,
21389 draw: function draw(context, size, angle, translation, edgeWidth) {
21390 var ptsTrans = transformPoints(this.points, size, angle, translation);
21391 var ctrlPt = this.controlPoint;
21392 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21393 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21394 },
21395 gap: function gap(edge) {
21396 return standardGap(edge) * 0.8;
21397 }
21398 });
21399 defineArrowShape('triangle-tee', {
21400 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21401 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21402 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21403 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21404 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21405 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21406 return inside;
21407 },
21408 draw: function draw(context, size, angle, translation, edgeWidth) {
21409 var triPts = transformPoints(this.points, size, angle, translation);
21410 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21411 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21412 }
21413 });
21414 defineArrowShape('circle-triangle', {
21415 radius: 0.15,
21416 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21417 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21418 var t = translation;
21419 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21420 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21421 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21422 },
21423 draw: function draw(context, size, angle, translation, edgeWidth) {
21424 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21425 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21426 },
21427 spacing: function spacing(edge) {
21428 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21429 }
21430 });
21431 defineArrowShape('triangle-cross', {
21432 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21433 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21434 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21435 0.15, -0.4],
21436 crossLinePts: function crossLinePts(size, edgeWidth) {
21437 // shift points so that the distance between the cross points matches edge width
21438 var p = this.baseCrossLinePts.slice();
21439 var shiftFactor = edgeWidth / size;
21440 var y0 = 3;
21441 var y1 = 5;
21442 p[y0] = p[y0] - shiftFactor;
21443 p[y1] = p[y1] - shiftFactor;
21444 return p;
21445 },
21446 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21447 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21448 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21449 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21450 return inside;
21451 },
21452 draw: function draw(context, size, angle, translation, edgeWidth) {
21453 var triPts = transformPoints(this.points, size, angle, translation);
21454 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21455 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21456 }
21457 });
21458 defineArrowShape('vee', {
21459 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21460 gap: function gap(edge) {
21461 return standardGap(edge) * 0.525;
21462 }
21463 });
21464 defineArrowShape('circle', {
21465 radius: 0.15,
21466 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21467 var t = translation;
21468 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21469 return inside;
21470 },
21471 draw: function draw(context, size, angle, translation, edgeWidth) {
21472 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21473 },
21474 spacing: function spacing(edge) {
21475 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21476 }
21477 });
21478 defineArrowShape('tee', {
21479 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21480 spacing: function spacing(edge) {
21481 return 1;
21482 },
21483 gap: function gap(edge) {
21484 return 1;
21485 }
21486 });
21487 defineArrowShape('square', {
21488 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21489 });
21490 defineArrowShape('diamond', {
21491 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21492 gap: function gap(edge) {
21493 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21494 }
21495 });
21496 defineArrowShape('chevron', {
21497 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21498 gap: function gap(edge) {
21499 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21500 }
21501 });
21502};
21503
21504var BRp$1 = {}; // Project mouse
21505
21506BRp$1.projectIntoViewport = function (clientX, clientY) {
21507 var cy = this.cy;
21508 var offsets = this.findContainerClientCoords();
21509 var offsetLeft = offsets[0];
21510 var offsetTop = offsets[1];
21511 var scale = offsets[4];
21512 var pan = cy.pan();
21513 var zoom = cy.zoom();
21514 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21515 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21516 return [x, y];
21517};
21518
21519BRp$1.findContainerClientCoords = function () {
21520 if (this.containerBB) {
21521 return this.containerBB;
21522 }
21523
21524 var container = this.container;
21525 var rect = container.getBoundingClientRect();
21526 var style = window$1.getComputedStyle(container);
21527
21528 var styleValue = function styleValue(name) {
21529 return parseFloat(style.getPropertyValue(name));
21530 };
21531
21532 var padding = {
21533 left: styleValue('padding-left'),
21534 right: styleValue('padding-right'),
21535 top: styleValue('padding-top'),
21536 bottom: styleValue('padding-bottom')
21537 };
21538 var border = {
21539 left: styleValue('border-left-width'),
21540 right: styleValue('border-right-width'),
21541 top: styleValue('border-top-width'),
21542 bottom: styleValue('border-bottom-width')
21543 };
21544 var clientWidth = container.clientWidth;
21545 var clientHeight = container.clientHeight;
21546 var paddingHor = padding.left + padding.right;
21547 var paddingVer = padding.top + padding.bottom;
21548 var borderHor = border.left + border.right;
21549 var scale = rect.width / (clientWidth + borderHor);
21550 var unscaledW = clientWidth - paddingHor;
21551 var unscaledH = clientHeight - paddingVer;
21552 var left = rect.left + padding.left + border.left;
21553 var top = rect.top + padding.top + border.top;
21554 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21555};
21556
21557BRp$1.invalidateContainerClientCoordsCache = function () {
21558 this.containerBB = null;
21559};
21560
21561BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21562 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21563};
21564
21565BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21566 var self = this;
21567 var r = this;
21568 var eles = r.getCachedZSortedEles();
21569 var near = []; // 1 node max, 1 edge max
21570
21571 var zoom = r.cy.zoom();
21572 var hasCompounds = r.cy.hasCompoundNodes();
21573 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21574 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21575 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21576 var minSqDist = Infinity;
21577 var nearEdge;
21578 var nearNode;
21579
21580 if (interactiveElementsOnly) {
21581 eles = eles.interactive;
21582 }
21583
21584 function addEle(ele, sqDist) {
21585 if (ele.isNode()) {
21586 if (nearNode) {
21587 return; // can't replace node
21588 } else {
21589 nearNode = ele;
21590 near.push(ele);
21591 }
21592 }
21593
21594 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21595 if (nearEdge) {
21596 // then replace existing edge
21597 // can replace only if same z-index
21598 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) {
21599 for (var i = 0; i < near.length; i++) {
21600 if (near[i].isEdge()) {
21601 near[i] = ele;
21602 nearEdge = ele;
21603 minSqDist = sqDist != null ? sqDist : minSqDist;
21604 break;
21605 }
21606 }
21607 }
21608 } else {
21609 near.push(ele);
21610 nearEdge = ele;
21611 minSqDist = sqDist != null ? sqDist : minSqDist;
21612 }
21613 }
21614 }
21615
21616 function checkNode(node) {
21617 var width = node.outerWidth() + 2 * nodeThreshold;
21618 var height = node.outerHeight() + 2 * nodeThreshold;
21619 var hw = width / 2;
21620 var hh = height / 2;
21621 var pos = node.position();
21622
21623 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21624 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21625 ) {
21626 var shape = r.nodeShapes[self.getNodeShape(node)];
21627
21628 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21629 addEle(node, 0);
21630 return true;
21631 }
21632 }
21633 }
21634
21635 function checkEdge(edge) {
21636 var _p = edge._private;
21637 var rs = _p.rscratch;
21638 var styleWidth = edge.pstyle('width').pfValue;
21639 var scale = edge.pstyle('arrow-scale').value;
21640 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21641
21642 var widthSq = width * width;
21643 var width2 = width * 2;
21644 var src = _p.source;
21645 var tgt = _p.target;
21646 var sqDist;
21647
21648 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21649 var pts = rs.allpts;
21650
21651 for (var i = 0; i + 3 < pts.length; i += 2) {
21652 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]))) {
21653 addEle(edge, sqDist);
21654 return true;
21655 }
21656 }
21657 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21658 var pts = rs.allpts;
21659
21660 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21661 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]))) {
21662 addEle(edge, sqDist);
21663 return true;
21664 }
21665 }
21666 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21667
21668
21669 var src = src || _p.source;
21670 var tgt = tgt || _p.target;
21671 var arSize = self.getArrowWidth(styleWidth, scale);
21672 var arrows = [{
21673 name: 'source',
21674 x: rs.arrowStartX,
21675 y: rs.arrowStartY,
21676 angle: rs.srcArrowAngle
21677 }, {
21678 name: 'target',
21679 x: rs.arrowEndX,
21680 y: rs.arrowEndY,
21681 angle: rs.tgtArrowAngle
21682 }, {
21683 name: 'mid-source',
21684 x: rs.midX,
21685 y: rs.midY,
21686 angle: rs.midsrcArrowAngle
21687 }, {
21688 name: 'mid-target',
21689 x: rs.midX,
21690 y: rs.midY,
21691 angle: rs.midtgtArrowAngle
21692 }];
21693
21694 for (var i = 0; i < arrows.length; i++) {
21695 var ar = arrows[i];
21696 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21697 var edgeWidth = edge.pstyle('width').pfValue;
21698
21699 if (shape.roughCollide(x, y, arSize, ar.angle, {
21700 x: ar.x,
21701 y: ar.y
21702 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21703 x: ar.x,
21704 y: ar.y
21705 }, edgeWidth, edgeThreshold)) {
21706 addEle(edge);
21707 return true;
21708 }
21709 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21710
21711
21712 if (hasCompounds && near.length > 0) {
21713 checkNode(src);
21714 checkNode(tgt);
21715 }
21716 }
21717
21718 function preprop(obj, name, pre) {
21719 return getPrefixedProperty(obj, name, pre);
21720 }
21721
21722 function checkLabel(ele, prefix) {
21723 var _p = ele._private;
21724 var th = labelThreshold;
21725 var prefixDash;
21726
21727 if (prefix) {
21728 prefixDash = prefix + '-';
21729 } else {
21730 prefixDash = '';
21731 }
21732
21733 ele.boundingBox();
21734 var bb = _p.labelBounds[prefix || 'main'];
21735 var text = ele.pstyle(prefixDash + 'label').value;
21736 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21737
21738 if (!eventsEnabled || !text) {
21739 return;
21740 }
21741
21742 var rstyle = _p.rstyle;
21743 var lx = preprop(rstyle, 'labelX', prefix);
21744 var ly = preprop(rstyle, 'labelY', prefix);
21745 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21746 var lx1 = bb.x1 - th;
21747 var lx2 = bb.x2 + th;
21748 var ly1 = bb.y1 - th;
21749 var ly2 = bb.y2 + th;
21750
21751 if (theta) {
21752 var cos = Math.cos(theta);
21753 var sin = Math.sin(theta);
21754
21755 var rotate = function rotate(x, y) {
21756 x = x - lx;
21757 y = y - ly;
21758 return {
21759 x: x * cos - y * sin + lx,
21760 y: x * sin + y * cos + ly
21761 };
21762 };
21763
21764 var px1y1 = rotate(lx1, ly1);
21765 var px1y2 = rotate(lx1, ly2);
21766 var px2y1 = rotate(lx2, ly1);
21767 var px2y2 = rotate(lx2, ly2);
21768 var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
21769
21770 if (pointInsidePolygonPoints(x, y, points)) {
21771 addEle(ele);
21772 return true;
21773 }
21774 } else {
21775 // do a cheaper bb check
21776 if (inBoundingBox(bb, x, y)) {
21777 addEle(ele);
21778 return true;
21779 }
21780 }
21781 }
21782
21783 for (var i = eles.length - 1; i >= 0; i--) {
21784 // reverse order for precedence
21785 var ele = eles[i];
21786
21787 if (ele.isNode()) {
21788 checkNode(ele) || checkLabel(ele);
21789 } else {
21790 // then edge
21791 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21792 }
21793 }
21794
21795 return near;
21796}; // 'Give me everything from this box'
21797
21798
21799BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21800 var eles = this.getCachedZSortedEles().interactive;
21801 var box = [];
21802 var x1c = Math.min(x1, x2);
21803 var x2c = Math.max(x1, x2);
21804 var y1c = Math.min(y1, y2);
21805 var y2c = Math.max(y1, y2);
21806 x1 = x1c;
21807 x2 = x2c;
21808 y1 = y1c;
21809 y2 = y2c;
21810 var boxBb = makeBoundingBox({
21811 x1: x1,
21812 y1: y1,
21813 x2: x2,
21814 y2: y2
21815 });
21816
21817 for (var e = 0; e < eles.length; e++) {
21818 var ele = eles[e];
21819
21820 if (ele.isNode()) {
21821 var node = ele;
21822 var nodeBb = node.boundingBox({
21823 includeNodes: true,
21824 includeEdges: false,
21825 includeLabels: false
21826 });
21827
21828 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21829 box.push(node);
21830 }
21831 } else {
21832 var edge = ele;
21833 var _p = edge._private;
21834 var rs = _p.rscratch;
21835
21836 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21837 continue;
21838 }
21839
21840 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21841 continue;
21842 }
21843
21844 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21845 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21846 var allInside = true;
21847
21848 for (var i = 0; i < pts.length; i++) {
21849 if (!pointInBoundingBox(boxBb, pts[i])) {
21850 allInside = false;
21851 break;
21852 }
21853 }
21854
21855 if (allInside) {
21856 box.push(edge);
21857 }
21858 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
21859 box.push(edge);
21860 }
21861 }
21862 }
21863
21864 return box;
21865};
21866
21867var BRp$2 = {};
21868
21869BRp$2.calculateArrowAngles = function (edge) {
21870 var rs = edge._private.rscratch;
21871 var isHaystack = rs.edgeType === 'haystack';
21872 var isBezier = rs.edgeType === 'bezier';
21873 var isMultibezier = rs.edgeType === 'multibezier';
21874 var isSegments = rs.edgeType === 'segments';
21875 var isCompound = rs.edgeType === 'compound';
21876 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
21877
21878 var dispX, dispY;
21879 var startX, startY, endX, endY, midX, midY;
21880
21881 if (isHaystack) {
21882 startX = rs.haystackPts[0];
21883 startY = rs.haystackPts[1];
21884 endX = rs.haystackPts[2];
21885 endY = rs.haystackPts[3];
21886 } else {
21887 startX = rs.arrowStartX;
21888 startY = rs.arrowStartY;
21889 endX = rs.arrowEndX;
21890 endY = rs.arrowEndY;
21891 }
21892
21893 midX = rs.midX;
21894 midY = rs.midY; // source
21895 //
21896
21897 if (isSegments) {
21898 dispX = startX - rs.segpts[0];
21899 dispY = startY - rs.segpts[1];
21900 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21901 var pts = rs.allpts;
21902 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
21903 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
21904 dispX = startX - bX;
21905 dispY = startY - bY;
21906 } else {
21907 dispX = startX - midX;
21908 dispY = startY - midY;
21909 }
21910
21911 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
21912 //
21913
21914 var midX = rs.midX;
21915 var midY = rs.midY;
21916
21917 if (isHaystack) {
21918 midX = (startX + endX) / 2;
21919 midY = (startY + endY) / 2;
21920 }
21921
21922 dispX = endX - startX;
21923 dispY = endY - startY;
21924
21925 if (isSegments) {
21926 var pts = rs.allpts;
21927
21928 if (pts.length / 2 % 2 === 0) {
21929 var i2 = pts.length / 2;
21930 var i1 = i2 - 2;
21931 dispX = pts[i2] - pts[i1];
21932 dispY = pts[i2 + 1] - pts[i1 + 1];
21933 } else {
21934 var i2 = pts.length / 2 - 1;
21935 var i1 = i2 - 2;
21936 var i3 = i2 + 2;
21937 dispX = pts[i2] - pts[i1];
21938 dispY = pts[i2 + 1] - pts[i1 + 1];
21939 }
21940 } else if (isMultibezier || isCompound || isSelf) {
21941 var pts = rs.allpts;
21942 var cpts = rs.ctrlpts;
21943 var bp0x, bp0y;
21944 var bp1x, bp1y;
21945
21946 if (cpts.length / 2 % 2 === 0) {
21947 var p0 = pts.length / 2 - 1; // startpt
21948
21949 var ic = p0 + 2;
21950 var p1 = ic + 2;
21951 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
21952 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
21953 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
21954 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
21955 } else {
21956 var ic = pts.length / 2 - 1; // ctrpt
21957
21958 var p0 = ic - 2; // startpt
21959
21960 var p1 = ic + 2; // endpt
21961
21962 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
21963 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
21964 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
21965 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
21966 }
21967
21968 dispX = bp1x - bp0x;
21969 dispY = bp1y - bp0y;
21970 }
21971
21972 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
21973 rs.midDispX = dispX;
21974 rs.midDispY = dispY; // mid source
21975 //
21976
21977 dispX *= -1;
21978 dispY *= -1;
21979
21980 if (isSegments) {
21981 var pts = rs.allpts;
21982
21983 if (pts.length / 2 % 2 === 0) ; else {
21984 var i2 = pts.length / 2 - 1;
21985 var i3 = i2 + 2;
21986 dispX = -(pts[i3] - pts[i2]);
21987 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
21988 }
21989 }
21990
21991 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
21992 //
21993
21994 if (isSegments) {
21995 dispX = endX - rs.segpts[rs.segpts.length - 2];
21996 dispY = endY - rs.segpts[rs.segpts.length - 1];
21997 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21998 var pts = rs.allpts;
21999 var l = pts.length;
22000 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22001 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22002 dispX = endX - bX;
22003 dispY = endY - bY;
22004 } else {
22005 dispX = endX - midX;
22006 dispY = endY - midY;
22007 }
22008
22009 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22010};
22011
22012BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22013 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22014 var cachedVal = cache[edgeWidth + ', ' + scale];
22015
22016 if (cachedVal) {
22017 return cachedVal;
22018 }
22019
22020 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22021 cache[edgeWidth + ', ' + scale] = cachedVal;
22022 return cachedVal;
22023};
22024
22025var BRp$3 = {};
22026
22027BRp$3.findHaystackPoints = function (edges) {
22028 for (var i = 0; i < edges.length; i++) {
22029 var edge = edges[i];
22030 var _p = edge._private;
22031 var rs = _p.rscratch;
22032
22033 if (!rs.haystack) {
22034 var angle = Math.random() * 2 * Math.PI;
22035 rs.source = {
22036 x: Math.cos(angle),
22037 y: Math.sin(angle)
22038 };
22039 angle = Math.random() * 2 * Math.PI;
22040 rs.target = {
22041 x: Math.cos(angle),
22042 y: Math.sin(angle)
22043 };
22044 }
22045
22046 var src = _p.source;
22047 var tgt = _p.target;
22048 var srcPos = src.position();
22049 var tgtPos = tgt.position();
22050 var srcW = src.width();
22051 var tgtW = tgt.width();
22052 var srcH = src.height();
22053 var tgtH = tgt.height();
22054 var radius = edge.pstyle('haystack-radius').value;
22055 var halfRadius = radius / 2; // b/c have to half width/height
22056
22057 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];
22058 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22059 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22060
22061 rs.edgeType = 'haystack';
22062 rs.haystack = true;
22063 this.storeEdgeProjections(edge);
22064 this.calculateArrowAngles(edge);
22065 this.recalculateEdgeLabelProjections(edge);
22066 this.calculateLabelAngles(edge);
22067 }
22068};
22069
22070BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22071 // Segments (multiple straight lines)
22072 var rs = edge._private.rscratch;
22073 var posPts = pairInfo.posPts,
22074 intersectionPts = pairInfo.intersectionPts,
22075 vectorNormInverse = pairInfo.vectorNormInverse;
22076 var edgeDistances = edge.pstyle('edge-distances').value;
22077 var segmentWs = edge.pstyle('segment-weights');
22078 var segmentDs = edge.pstyle('segment-distances');
22079 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22080 rs.edgeType = 'segments';
22081 rs.segpts = [];
22082
22083 for (var s = 0; s < segmentsN; s++) {
22084 var w = segmentWs.pfValue[s];
22085 var d = segmentDs.pfValue[s];
22086 var w1 = 1 - w;
22087 var w2 = w;
22088 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22089 var adjustedMidpt = {
22090 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22091 y: midptPts.y1 * w1 + midptPts.y2 * w2
22092 };
22093 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22094 }
22095};
22096
22097BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22098 // Self-edge
22099 var rs = edge._private.rscratch;
22100 var dirCounts = pairInfo.dirCounts,
22101 srcPos = pairInfo.srcPos;
22102 var ctrlptDists = edge.pstyle('control-point-distances');
22103 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22104 var loopDir = edge.pstyle('loop-direction').pfValue;
22105 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22106 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22107 rs.edgeType = 'self';
22108 var j = i;
22109 var loopDist = stepSize;
22110
22111 if (edgeIsUnbundled) {
22112 j = 0;
22113 loopDist = ctrlptDist;
22114 }
22115
22116 var loopAngle = loopDir - Math.PI / 2;
22117 var outAngle = loopAngle - loopSwp / 2;
22118 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22119
22120 var dc = String(loopDir + '_' + loopSwp);
22121 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22122 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)];
22123};
22124
22125BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22126 // Compound edge
22127 var rs = edge._private.rscratch;
22128 rs.edgeType = 'compound';
22129 var srcPos = pairInfo.srcPos,
22130 tgtPos = pairInfo.tgtPos,
22131 srcW = pairInfo.srcW,
22132 srcH = pairInfo.srcH,
22133 tgtW = pairInfo.tgtW,
22134 tgtH = pairInfo.tgtH;
22135 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22136 var ctrlptDists = edge.pstyle('control-point-distances');
22137 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22138 var j = i;
22139 var loopDist = stepSize;
22140
22141 if (edgeIsUnbundled) {
22142 j = 0;
22143 loopDist = ctrlptDist;
22144 }
22145
22146 var loopW = 50;
22147 var loopaPos = {
22148 x: srcPos.x - srcW / 2,
22149 y: srcPos.y - srcH / 2
22150 };
22151 var loopbPos = {
22152 x: tgtPos.x - tgtW / 2,
22153 y: tgtPos.y - tgtH / 2
22154 };
22155 var loopPos = {
22156 x: Math.min(loopaPos.x, loopbPos.x),
22157 y: Math.min(loopaPos.y, loopbPos.y)
22158 }; // avoids cases with impossible beziers
22159
22160 var minCompoundStretch = 0.5;
22161 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22162 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22163 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];
22164};
22165
22166BRp$3.findStraightEdgePoints = function (edge) {
22167 // Straight edge within bundle
22168 edge._private.rscratch.edgeType = 'straight';
22169};
22170
22171BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22172 var rs = edge._private.rscratch;
22173 var vectorNormInverse = pairInfo.vectorNormInverse,
22174 posPts = pairInfo.posPts,
22175 intersectionPts = pairInfo.intersectionPts;
22176 var edgeDistances = edge.pstyle('edge-distances').value;
22177 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22178 var ctrlptDists = edge.pstyle('control-point-distances');
22179 var ctrlptWs = edge.pstyle('control-point-weights');
22180 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22181 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22182 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22183
22184 var multi = edgeIsUnbundled;
22185 rs.edgeType = multi ? 'multibezier' : 'bezier';
22186 rs.ctrlpts = [];
22187
22188 for (var b = 0; b < bezierN; b++) {
22189 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22190 var manctrlptDist = void 0;
22191 var sign = signum(normctrlptDist);
22192
22193 if (multi) {
22194 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22195
22196 ctrlptWeight = ctrlptWs.value[b];
22197 }
22198
22199 if (edgeIsUnbundled) {
22200 // multi or single unbundled
22201 manctrlptDist = ctrlptDist;
22202 } else {
22203 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22204 }
22205
22206 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22207 var w1 = 1 - ctrlptWeight;
22208 var w2 = ctrlptWeight;
22209 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22210 var adjustedMidpt = {
22211 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22212 y: midptPts.y1 * w1 + midptPts.y2 * w2
22213 };
22214 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22215 }
22216};
22217
22218BRp$3.findTaxiPoints = function (edge, pairInfo) {
22219 // Taxicab geometry with two turns maximum
22220 var rs = edge._private.rscratch;
22221 rs.edgeType = 'segments';
22222 var VERTICAL = 'vertical';
22223 var HORIZONTAL = 'horizontal';
22224 var LEFTWARD = 'leftward';
22225 var RIGHTWARD = 'rightward';
22226 var DOWNWARD = 'downward';
22227 var UPWARD = 'upward';
22228 var AUTO = 'auto';
22229 var posPts = pairInfo.posPts,
22230 srcW = pairInfo.srcW,
22231 srcH = pairInfo.srcH,
22232 tgtW = pairInfo.tgtW,
22233 tgtH = pairInfo.tgtH;
22234 var edgeDistances = edge.pstyle('edge-distances').value;
22235 var dIncludesNodeBody = edgeDistances !== 'node-position';
22236 var taxiDir = edge.pstyle('taxi-direction').value;
22237 var rawTaxiDir = taxiDir; // unprocessed value
22238
22239 var taxiTurn = edge.pstyle('taxi-turn');
22240 var turnIsPercent = taxiTurn.units === '%';
22241 var taxiTurnPfVal = taxiTurn.pfValue;
22242 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22243
22244 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22245 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22246 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22247 var pdx = posPts.x2 - posPts.x1;
22248 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22249
22250 var subDWH = function subDWH(dxy, dwh) {
22251 if (dxy > 0) {
22252 return Math.max(dxy - dwh, 0);
22253 } else {
22254 return Math.min(dxy + dwh, 0);
22255 }
22256 };
22257
22258 var dx = subDWH(pdx, dw);
22259 var dy = subDWH(pdy, dh);
22260 var isExplicitDir = false;
22261
22262 if (rawTaxiDir === AUTO) {
22263 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22264 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22265 taxiDir = VERTICAL;
22266 isExplicitDir = true;
22267 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22268 taxiDir = HORIZONTAL;
22269 isExplicitDir = true;
22270 }
22271
22272 var isVert = taxiDir === VERTICAL;
22273 var l = isVert ? dy : dx;
22274 var pl = isVert ? pdy : pdx;
22275 var sgnL = signum(pl);
22276 var forcedDir = false;
22277
22278 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22279 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22280 sgnL *= -1;
22281 l = sgnL * Math.abs(l);
22282 forcedDir = true;
22283 }
22284
22285 var d;
22286
22287 if (turnIsPercent) {
22288 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22289 d = p * l;
22290 } else {
22291 var k = taxiTurnPfVal < 0 ? l : 0;
22292 d = k + taxiTurnPfVal * sgnL;
22293 }
22294
22295 var getIsTooClose = function getIsTooClose(d) {
22296 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22297 };
22298
22299 var isTooCloseSrc = getIsTooClose(d);
22300 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22301 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22302
22303 if (isTooClose && !forcedDir) {
22304 // non-ideal routing
22305 if (isVert) {
22306 // vertical fallbacks
22307 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22308 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22309
22310 if (lShapeInsideSrc) {
22311 // horizontal Z-shape (direction not respected)
22312 var x = (posPts.x1 + posPts.x2) / 2;
22313 var y1 = posPts.y1,
22314 y2 = posPts.y2;
22315 rs.segpts = [x, y1, x, y2];
22316 } else if (lShapeInsideTgt) {
22317 // vertical Z-shape (distance not respected)
22318 var y = (posPts.y1 + posPts.y2) / 2;
22319 var x1 = posPts.x1,
22320 x2 = posPts.x2;
22321 rs.segpts = [x1, y, x2, y];
22322 } else {
22323 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22324 rs.segpts = [posPts.x1, posPts.y2];
22325 }
22326 } else {
22327 // horizontal fallbacks
22328 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22329
22330 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22331
22332 if (_lShapeInsideSrc) {
22333 // vertical Z-shape (direction not respected)
22334 var _y = (posPts.y1 + posPts.y2) / 2;
22335
22336 var _x = posPts.x1,
22337 _x2 = posPts.x2;
22338 rs.segpts = [_x, _y, _x2, _y];
22339 } else if (_lShapeInsideTgt) {
22340 // horizontal Z-shape (turn distance not respected)
22341 var _x3 = (posPts.x1 + posPts.x2) / 2;
22342
22343 var _y2 = posPts.y1,
22344 _y3 = posPts.y2;
22345 rs.segpts = [_x3, _y2, _x3, _y3];
22346 } else {
22347 // L-shape (turn distance not respected, but works well for tree siblings)
22348 rs.segpts = [posPts.x2, posPts.y1];
22349 }
22350 }
22351 } else {
22352 // ideal routing
22353 if (isVert) {
22354 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22355
22356 var _x4 = posPts.x1,
22357 _x5 = posPts.x2;
22358 rs.segpts = [_x4, _y4, _x5, _y4];
22359 } else {
22360 // horizontal
22361 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22362
22363 var _y5 = posPts.y1,
22364 _y6 = posPts.y2;
22365 rs.segpts = [_x6, _y5, _x6, _y6];
22366 }
22367 }
22368};
22369
22370BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22371 var rs = edge._private.rscratch; // can only correct beziers for now...
22372
22373 if (rs.edgeType === 'bezier') {
22374 var srcPos = pairInfo.srcPos,
22375 tgtPos = pairInfo.tgtPos,
22376 srcW = pairInfo.srcW,
22377 srcH = pairInfo.srcH,
22378 tgtW = pairInfo.tgtW,
22379 tgtH = pairInfo.tgtH,
22380 srcShape = pairInfo.srcShape,
22381 tgtShape = pairInfo.tgtShape;
22382 var badStart = !number(rs.startX) || !number(rs.startY);
22383 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22384 var badEnd = !number(rs.endX) || !number(rs.endY);
22385 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22386 var minCpADistFactor = 3;
22387 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22388 var minCpADist = minCpADistFactor * arrowW;
22389 var startACpDist = dist({
22390 x: rs.ctrlpts[0],
22391 y: rs.ctrlpts[1]
22392 }, {
22393 x: rs.startX,
22394 y: rs.startY
22395 });
22396 var closeStartACp = startACpDist < minCpADist;
22397 var endACpDist = dist({
22398 x: rs.ctrlpts[0],
22399 y: rs.ctrlpts[1]
22400 }, {
22401 x: rs.endX,
22402 y: rs.endY
22403 });
22404 var closeEndACp = endACpDist < minCpADist;
22405 var overlapping = false;
22406
22407 if (badStart || badAStart || closeStartACp) {
22408 overlapping = true; // project control point along line from src centre to outside the src shape
22409 // (otherwise intersection will yield nothing)
22410
22411 var cpD = {
22412 // delta
22413 x: rs.ctrlpts[0] - srcPos.x,
22414 y: rs.ctrlpts[1] - srcPos.y
22415 };
22416 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22417
22418 var cpM = {
22419 // normalised delta
22420 x: cpD.x / cpL,
22421 y: cpD.y / cpL
22422 };
22423 var radius = Math.max(srcW, srcH);
22424 var cpProj = {
22425 // *2 radius guarantees outside shape
22426 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22427 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22428 };
22429 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22430
22431 if (closeStartACp) {
22432 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22433 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22434 } else {
22435 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22436 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22437 }
22438 }
22439
22440 if (badEnd || badAEnd || closeEndACp) {
22441 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22442 // (otherwise intersection will yield nothing)
22443
22444 var _cpD = {
22445 // delta
22446 x: rs.ctrlpts[0] - tgtPos.x,
22447 y: rs.ctrlpts[1] - tgtPos.y
22448 };
22449
22450 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22451
22452
22453 var _cpM = {
22454 // normalised delta
22455 x: _cpD.x / _cpL,
22456 y: _cpD.y / _cpL
22457 };
22458
22459 var _radius = Math.max(srcW, srcH);
22460
22461 var _cpProj = {
22462 // *2 radius guarantees outside shape
22463 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22464 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22465 };
22466 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22467
22468 if (closeEndACp) {
22469 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22470 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22471 } else {
22472 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22473 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22474 }
22475 }
22476
22477 if (overlapping) {
22478 // recalc endpts
22479 this.findEndpoints(edge);
22480 }
22481 }
22482};
22483
22484BRp$3.storeAllpts = function (edge) {
22485 var rs = edge._private.rscratch;
22486
22487 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22488 rs.allpts = [];
22489 rs.allpts.push(rs.startX, rs.startY);
22490
22491 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22492 // ctrl pt itself
22493 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22494
22495 if (b + 3 < rs.ctrlpts.length) {
22496 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22497 }
22498 }
22499
22500 rs.allpts.push(rs.endX, rs.endY);
22501 var m, mt;
22502
22503 if (rs.ctrlpts.length / 2 % 2 === 0) {
22504 m = rs.allpts.length / 2 - 1;
22505 rs.midX = rs.allpts[m];
22506 rs.midY = rs.allpts[m + 1];
22507 } else {
22508 m = rs.allpts.length / 2 - 3;
22509 mt = 0.5;
22510 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22511 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22512 }
22513 } else if (rs.edgeType === 'straight') {
22514 // need to calc these after endpts
22515 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22516
22517 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22518 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22519 } else if (rs.edgeType === 'segments') {
22520 rs.allpts = [];
22521 rs.allpts.push(rs.startX, rs.startY);
22522 rs.allpts.push.apply(rs.allpts, rs.segpts);
22523 rs.allpts.push(rs.endX, rs.endY);
22524
22525 if (rs.segpts.length % 4 === 0) {
22526 var i2 = rs.segpts.length / 2;
22527 var i1 = i2 - 2;
22528 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22529 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22530 } else {
22531 var _i = rs.segpts.length / 2 - 1;
22532
22533 rs.midX = rs.segpts[_i];
22534 rs.midY = rs.segpts[_i + 1];
22535 }
22536 }
22537};
22538
22539BRp$3.checkForInvalidEdgeWarning = function (edge) {
22540 var rs = edge[0]._private.rscratch;
22541
22542 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22543 rs.loggedErr = false;
22544 } else {
22545 if (!rs.loggedErr) {
22546 rs.loggedErr = true;
22547 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.');
22548 }
22549 }
22550};
22551
22552BRp$3.findEdgeControlPoints = function (edges) {
22553 var _this = this;
22554
22555 if (!edges || edges.length === 0) {
22556 return;
22557 }
22558
22559 var r = this;
22560 var cy = r.cy;
22561 var hasCompounds = cy.hasCompoundNodes();
22562 var hashTable = {
22563 map: new Map$1(),
22564 get: function get(pairId) {
22565 var map2 = this.map.get(pairId[0]);
22566
22567 if (map2 != null) {
22568 return map2.get(pairId[1]);
22569 } else {
22570 return null;
22571 }
22572 },
22573 set: function set(pairId, val) {
22574 var map2 = this.map.get(pairId[0]);
22575
22576 if (map2 == null) {
22577 map2 = new Map$1();
22578 this.map.set(pairId[0], map2);
22579 }
22580
22581 map2.set(pairId[1], val);
22582 }
22583 };
22584 var pairIds = [];
22585 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22586
22587 for (var i = 0; i < edges.length; i++) {
22588 var edge = edges[i];
22589 var _p = edge._private;
22590 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22591 // they shouldn't take up space
22592
22593 if (edge.removed() || !edge.takesUpSpace()) {
22594 continue;
22595 }
22596
22597 if (curveStyle === 'haystack') {
22598 haystackEdges.push(edge);
22599 continue;
22600 }
22601
22602 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
22603 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22604 var src = _p.source;
22605 var tgt = _p.target;
22606 var srcIndex = src.poolIndex();
22607 var tgtIndex = tgt.poolIndex();
22608 var pairId = [srcIndex, tgtIndex].sort();
22609 var tableEntry = hashTable.get(pairId);
22610
22611 if (tableEntry == null) {
22612 tableEntry = {
22613 eles: []
22614 };
22615 hashTable.set(pairId, tableEntry);
22616 pairIds.push(pairId);
22617 }
22618
22619 tableEntry.eles.push(edge);
22620
22621 if (edgeIsUnbundled) {
22622 tableEntry.hasUnbundled = true;
22623 }
22624
22625 if (edgeIsBezier) {
22626 tableEntry.hasBezier = true;
22627 }
22628 } // for each pair (src, tgt), create the ctrl pts
22629 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22630
22631
22632 var _loop = function _loop(p) {
22633 var pairId = pairIds[p];
22634 var pairInfo = hashTable.get(pairId);
22635 var swappedpairInfo = void 0;
22636
22637 if (!pairInfo.hasUnbundled) {
22638 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22639 return e.isBundledBezier();
22640 });
22641 clearArray(pairInfo.eles);
22642 pllEdges.forEach(function (edge) {
22643 return pairInfo.eles.push(edge);
22644 }); // for each pair id, the edges should be sorted by index
22645
22646 pairInfo.eles.sort(function (edge1, edge2) {
22647 return edge1.poolIndex() - edge2.poolIndex();
22648 });
22649 }
22650
22651 var firstEdge = pairInfo.eles[0];
22652 var src = firstEdge.source();
22653 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22654
22655 if (src.poolIndex() > tgt.poolIndex()) {
22656 var temp = src;
22657 src = tgt;
22658 tgt = temp;
22659 }
22660
22661 var srcPos = pairInfo.srcPos = src.position();
22662 var tgtPos = pairInfo.tgtPos = tgt.position();
22663 var srcW = pairInfo.srcW = src.outerWidth();
22664 var srcH = pairInfo.srcH = src.outerHeight();
22665 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22666 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22667
22668 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22669
22670 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22671
22672 pairInfo.dirCounts = {
22673 'north': 0,
22674 'west': 0,
22675 'south': 0,
22676 'east': 0,
22677 'northwest': 0,
22678 'southwest': 0,
22679 'northeast': 0,
22680 'southeast': 0
22681 };
22682
22683 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22684 var _edge = pairInfo.eles[_i2];
22685 var rs = _edge[0]._private.rscratch;
22686
22687 var _curveStyle = _edge.pstyle('curve-style').value;
22688
22689 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22690
22691
22692 var edgeIsSwapped = !src.same(_edge.source());
22693
22694 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22695 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22696
22697 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22698 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22699
22700 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22701 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22702 var intersectionPts = pairInfo.intersectionPts = {
22703 x1: srcOutside[0],
22704 x2: tgtOutside[0],
22705 y1: srcOutside[1],
22706 y2: tgtOutside[1]
22707 };
22708 var posPts = pairInfo.posPts = {
22709 x1: srcPos.x,
22710 x2: tgtPos.x,
22711 y1: srcPos.y,
22712 y2: tgtPos.y
22713 };
22714 var dy = tgtOutside[1] - srcOutside[1];
22715 var dx = tgtOutside[0] - srcOutside[0];
22716 var l = Math.sqrt(dx * dx + dy * dy);
22717 var vector = pairInfo.vector = {
22718 x: dx,
22719 y: dy
22720 };
22721 var vectorNorm = pairInfo.vectorNorm = {
22722 x: vector.x / l,
22723 y: vector.y / l
22724 };
22725 var vectorNormInverse = {
22726 x: -vectorNorm.y,
22727 y: vectorNorm.x
22728 }; // if node shapes overlap, then no ctrl pts to draw
22729
22730 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);
22731 pairInfo.vectorNormInverse = vectorNormInverse;
22732 swappedpairInfo = {
22733 nodesOverlap: pairInfo.nodesOverlap,
22734 dirCounts: pairInfo.dirCounts,
22735 calculatedIntersection: true,
22736 hasBezier: pairInfo.hasBezier,
22737 hasUnbundled: pairInfo.hasUnbundled,
22738 eles: pairInfo.eles,
22739 srcPos: tgtPos,
22740 tgtPos: srcPos,
22741 srcW: tgtW,
22742 srcH: tgtH,
22743 tgtW: srcW,
22744 tgtH: srcH,
22745 srcIntn: tgtIntn,
22746 tgtIntn: srcIntn,
22747 srcShape: tgtShape,
22748 tgtShape: srcShape,
22749 posPts: {
22750 x1: posPts.x2,
22751 y1: posPts.y2,
22752 x2: posPts.x1,
22753 y2: posPts.y1
22754 },
22755 intersectionPts: {
22756 x1: intersectionPts.x2,
22757 y1: intersectionPts.y2,
22758 x2: intersectionPts.x1,
22759 y2: intersectionPts.y1
22760 },
22761 vector: {
22762 x: -vector.x,
22763 y: -vector.y
22764 },
22765 vectorNorm: {
22766 x: -vectorNorm.x,
22767 y: -vectorNorm.y
22768 },
22769 vectorNormInverse: {
22770 x: -vectorNormInverse.x,
22771 y: -vectorNormInverse.y
22772 }
22773 };
22774 }
22775
22776 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22777 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22778 rs.srcIntn = passedPairInfo.srcIntn;
22779 rs.tgtIntn = passedPairInfo.tgtIntn;
22780
22781 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22782 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22783 } else if (src === tgt) {
22784 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22785 } else if (_curveStyle === 'segments') {
22786 _this.findSegmentsPoints(_edge, passedPairInfo);
22787 } else if (_curveStyle === 'taxi') {
22788 _this.findTaxiPoints(_edge, passedPairInfo);
22789 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22790 _this.findStraightEdgePoints(_edge);
22791 } else {
22792 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22793 }
22794
22795 _this.findEndpoints(_edge);
22796
22797 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22798
22799 _this.checkForInvalidEdgeWarning(_edge);
22800
22801 _this.storeAllpts(_edge);
22802
22803 _this.storeEdgeProjections(_edge);
22804
22805 _this.calculateArrowAngles(_edge);
22806
22807 _this.recalculateEdgeLabelProjections(_edge);
22808
22809 _this.calculateLabelAngles(_edge);
22810 } // for pair edges
22811
22812 };
22813
22814 for (var p = 0; p < pairIds.length; p++) {
22815 _loop(p);
22816 } // for pair ids
22817 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22818
22819
22820 this.findHaystackPoints(haystackEdges);
22821};
22822
22823function getPts(pts) {
22824 var retPts = [];
22825
22826 if (pts == null) {
22827 return;
22828 }
22829
22830 for (var i = 0; i < pts.length; i += 2) {
22831 var x = pts[i];
22832 var y = pts[i + 1];
22833 retPts.push({
22834 x: x,
22835 y: y
22836 });
22837 }
22838
22839 return retPts;
22840}
22841
22842BRp$3.getSegmentPoints = function (edge) {
22843 var rs = edge[0]._private.rscratch;
22844 var type = rs.edgeType;
22845
22846 if (type === 'segments') {
22847 this.recalculateRenderedStyle(edge);
22848 return getPts(rs.segpts);
22849 }
22850};
22851
22852BRp$3.getControlPoints = function (edge) {
22853 var rs = edge[0]._private.rscratch;
22854 var type = rs.edgeType;
22855
22856 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
22857 this.recalculateRenderedStyle(edge);
22858 return getPts(rs.ctrlpts);
22859 }
22860};
22861
22862BRp$3.getEdgeMidpoint = function (edge) {
22863 var rs = edge[0]._private.rscratch;
22864 this.recalculateRenderedStyle(edge);
22865 return {
22866 x: rs.midX,
22867 y: rs.midY
22868 };
22869};
22870
22871var BRp$4 = {};
22872
22873BRp$4.manualEndptToPx = function (node, prop) {
22874 var r = this;
22875 var npos = node.position();
22876 var w = node.outerWidth();
22877 var h = node.outerHeight();
22878
22879 if (prop.value.length === 2) {
22880 var p = [prop.pfValue[0], prop.pfValue[1]];
22881
22882 if (prop.units[0] === '%') {
22883 p[0] = p[0] * w;
22884 }
22885
22886 if (prop.units[1] === '%') {
22887 p[1] = p[1] * h;
22888 }
22889
22890 p[0] += npos.x;
22891 p[1] += npos.y;
22892 return p;
22893 } else {
22894 var angle = prop.pfValue[0];
22895 angle = -Math.PI / 2 + angle; // start at 12 o'clock
22896
22897 var l = 2 * Math.max(w, h);
22898 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
22899 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
22900 }
22901};
22902
22903BRp$4.findEndpoints = function (edge) {
22904 var r = this;
22905 var intersect;
22906 var source = edge.source()[0];
22907 var target = edge.target()[0];
22908 var srcPos = source.position();
22909 var tgtPos = target.position();
22910 var tgtArShape = edge.pstyle('target-arrow-shape').value;
22911 var srcArShape = edge.pstyle('source-arrow-shape').value;
22912 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
22913 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
22914 var curveStyle = edge.pstyle('curve-style').value;
22915 var rs = edge._private.rscratch;
22916 var et = rs.edgeType;
22917 var taxi = curveStyle === 'taxi';
22918 var self = et === 'self' || et === 'compound';
22919 var bezier = et === 'bezier' || et === 'multibezier' || self;
22920 var multi = et !== 'bezier';
22921 var lines = et === 'straight' || et === 'segments';
22922 var segments = et === 'segments';
22923 var hasEndpts = bezier || multi || lines;
22924 var overrideEndpts = self || taxi;
22925 var srcManEndpt = edge.pstyle('source-endpoint');
22926 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
22927 var tgtManEndpt = edge.pstyle('target-endpoint');
22928 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
22929 rs.srcManEndpt = srcManEndpt;
22930 rs.tgtManEndpt = tgtManEndpt;
22931 var p1; // last known point of edge on target side
22932
22933 var p2; // last known point of edge on source side
22934
22935 var p1_i; // point to intersect with target shape
22936
22937 var p2_i; // point to intersect with source shape
22938
22939 if (bezier) {
22940 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
22941 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
22942 p1 = cpEnd;
22943 p2 = cpStart;
22944 } else if (lines) {
22945 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
22946 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
22947 p1 = tgtArrowFromPt;
22948 p2 = srcArrowFromPt;
22949 }
22950
22951 if (tgtManEndptVal === 'inside-to-node') {
22952 intersect = [tgtPos.x, tgtPos.y];
22953 } else if (tgtManEndpt.units) {
22954 intersect = this.manualEndptToPx(target, tgtManEndpt);
22955 } else if (tgtManEndptVal === 'outside-to-line') {
22956 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
22957 } else {
22958 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
22959 p1_i = p1;
22960 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
22961 p1_i = [srcPos.x, srcPos.y];
22962 }
22963
22964 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
22965
22966 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
22967 var trs = target._private.rscratch;
22968 var lw = trs.labelWidth;
22969 var lh = trs.labelHeight;
22970 var lx = trs.labelX;
22971 var ly = trs.labelY;
22972 var lw2 = lw / 2;
22973 var lh2 = lh / 2;
22974 var va = target.pstyle('text-valign').value;
22975
22976 if (va === 'top') {
22977 ly -= lh2;
22978 } else if (va === 'bottom') {
22979 ly += lh2;
22980 }
22981
22982 var ha = target.pstyle('text-halign').value;
22983
22984 if (ha === 'left') {
22985 lx -= lw2;
22986 } else if (ha === 'right') {
22987 lx += lw2;
22988 }
22989
22990 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);
22991
22992 if (labelIntersect.length > 0) {
22993 var refPt = srcPos;
22994 var intSqdist = sqdist(refPt, array2point(intersect));
22995 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
22996 var minSqDist = intSqdist;
22997
22998 if (labIntSqdist < intSqdist) {
22999 intersect = labelIntersect;
23000 minSqDist = labIntSqdist;
23001 }
23002
23003 if (labelIntersect.length > 2) {
23004 var labInt2SqDist = sqdist(refPt, {
23005 x: labelIntersect[2],
23006 y: labelIntersect[3]
23007 });
23008
23009 if (labInt2SqDist < minSqDist) {
23010 intersect = [labelIntersect[2], labelIntersect[3]];
23011 }
23012 }
23013 }
23014 }
23015 }
23016
23017 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23018 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23019 rs.endX = edgeEnd[0];
23020 rs.endY = edgeEnd[1];
23021 rs.arrowEndX = arrowEnd[0];
23022 rs.arrowEndY = arrowEnd[1];
23023
23024 if (srcManEndptVal === 'inside-to-node') {
23025 intersect = [srcPos.x, srcPos.y];
23026 } else if (srcManEndpt.units) {
23027 intersect = this.manualEndptToPx(source, srcManEndpt);
23028 } else if (srcManEndptVal === 'outside-to-line') {
23029 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23030 } else {
23031 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23032 p2_i = p2;
23033 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23034 p2_i = [tgtPos.x, tgtPos.y];
23035 }
23036
23037 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23038
23039 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23040 var srs = source._private.rscratch;
23041 var _lw = srs.labelWidth;
23042 var _lh = srs.labelHeight;
23043 var _lx = srs.labelX;
23044 var _ly = srs.labelY;
23045
23046 var _lw2 = _lw / 2;
23047
23048 var _lh2 = _lh / 2;
23049
23050 var _va = source.pstyle('text-valign').value;
23051
23052 if (_va === 'top') {
23053 _ly -= _lh2;
23054 } else if (_va === 'bottom') {
23055 _ly += _lh2;
23056 }
23057
23058 var _ha = source.pstyle('text-halign').value;
23059
23060 if (_ha === 'left') {
23061 _lx -= _lw2;
23062 } else if (_ha === 'right') {
23063 _lx += _lw2;
23064 }
23065
23066 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);
23067
23068 if (_labelIntersect.length > 0) {
23069 var _refPt = tgtPos;
23070
23071 var _intSqdist = sqdist(_refPt, array2point(intersect));
23072
23073 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23074
23075 var _minSqDist = _intSqdist;
23076
23077 if (_labIntSqdist < _intSqdist) {
23078 intersect = [_labelIntersect[0], _labelIntersect[1]];
23079 _minSqDist = _labIntSqdist;
23080 }
23081
23082 if (_labelIntersect.length > 2) {
23083 var _labInt2SqDist = sqdist(_refPt, {
23084 x: _labelIntersect[2],
23085 y: _labelIntersect[3]
23086 });
23087
23088 if (_labInt2SqDist < _minSqDist) {
23089 intersect = [_labelIntersect[2], _labelIntersect[3]];
23090 }
23091 }
23092 }
23093 }
23094 }
23095
23096 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23097 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23098 rs.startX = edgeStart[0];
23099 rs.startY = edgeStart[1];
23100 rs.arrowStartX = arrowStart[0];
23101 rs.arrowStartY = arrowStart[1];
23102
23103 if (hasEndpts) {
23104 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23105 rs.badLine = true;
23106 } else {
23107 rs.badLine = false;
23108 }
23109 }
23110};
23111
23112BRp$4.getSourceEndpoint = function (edge) {
23113 var rs = edge[0]._private.rscratch;
23114 this.recalculateRenderedStyle(edge);
23115
23116 switch (rs.edgeType) {
23117 case 'haystack':
23118 return {
23119 x: rs.haystackPts[0],
23120 y: rs.haystackPts[1]
23121 };
23122
23123 default:
23124 return {
23125 x: rs.arrowStartX,
23126 y: rs.arrowStartY
23127 };
23128 }
23129};
23130
23131BRp$4.getTargetEndpoint = function (edge) {
23132 var rs = edge[0]._private.rscratch;
23133 this.recalculateRenderedStyle(edge);
23134
23135 switch (rs.edgeType) {
23136 case 'haystack':
23137 return {
23138 x: rs.haystackPts[2],
23139 y: rs.haystackPts[3]
23140 };
23141
23142 default:
23143 return {
23144 x: rs.arrowEndX,
23145 y: rs.arrowEndY
23146 };
23147 }
23148};
23149
23150var BRp$5 = {};
23151
23152function pushBezierPts(r, edge, pts) {
23153 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23154 return qbezierAt(p1, p2, p3, t);
23155 };
23156
23157 var _p = edge._private;
23158 var bpts = _p.rstyle.bezierPts;
23159
23160 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23161 var p = r.bezierProjPcts[i];
23162 bpts.push({
23163 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23164 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23165 });
23166 }
23167}
23168
23169BRp$5.storeEdgeProjections = function (edge) {
23170 var _p = edge._private;
23171 var rs = _p.rscratch;
23172 var et = rs.edgeType; // clear the cached points state
23173
23174 _p.rstyle.bezierPts = null;
23175 _p.rstyle.linePts = null;
23176 _p.rstyle.haystackPts = null;
23177
23178 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23179 _p.rstyle.bezierPts = [];
23180
23181 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23182 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23183 }
23184 } else if (et === 'segments') {
23185 var lpts = _p.rstyle.linePts = [];
23186
23187 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23188 lpts.push({
23189 x: rs.allpts[i],
23190 y: rs.allpts[i + 1]
23191 });
23192 }
23193 } else if (et === 'haystack') {
23194 var hpts = rs.haystackPts;
23195 _p.rstyle.haystackPts = [{
23196 x: hpts[0],
23197 y: hpts[1]
23198 }, {
23199 x: hpts[2],
23200 y: hpts[3]
23201 }];
23202 }
23203
23204 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23205};
23206
23207BRp$5.recalculateEdgeProjections = function (edges) {
23208 this.findEdgeControlPoints(edges);
23209};
23210
23211/* global document */
23212
23213var BRp$6 = {};
23214
23215BRp$6.recalculateNodeLabelProjection = function (node) {
23216 var content = node.pstyle('label').strValue;
23217
23218 if (emptyString(content)) {
23219 return;
23220 }
23221
23222 var textX, textY;
23223 var _p = node._private;
23224 var nodeWidth = node.width();
23225 var nodeHeight = node.height();
23226 var padding = node.padding();
23227 var nodePos = node.position();
23228 var textHalign = node.pstyle('text-halign').strValue;
23229 var textValign = node.pstyle('text-valign').strValue;
23230 var rs = _p.rscratch;
23231 var rstyle = _p.rstyle;
23232
23233 switch (textHalign) {
23234 case 'left':
23235 textX = nodePos.x - nodeWidth / 2 - padding;
23236 break;
23237
23238 case 'right':
23239 textX = nodePos.x + nodeWidth / 2 + padding;
23240 break;
23241
23242 default:
23243 // e.g. center
23244 textX = nodePos.x;
23245 }
23246
23247 switch (textValign) {
23248 case 'top':
23249 textY = nodePos.y - nodeHeight / 2 - padding;
23250 break;
23251
23252 case 'bottom':
23253 textY = nodePos.y + nodeHeight / 2 + padding;
23254 break;
23255
23256 default:
23257 // e.g. middle
23258 textY = nodePos.y;
23259 }
23260
23261 rs.labelX = textX;
23262 rs.labelY = textY;
23263 rstyle.labelX = textX;
23264 rstyle.labelY = textY;
23265 this.applyLabelDimensions(node);
23266};
23267
23268var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23269 var angle = Math.atan(dy / dx);
23270
23271 if (dx === 0 && angle < 0) {
23272 angle = angle * -1;
23273 }
23274
23275 return angle;
23276};
23277
23278var lineAngle = function lineAngle(p0, p1) {
23279 var dx = p1.x - p0.x;
23280 var dy = p1.y - p0.y;
23281 return lineAngleFromDelta(dx, dy);
23282};
23283
23284var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23285 var t0 = bound(0, t - 0.001, 1);
23286 var t1 = bound(0, t + 0.001, 1);
23287 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23288 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23289 return lineAngle(lp0, lp1);
23290};
23291
23292BRp$6.recalculateEdgeLabelProjections = function (edge) {
23293 var p;
23294 var _p = edge._private;
23295 var rs = _p.rscratch;
23296 var r = this;
23297 var content = {
23298 mid: edge.pstyle('label').strValue,
23299 source: edge.pstyle('source-label').strValue,
23300 target: edge.pstyle('target-label').strValue
23301 };
23302
23303 if (content.mid || content.source || content.target) ; else {
23304 return; // no labels => no calcs
23305 } // add center point to style so bounding box calculations can use it
23306 //
23307
23308
23309 p = {
23310 x: rs.midX,
23311 y: rs.midY
23312 };
23313
23314 var setRs = function setRs(propName, prefix, value) {
23315 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23316 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23317 };
23318
23319 setRs('labelX', null, p.x);
23320 setRs('labelY', null, p.y);
23321 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23322 setRs('labelAutoAngle', null, midAngle);
23323
23324 var createControlPointInfo = function createControlPointInfo() {
23325 if (createControlPointInfo.cache) {
23326 return createControlPointInfo.cache;
23327 } // use cache so only 1x per edge
23328
23329
23330 var ctrlpts = []; // store each ctrlpt info init
23331
23332 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23333 var p0 = {
23334 x: rs.allpts[i],
23335 y: rs.allpts[i + 1]
23336 };
23337 var p1 = {
23338 x: rs.allpts[i + 2],
23339 y: rs.allpts[i + 3]
23340 }; // ctrlpt
23341
23342 var p2 = {
23343 x: rs.allpts[i + 4],
23344 y: rs.allpts[i + 5]
23345 };
23346 ctrlpts.push({
23347 p0: p0,
23348 p1: p1,
23349 p2: p2,
23350 startDist: 0,
23351 length: 0,
23352 segments: []
23353 });
23354 }
23355
23356 var bpts = _p.rstyle.bezierPts;
23357 var nProjs = r.bezierProjPcts.length;
23358
23359 function addSegment(cp, p0, p1, t0, t1) {
23360 var length = dist(p0, p1);
23361 var prevSegment = cp.segments[cp.segments.length - 1];
23362 var segment = {
23363 p0: p0,
23364 p1: p1,
23365 t0: t0,
23366 t1: t1,
23367 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23368 length: length
23369 };
23370 cp.segments.push(segment);
23371 cp.length += length;
23372 } // update each ctrlpt with segment info
23373
23374
23375 for (var _i = 0; _i < ctrlpts.length; _i++) {
23376 var cp = ctrlpts[_i];
23377 var prevCp = ctrlpts[_i - 1];
23378
23379 if (prevCp) {
23380 cp.startDist = prevCp.startDist + prevCp.length;
23381 }
23382
23383 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23384
23385 for (var j = 0; j < nProjs - 1; j++) {
23386 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23387 }
23388
23389 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23390 }
23391
23392 return createControlPointInfo.cache = ctrlpts;
23393 };
23394
23395 var calculateEndProjection = function calculateEndProjection(prefix) {
23396 var angle;
23397 var isSrc = prefix === 'source';
23398
23399 if (!content[prefix]) {
23400 return;
23401 }
23402
23403 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23404
23405 switch (rs.edgeType) {
23406 case 'self':
23407 case 'compound':
23408 case 'bezier':
23409 case 'multibezier':
23410 {
23411 var cps = createControlPointInfo();
23412 var selected;
23413 var startDist = 0;
23414 var totalDist = 0; // find the segment we're on
23415
23416 for (var i = 0; i < cps.length; i++) {
23417 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23418
23419 for (var j = 0; j < _cp.segments.length; j++) {
23420 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23421 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23422 startDist = totalDist;
23423 totalDist += _seg.length;
23424
23425 if (totalDist >= offset || lastSeg) {
23426 selected = {
23427 cp: _cp,
23428 segment: _seg
23429 };
23430 break;
23431 }
23432 }
23433
23434 if (selected) {
23435 break;
23436 }
23437 }
23438
23439 var cp = selected.cp;
23440 var seg = selected.segment;
23441 var tSegment = (offset - startDist) / seg.length;
23442 var segDt = seg.t1 - seg.t0;
23443 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23444 t = bound(0, t, 1);
23445 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23446 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23447 break;
23448 }
23449
23450 case 'straight':
23451 case 'segments':
23452 case 'haystack':
23453 {
23454 var d = 0,
23455 di,
23456 d0;
23457 var p0, p1;
23458 var l = rs.allpts.length;
23459
23460 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23461 if (isSrc) {
23462 p0 = {
23463 x: rs.allpts[_i2],
23464 y: rs.allpts[_i2 + 1]
23465 };
23466 p1 = {
23467 x: rs.allpts[_i2 + 2],
23468 y: rs.allpts[_i2 + 3]
23469 };
23470 } else {
23471 p0 = {
23472 x: rs.allpts[l - 2 - _i2],
23473 y: rs.allpts[l - 1 - _i2]
23474 };
23475 p1 = {
23476 x: rs.allpts[l - 4 - _i2],
23477 y: rs.allpts[l - 3 - _i2]
23478 };
23479 }
23480
23481 di = dist(p0, p1);
23482 d0 = d;
23483 d += di;
23484
23485 if (d >= offset) {
23486 break;
23487 }
23488 }
23489
23490 var pD = offset - d0;
23491
23492 var _t = pD / di;
23493
23494 _t = bound(0, _t, 1);
23495 p = lineAt(p0, p1, _t);
23496 angle = lineAngle(p0, p1);
23497 break;
23498 }
23499 }
23500
23501 setRs('labelX', prefix, p.x);
23502 setRs('labelY', prefix, p.y);
23503 setRs('labelAutoAngle', prefix, angle);
23504 };
23505
23506 calculateEndProjection('source');
23507 calculateEndProjection('target');
23508 this.applyLabelDimensions(edge);
23509};
23510
23511BRp$6.applyLabelDimensions = function (ele) {
23512 this.applyPrefixedLabelDimensions(ele);
23513
23514 if (ele.isEdge()) {
23515 this.applyPrefixedLabelDimensions(ele, 'source');
23516 this.applyPrefixedLabelDimensions(ele, 'target');
23517 }
23518};
23519
23520BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23521 var _p = ele._private;
23522 var text = this.getLabelText(ele, prefix);
23523 var labelDims = this.calculateLabelDimensions(ele, text);
23524 var lineHeight = ele.pstyle('line-height').pfValue;
23525 var textWrap = ele.pstyle('text-wrap').strValue;
23526 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23527 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23528 var normPerLineHeight = labelDims.height / numLines;
23529 var labelLineHeight = normPerLineHeight * lineHeight;
23530 var width = labelDims.width;
23531 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23532 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23533 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23534 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23535 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23536 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23537};
23538
23539BRp$6.getLabelText = function (ele, prefix) {
23540 var _p = ele._private;
23541 var pfd = prefix ? prefix + '-' : '';
23542 var text = ele.pstyle(pfd + 'label').strValue;
23543 var textTransform = ele.pstyle('text-transform').value;
23544
23545 var rscratch = function rscratch(propName, value) {
23546 if (value) {
23547 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23548 return value;
23549 } else {
23550 return getPrefixedProperty(_p.rscratch, propName, prefix);
23551 }
23552 }; // for empty text, skip all processing
23553
23554
23555 if (!text) {
23556 return '';
23557 }
23558
23559 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23560 text = text.toUpperCase();
23561 } else if (textTransform == 'lowercase') {
23562 text = text.toLowerCase();
23563 }
23564
23565 var wrapStyle = ele.pstyle('text-wrap').value;
23566
23567 if (wrapStyle === 'wrap') {
23568 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23569
23570 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23571 return rscratch('labelWrapCachedText');
23572 }
23573
23574 var zwsp = "\u200B";
23575 var lines = text.split('\n');
23576 var maxW = ele.pstyle('text-max-width').pfValue;
23577 var overflow = ele.pstyle('text-overflow-wrap').value;
23578 var overflowAny = overflow === 'anywhere';
23579 var wrappedLines = [];
23580 var wordsRegex = /[\s\u200b]+/;
23581 var wordSeparator = overflowAny ? '' : ' ';
23582
23583 for (var l = 0; l < lines.length; l++) {
23584 var line = lines[l];
23585 var lineDims = this.calculateLabelDimensions(ele, line);
23586 var lineW = lineDims.width;
23587
23588 if (overflowAny) {
23589 var processedLine = line.split('').join(zwsp);
23590 line = processedLine;
23591 }
23592
23593 if (lineW > maxW) {
23594 // line is too long
23595 var words = line.split(wordsRegex);
23596 var subline = '';
23597
23598 for (var w = 0; w < words.length; w++) {
23599 var word = words[w];
23600 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23601 var testDims = this.calculateLabelDimensions(ele, testLine);
23602 var testW = testDims.width;
23603
23604 if (testW <= maxW) {
23605 // word fits on current line
23606 subline += word + wordSeparator;
23607 } else {
23608 // word starts new line
23609 if (subline) {
23610 wrappedLines.push(subline);
23611 }
23612
23613 subline = word + wordSeparator;
23614 }
23615 } // if there's remaining text, put it in a wrapped line
23616
23617
23618 if (!subline.match(/^[\s\u200b]+$/)) {
23619 wrappedLines.push(subline);
23620 }
23621 } else {
23622 // line is already short enough
23623 wrappedLines.push(line);
23624 }
23625 } // for
23626
23627
23628 rscratch('labelWrapCachedLines', wrappedLines);
23629 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23630 rscratch('labelWrapKey', labelKey);
23631 } else if (wrapStyle === 'ellipsis') {
23632 var _maxW = ele.pstyle('text-max-width').pfValue;
23633 var ellipsized = '';
23634 var ellipsis = "\u2026";
23635 var incLastCh = false;
23636
23637 for (var i = 0; i < text.length; i++) {
23638 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23639
23640 if (widthWithNextCh > _maxW) {
23641 break;
23642 }
23643
23644 ellipsized += text[i];
23645
23646 if (i === text.length - 1) {
23647 incLastCh = true;
23648 }
23649 }
23650
23651 if (!incLastCh) {
23652 ellipsized += ellipsis;
23653 }
23654
23655 return ellipsized;
23656 } // if ellipsize
23657
23658
23659 return text;
23660};
23661
23662BRp$6.getLabelJustification = function (ele) {
23663 var justification = ele.pstyle('text-justification').strValue;
23664 var textHalign = ele.pstyle('text-halign').strValue;
23665
23666 if (justification === 'auto') {
23667 if (ele.isNode()) {
23668 switch (textHalign) {
23669 case 'left':
23670 return 'right';
23671
23672 case 'right':
23673 return 'left';
23674
23675 default:
23676 return 'center';
23677 }
23678 } else {
23679 return 'center';
23680 }
23681 } else {
23682 return justification;
23683 }
23684};
23685
23686BRp$6.calculateLabelDimensions = function (ele, text) {
23687 var r = this;
23688 var cacheKey = hashString(text, ele._private.labelDimsKey);
23689 var cache = r.labelDimCache || (r.labelDimCache = []);
23690 var existingVal = cache[cacheKey];
23691
23692 if (existingVal != null) {
23693 return existingVal;
23694 }
23695
23696 var padding = 6; // add padding around text dims, as the measurement isn't that accurate
23697
23698 var fStyle = ele.pstyle('font-style').strValue;
23699 var size = ele.pstyle('font-size').pfValue;
23700 var family = ele.pstyle('font-family').strValue;
23701 var weight = ele.pstyle('font-weight').strValue;
23702 var canvas = this.labelCalcCanvas;
23703 var c2d = this.labelCalcCanvasContext;
23704
23705 if (!canvas) {
23706 canvas = this.labelCalcCanvas = document.createElement('canvas');
23707 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23708 var ds = canvas.style;
23709 ds.position = 'absolute';
23710 ds.left = '-9999px';
23711 ds.top = '-9999px';
23712 ds.zIndex = '-1';
23713 ds.visibility = 'hidden';
23714 ds.pointerEvents = 'none';
23715 }
23716
23717 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23718 var width = 0;
23719 var height = 0;
23720 var lines = text.split('\n');
23721
23722 for (var i = 0; i < lines.length; i++) {
23723 var line = lines[i];
23724 var metrics = c2d.measureText(line);
23725 var w = Math.ceil(metrics.width);
23726 var h = size;
23727 width = Math.max(w, width);
23728 height += h;
23729 }
23730
23731 width += padding;
23732 height += padding;
23733 return cache[cacheKey] = {
23734 width: width,
23735 height: height
23736 };
23737};
23738
23739BRp$6.calculateLabelAngle = function (ele, prefix) {
23740 var _p = ele._private;
23741 var rs = _p.rscratch;
23742 var isEdge = ele.isEdge();
23743 var prefixDash = prefix ? prefix + '-' : '';
23744 var rot = ele.pstyle(prefixDash + 'text-rotation');
23745 var rotStr = rot.strValue;
23746
23747 if (rotStr === 'none') {
23748 return 0;
23749 } else if (isEdge && rotStr === 'autorotate') {
23750 return rs.labelAutoAngle;
23751 } else if (rotStr === 'autorotate') {
23752 return 0;
23753 } else {
23754 return rot.pfValue;
23755 }
23756};
23757
23758BRp$6.calculateLabelAngles = function (ele) {
23759 var r = this;
23760 var isEdge = ele.isEdge();
23761 var _p = ele._private;
23762 var rs = _p.rscratch;
23763 rs.labelAngle = r.calculateLabelAngle(ele);
23764
23765 if (isEdge) {
23766 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23767 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23768 }
23769};
23770
23771var BRp$7 = {};
23772var TOO_SMALL_CUT_RECT = 28;
23773var warnedCutRect = false;
23774
23775BRp$7.getNodeShape = function (node) {
23776 var r = this;
23777 var shape = node.pstyle('shape').value;
23778
23779 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23780 if (!warnedCutRect) {
23781 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23782 warnedCutRect = true;
23783 }
23784
23785 return 'rectangle';
23786 }
23787
23788 if (node.isParent()) {
23789 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23790 return shape;
23791 } else {
23792 return 'rectangle';
23793 }
23794 }
23795
23796 if (shape === 'polygon') {
23797 var points = node.pstyle('shape-polygon-points').value;
23798 return r.nodeShapes.makePolygon(points).name;
23799 }
23800
23801 return shape;
23802};
23803
23804var BRp$8 = {};
23805
23806BRp$8.registerCalculationListeners = function () {
23807 var cy = this.cy;
23808 var elesToUpdate = cy.collection();
23809 var r = this;
23810
23811 var enqueue = function enqueue(eles) {
23812 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23813 elesToUpdate.merge(eles);
23814
23815 if (dirtyStyleCaches) {
23816 for (var i = 0; i < eles.length; i++) {
23817 var ele = eles[i];
23818 var _p = ele._private;
23819 var rstyle = _p.rstyle;
23820 rstyle.clean = false;
23821 rstyle.cleanConnected = false;
23822 }
23823 }
23824 };
23825
23826 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23827 var ele = e.target;
23828 enqueue(ele);
23829 }).on('style.* background.*', function onDirtyStyle(e) {
23830 var ele = e.target;
23831 enqueue(ele, false);
23832 });
23833
23834 var updateEleCalcs = function updateEleCalcs(willDraw) {
23835 if (willDraw) {
23836 var fns = r.onUpdateEleCalcsFns;
23837
23838 for (var i = 0; i < elesToUpdate.length; i++) {
23839 var ele = elesToUpdate[i];
23840 var rstyle = ele._private.rstyle;
23841
23842 if (ele.isNode() && !rstyle.cleanConnected) {
23843 enqueue(ele.connectedEdges());
23844 rstyle.cleanConnected = true;
23845 }
23846 }
23847
23848 if (fns) {
23849 for (var _i = 0; _i < fns.length; _i++) {
23850 var fn = fns[_i];
23851 fn(willDraw, elesToUpdate);
23852 }
23853 }
23854
23855 r.recalculateRenderedStyle(elesToUpdate);
23856 elesToUpdate = cy.collection();
23857 }
23858 };
23859
23860 r.flushRenderedStyleQueue = function () {
23861 updateEleCalcs(true);
23862 };
23863
23864 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
23865};
23866
23867BRp$8.onUpdateEleCalcs = function (fn) {
23868 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
23869 fns.push(fn);
23870};
23871
23872BRp$8.recalculateRenderedStyle = function (eles, useCache) {
23873 var isCleanConnected = function isCleanConnected(ele) {
23874 return ele._private.rstyle.cleanConnected;
23875 };
23876
23877 var edges = [];
23878 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
23879
23880 if (this.destroyed) {
23881 return;
23882 } // use cache by default for perf
23883
23884
23885 if (useCache === undefined) {
23886 useCache = true;
23887 }
23888
23889 for (var i = 0; i < eles.length; i++) {
23890 var ele = eles[i];
23891 var _p = ele._private;
23892 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
23893 // (and a request for recalc may come in between frames)
23894
23895 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
23896 rstyle.clean = false;
23897 } // only update if dirty and in graph
23898
23899
23900 if (useCache && rstyle.clean || ele.removed()) {
23901 continue;
23902 } // only update if not display: none
23903
23904
23905 if (ele.pstyle('display').value === 'none') {
23906 continue;
23907 }
23908
23909 if (_p.group === 'nodes') {
23910 nodes.push(ele);
23911 } else {
23912 // edges
23913 edges.push(ele);
23914 }
23915
23916 rstyle.clean = true;
23917 } // update node data from projections
23918
23919
23920 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
23921 var _ele = nodes[_i2];
23922 var _p2 = _ele._private;
23923 var _rstyle = _p2.rstyle;
23924
23925 var pos = _ele.position();
23926
23927 this.recalculateNodeLabelProjection(_ele);
23928 _rstyle.nodeX = pos.x;
23929 _rstyle.nodeY = pos.y;
23930 _rstyle.nodeW = _ele.pstyle('width').pfValue;
23931 _rstyle.nodeH = _ele.pstyle('height').pfValue;
23932 }
23933
23934 this.recalculateEdgeProjections(edges); // update edge data from projections
23935
23936 for (var _i3 = 0; _i3 < edges.length; _i3++) {
23937 var _ele2 = edges[_i3];
23938 var _p3 = _ele2._private;
23939 var _rstyle2 = _p3.rstyle;
23940 var rs = _p3.rscratch; // update rstyle positions
23941
23942 _rstyle2.srcX = rs.arrowStartX;
23943 _rstyle2.srcY = rs.arrowStartY;
23944 _rstyle2.tgtX = rs.arrowEndX;
23945 _rstyle2.tgtY = rs.arrowEndY;
23946 _rstyle2.midX = rs.midX;
23947 _rstyle2.midY = rs.midY;
23948 _rstyle2.labelAngle = rs.labelAngle;
23949 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
23950 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
23951 }
23952};
23953
23954var BRp$9 = {};
23955
23956BRp$9.updateCachedGrabbedEles = function () {
23957 var eles = this.cachedZSortedEles;
23958
23959 if (!eles) {
23960 // just let this be recalculated on the next z sort tick
23961 return;
23962 }
23963
23964 eles.drag = [];
23965 eles.nondrag = [];
23966 var grabTargets = [];
23967
23968 for (var i = 0; i < eles.length; i++) {
23969 var ele = eles[i];
23970 var rs = ele._private.rscratch;
23971
23972 if (ele.grabbed() && !ele.isParent()) {
23973 grabTargets.push(ele);
23974 } else if (rs.inDragLayer) {
23975 eles.drag.push(ele);
23976 } else {
23977 eles.nondrag.push(ele);
23978 }
23979 } // put the grab target nodes last so it's on top of its neighbourhood
23980
23981
23982 for (var i = 0; i < grabTargets.length; i++) {
23983 var ele = grabTargets[i];
23984 eles.drag.push(ele);
23985 }
23986};
23987
23988BRp$9.invalidateCachedZSortedEles = function () {
23989 this.cachedZSortedEles = null;
23990};
23991
23992BRp$9.getCachedZSortedEles = function (forceRecalc) {
23993 if (forceRecalc || !this.cachedZSortedEles) {
23994 var eles = this.cy.mutableElements().toArray();
23995 eles.sort(zIndexSort);
23996 eles.interactive = eles.filter(function (ele) {
23997 return ele.interactive();
23998 });
23999 this.cachedZSortedEles = eles;
24000 this.updateCachedGrabbedEles();
24001 } else {
24002 eles = this.cachedZSortedEles;
24003 }
24004
24005 return eles;
24006};
24007
24008var BRp$a = {};
24009[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24010 extend(BRp$a, props);
24011});
24012
24013var BRp$b = {};
24014
24015BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24016 var r = this;
24017 var imageCache = r.imageCache = r.imageCache || {};
24018 var cache = imageCache[url];
24019
24020 if (cache) {
24021 if (!cache.image.complete) {
24022 cache.image.addEventListener('load', onLoad);
24023 }
24024
24025 return cache.image;
24026 } else {
24027 cache = imageCache[url] = imageCache[url] || {};
24028 var image = cache.image = new Image(); // eslint-disable-line no-undef
24029
24030 image.addEventListener('load', onLoad);
24031 image.addEventListener('error', function () {
24032 image.error = true;
24033 }); // #1582 safari doesn't load data uris with crossOrigin properly
24034 // https://bugs.webkit.org/show_bug.cgi?id=123978
24035
24036 var dataUriPrefix = 'data:';
24037 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24038
24039 if (!isDataUri) {
24040 image.crossOrigin = crossOrigin; // prevent tainted canvas
24041 }
24042
24043 image.src = url;
24044 return image;
24045 }
24046};
24047
24048var BRp$c = {};
24049/* global document, window, ResizeObserver, MutationObserver */
24050
24051BRp$c.registerBinding = function (target, event, handler, useCapture) {
24052 // eslint-disable-line no-unused-vars
24053 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24054
24055 var b = this.binder(target);
24056 return b.on.apply(b, args);
24057};
24058
24059BRp$c.binder = function (tgt) {
24060 var r = this;
24061 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24062
24063 if (r.supportsPassiveEvents == null) {
24064 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24065 var supportsPassive = false;
24066
24067 try {
24068 var opts = Object.defineProperty({}, 'passive', {
24069 get: function get() {
24070 supportsPassive = true;
24071 return true;
24072 }
24073 });
24074 window.addEventListener('test', null, opts);
24075 } catch (err) {// not supported
24076 }
24077
24078 r.supportsPassiveEvents = supportsPassive;
24079 }
24080
24081 var on = function on(event, handler, useCapture) {
24082 var args = Array.prototype.slice.call(arguments);
24083
24084 if (tgtIsDom && r.supportsPassiveEvents) {
24085 // replace useCapture w/ opts obj
24086 args[2] = {
24087 capture: useCapture != null ? useCapture : false,
24088 passive: false,
24089 once: false
24090 };
24091 }
24092
24093 r.bindings.push({
24094 target: tgt,
24095 args: args
24096 });
24097 (tgt.addEventListener || tgt.on).apply(tgt, args);
24098 return this;
24099 };
24100
24101 return {
24102 on: on,
24103 addEventListener: on,
24104 addListener: on,
24105 bind: on
24106 };
24107};
24108
24109BRp$c.nodeIsDraggable = function (node) {
24110 return node && node.isNode() && !node.locked() && node.grabbable();
24111};
24112
24113BRp$c.nodeIsGrabbable = function (node) {
24114 return this.nodeIsDraggable(node) && node.interactive();
24115};
24116
24117BRp$c.load = function () {
24118 var r = this;
24119
24120 var isSelected = function isSelected(ele) {
24121 return ele.selected();
24122 };
24123
24124 var triggerEvents = function triggerEvents(target, names, e, position) {
24125 if (target == null) {
24126 target = r.cy;
24127 }
24128
24129 for (var i = 0; i < names.length; i++) {
24130 var name = names[i];
24131 target.emit({
24132 originalEvent: e,
24133 type: name,
24134 position: position
24135 });
24136 }
24137 };
24138
24139 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24140 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24141 };
24142
24143 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24144 var allowPassthrough = true;
24145
24146 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24147 // a grabbable compound node below the ele => no passthrough panning
24148 for (var i = 0; downs && i < downs.length; i++) {
24149 var down = downs[i];
24150
24151 if (down.isNode() && down.isParent()) {
24152 allowPassthrough = false;
24153 break;
24154 }
24155 }
24156 } else {
24157 allowPassthrough = true;
24158 }
24159
24160 return allowPassthrough;
24161 };
24162
24163 var setGrabbed = function setGrabbed(ele) {
24164 ele[0]._private.grabbed = true;
24165 };
24166
24167 var setFreed = function setFreed(ele) {
24168 ele[0]._private.grabbed = false;
24169 };
24170
24171 var setInDragLayer = function setInDragLayer(ele) {
24172 ele[0]._private.rscratch.inDragLayer = true;
24173 };
24174
24175 var setOutDragLayer = function setOutDragLayer(ele) {
24176 ele[0]._private.rscratch.inDragLayer = false;
24177 };
24178
24179 var setGrabTarget = function setGrabTarget(ele) {
24180 ele[0]._private.rscratch.isGrabTarget = true;
24181 };
24182
24183 var removeGrabTarget = function removeGrabTarget(ele) {
24184 ele[0]._private.rscratch.isGrabTarget = false;
24185 };
24186
24187 var addToDragList = function addToDragList(ele, opts) {
24188 var list = opts.addToList;
24189 var listHasEle = list.has(ele);
24190
24191 if (!listHasEle) {
24192 list.merge(ele);
24193 setGrabbed(ele);
24194 }
24195 }; // helper function to determine which child nodes and inner edges
24196 // of a compound node to be dragged as well as the grabbed and selected nodes
24197
24198
24199 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24200 if (!node.cy().hasCompoundNodes()) {
24201 return;
24202 }
24203
24204 if (opts.inDragLayer == null && opts.addToList == null) {
24205 return;
24206 } // nothing to do
24207
24208
24209 var innerNodes = node.descendants();
24210
24211 if (opts.inDragLayer) {
24212 innerNodes.forEach(setInDragLayer);
24213 innerNodes.connectedEdges().forEach(setInDragLayer);
24214 }
24215
24216 if (opts.addToList) {
24217 opts.addToList.unmerge(innerNodes);
24218 }
24219 }; // adds the given nodes and its neighbourhood to the drag layer
24220
24221
24222 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24223 opts = opts || {};
24224 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24225
24226 if (opts.inDragLayer) {
24227 nodes.forEach(setInDragLayer);
24228 nodes.neighborhood().stdFilter(function (ele) {
24229 return !hasCompoundNodes || ele.isEdge();
24230 }).forEach(setInDragLayer);
24231 }
24232
24233 if (opts.addToList) {
24234 nodes.forEach(function (ele) {
24235 addToDragList(ele, opts);
24236 });
24237 }
24238
24239 addDescendantsToDrag(nodes, opts); // always add to drag
24240 // also add nodes and edges related to the topmost ancestor
24241
24242 updateAncestorsInDragLayer(nodes, {
24243 inDragLayer: opts.inDragLayer
24244 });
24245 r.updateCachedGrabbedEles();
24246 };
24247
24248 var addNodeToDrag = addNodesToDrag;
24249
24250 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24251 if (!grabbedEles) {
24252 return;
24253 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24254
24255
24256 r.getCachedZSortedEles().forEach(function (ele) {
24257 setFreed(ele);
24258 setOutDragLayer(ele);
24259 removeGrabTarget(ele);
24260 });
24261 r.updateCachedGrabbedEles();
24262 }; // helper function to determine which ancestor nodes and edges should go
24263 // to the drag layer (or should be removed from drag layer).
24264
24265
24266 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24267 if (opts.inDragLayer == null && opts.addToList == null) {
24268 return;
24269 } // nothing to do
24270
24271
24272 if (!node.cy().hasCompoundNodes()) {
24273 return;
24274 } // find top-level parent
24275
24276
24277 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24278
24279 if (parent.same(node)) {
24280 return;
24281 }
24282
24283 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24284 var edges = nodes.connectedEdges();
24285
24286 if (opts.inDragLayer) {
24287 edges.forEach(setInDragLayer);
24288 nodes.forEach(setInDragLayer);
24289 }
24290
24291 if (opts.addToList) {
24292 nodes.forEach(function (ele) {
24293 addToDragList(ele, opts);
24294 });
24295 }
24296 };
24297
24298 var blurActiveDomElement = function blurActiveDomElement() {
24299 if (document.activeElement != null && document.activeElement.blur != null) {
24300 document.activeElement.blur();
24301 }
24302 };
24303
24304 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24305 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24306
24307 if (haveMutationsApi) {
24308 r.removeObserver = new MutationObserver(function (mutns) {
24309 // eslint-disable-line no-undef
24310 for (var i = 0; i < mutns.length; i++) {
24311 var mutn = mutns[i];
24312 var rNodes = mutn.removedNodes;
24313
24314 if (rNodes) {
24315 for (var j = 0; j < rNodes.length; j++) {
24316 var rNode = rNodes[j];
24317
24318 if (rNode === r.container) {
24319 r.destroy();
24320 break;
24321 }
24322 }
24323 }
24324 }
24325 });
24326
24327 if (r.container.parentNode) {
24328 r.removeObserver.observe(r.container.parentNode, {
24329 childList: true
24330 });
24331 }
24332 } else {
24333 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24334 // eslint-disable-line no-unused-vars
24335 r.destroy();
24336 });
24337 }
24338
24339 var onResize = util(function () {
24340 r.cy.resize();
24341 }, 100);
24342
24343 if (haveMutationsApi) {
24344 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24345
24346 r.styleObserver.observe(r.container, {
24347 attributes: true
24348 });
24349 } // auto resize
24350
24351
24352 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24353
24354 if (haveResizeObserverApi) {
24355 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24356
24357 r.resizeObserver.observe(r.container);
24358 }
24359
24360 var forEachUp = function forEachUp(domEle, fn) {
24361 while (domEle != null) {
24362 fn(domEle);
24363 domEle = domEle.parentNode;
24364 }
24365 };
24366
24367 var invalidateCoords = function invalidateCoords() {
24368 r.invalidateContainerClientCoordsCache();
24369 };
24370
24371 forEachUp(r.container, function (domEle) {
24372 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24373 r.registerBinding(domEle, 'animationend', invalidateCoords);
24374 r.registerBinding(domEle, 'scroll', invalidateCoords);
24375 }); // stop right click menu from appearing on cy
24376
24377 r.registerBinding(r.container, 'contextmenu', function (e) {
24378 e.preventDefault();
24379 });
24380
24381 var inBoxSelection = function inBoxSelection() {
24382 return r.selection[4] !== 0;
24383 };
24384
24385 var eventInContainer = function eventInContainer(e) {
24386 // save cycles if mouse events aren't to be captured
24387 var containerPageCoords = r.findContainerClientCoords();
24388 var x = containerPageCoords[0];
24389 var y = containerPageCoords[1];
24390 var width = containerPageCoords[2];
24391 var height = containerPageCoords[3];
24392 var positions = e.touches ? e.touches : [e];
24393 var atLeastOnePosInside = false;
24394
24395 for (var i = 0; i < positions.length; i++) {
24396 var p = positions[i];
24397
24398 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24399 atLeastOnePosInside = true;
24400 break;
24401 }
24402 }
24403
24404 if (!atLeastOnePosInside) {
24405 return false;
24406 }
24407
24408 var container = r.container;
24409 var target = e.target;
24410 var tParent = target.parentNode;
24411 var containerIsTarget = false;
24412
24413 while (tParent) {
24414 if (tParent === container) {
24415 containerIsTarget = true;
24416 break;
24417 }
24418
24419 tParent = tParent.parentNode;
24420 }
24421
24422 if (!containerIsTarget) {
24423 return false;
24424 } // if target is outisde cy container, then this event is not for us
24425
24426
24427 return true;
24428 }; // Primary key
24429
24430
24431 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24432 if (!eventInContainer(e)) {
24433 return;
24434 }
24435
24436 e.preventDefault();
24437 blurActiveDomElement();
24438 r.hoverData.capture = true;
24439 r.hoverData.which = e.which;
24440 var cy = r.cy;
24441 var gpos = [e.clientX, e.clientY];
24442 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24443 var select = r.selection;
24444 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24445 var near = nears[0];
24446 var draggedElements = r.dragData.possibleDragElements;
24447 r.hoverData.mdownPos = pos;
24448 r.hoverData.mdownGPos = gpos;
24449
24450 var checkForTaphold = function checkForTaphold() {
24451 r.hoverData.tapholdCancelled = false;
24452 clearTimeout(r.hoverData.tapholdTimeout);
24453 r.hoverData.tapholdTimeout = setTimeout(function () {
24454 if (r.hoverData.tapholdCancelled) {
24455 return;
24456 } else {
24457 var ele = r.hoverData.down;
24458
24459 if (ele) {
24460 ele.emit({
24461 originalEvent: e,
24462 type: 'taphold',
24463 position: {
24464 x: pos[0],
24465 y: pos[1]
24466 }
24467 });
24468 } else {
24469 cy.emit({
24470 originalEvent: e,
24471 type: 'taphold',
24472 position: {
24473 x: pos[0],
24474 y: pos[1]
24475 }
24476 });
24477 }
24478 }
24479 }, r.tapholdDuration);
24480 }; // Right click button
24481
24482
24483 if (e.which == 3) {
24484 r.hoverData.cxtStarted = true;
24485 var cxtEvt = {
24486 originalEvent: e,
24487 type: 'cxttapstart',
24488 position: {
24489 x: pos[0],
24490 y: pos[1]
24491 }
24492 };
24493
24494 if (near) {
24495 near.activate();
24496 near.emit(cxtEvt);
24497 r.hoverData.down = near;
24498 } else {
24499 cy.emit(cxtEvt);
24500 }
24501
24502 r.hoverData.downTime = new Date().getTime();
24503 r.hoverData.cxtDragged = false; // Primary button
24504 } else if (e.which == 1) {
24505 if (near) {
24506 near.activate();
24507 } // Element dragging
24508
24509
24510 {
24511 // If something is under the cursor and it is draggable, prepare to grab it
24512 if (near != null) {
24513 if (r.nodeIsGrabbable(near)) {
24514 var makeEvent = function makeEvent(type) {
24515 return {
24516 originalEvent: e,
24517 type: type,
24518 position: {
24519 x: pos[0],
24520 y: pos[1]
24521 }
24522 };
24523 };
24524
24525 var triggerGrab = function triggerGrab(ele) {
24526 ele.emit(makeEvent('grab'));
24527 };
24528
24529 setGrabTarget(near);
24530
24531 if (!near.selected()) {
24532 draggedElements = r.dragData.possibleDragElements = cy.collection();
24533 addNodeToDrag(near, {
24534 addToList: draggedElements
24535 });
24536 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24537 } else {
24538 draggedElements = r.dragData.possibleDragElements = cy.collection();
24539 var selectedNodes = cy.$(function (ele) {
24540 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24541 });
24542 addNodesToDrag(selectedNodes, {
24543 addToList: draggedElements
24544 });
24545 near.emit(makeEvent('grabon'));
24546 selectedNodes.forEach(triggerGrab);
24547 }
24548
24549 r.redrawHint('eles', true);
24550 r.redrawHint('drag', true);
24551 }
24552 }
24553
24554 r.hoverData.down = near;
24555 r.hoverData.downs = nears;
24556 r.hoverData.downTime = new Date().getTime();
24557 }
24558 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24559 x: pos[0],
24560 y: pos[1]
24561 });
24562
24563 if (near == null) {
24564 select[4] = 1;
24565 r.data.bgActivePosistion = {
24566 x: pos[0],
24567 y: pos[1]
24568 };
24569 r.redrawHint('select', true);
24570 r.redraw();
24571 } else if (near.pannable()) {
24572 select[4] = 1; // for future pan
24573 }
24574
24575 checkForTaphold();
24576 } // Initialize selection box coordinates
24577
24578
24579 select[0] = select[2] = pos[0];
24580 select[1] = select[3] = pos[1];
24581 }, false);
24582 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24583 // eslint-disable-line no-undef
24584 var capture = r.hoverData.capture;
24585
24586 if (!capture && !eventInContainer(e)) {
24587 return;
24588 }
24589
24590 var preventDefault = false;
24591 var cy = r.cy;
24592 var zoom = cy.zoom();
24593 var gpos = [e.clientX, e.clientY];
24594 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24595 var mdownPos = r.hoverData.mdownPos;
24596 var mdownGPos = r.hoverData.mdownGPos;
24597 var select = r.selection;
24598 var near = null;
24599
24600 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24601 near = r.findNearestElement(pos[0], pos[1], true, false);
24602 }
24603
24604 var last = r.hoverData.last;
24605 var down = r.hoverData.down;
24606 var disp = [pos[0] - select[2], pos[1] - select[3]];
24607 var draggedElements = r.dragData.possibleDragElements;
24608 var isOverThresholdDrag;
24609
24610 if (mdownGPos) {
24611 var dx = gpos[0] - mdownGPos[0];
24612 var dx2 = dx * dx;
24613 var dy = gpos[1] - mdownGPos[1];
24614 var dy2 = dy * dy;
24615 var dist2 = dx2 + dy2;
24616 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24617 }
24618
24619 var multSelKeyDown = isMultSelKeyDown(e);
24620
24621 if (isOverThresholdDrag) {
24622 r.hoverData.tapholdCancelled = true;
24623 }
24624
24625 var updateDragDelta = function updateDragDelta() {
24626 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24627
24628 if (dragDelta.length === 0) {
24629 dragDelta.push(disp[0]);
24630 dragDelta.push(disp[1]);
24631 } else {
24632 dragDelta[0] += disp[0];
24633 dragDelta[1] += disp[1];
24634 }
24635 };
24636
24637 preventDefault = true;
24638 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24639 x: pos[0],
24640 y: pos[1]
24641 });
24642
24643 var goIntoBoxMode = function goIntoBoxMode() {
24644 r.data.bgActivePosistion = undefined;
24645
24646 if (!r.hoverData.selecting) {
24647 cy.emit({
24648 originalEvent: e,
24649 type: 'boxstart',
24650 position: {
24651 x: pos[0],
24652 y: pos[1]
24653 }
24654 });
24655 }
24656
24657 select[4] = 1;
24658 r.hoverData.selecting = true;
24659 r.redrawHint('select', true);
24660 r.redraw();
24661 }; // trigger context drag if rmouse down
24662
24663
24664 if (r.hoverData.which === 3) {
24665 // but only if over threshold
24666 if (isOverThresholdDrag) {
24667 var cxtEvt = {
24668 originalEvent: e,
24669 type: 'cxtdrag',
24670 position: {
24671 x: pos[0],
24672 y: pos[1]
24673 }
24674 };
24675
24676 if (down) {
24677 down.emit(cxtEvt);
24678 } else {
24679 cy.emit(cxtEvt);
24680 }
24681
24682 r.hoverData.cxtDragged = true;
24683
24684 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24685 if (r.hoverData.cxtOver) {
24686 r.hoverData.cxtOver.emit({
24687 originalEvent: e,
24688 type: 'cxtdragout',
24689 position: {
24690 x: pos[0],
24691 y: pos[1]
24692 }
24693 });
24694 }
24695
24696 r.hoverData.cxtOver = near;
24697
24698 if (near) {
24699 near.emit({
24700 originalEvent: e,
24701 type: 'cxtdragover',
24702 position: {
24703 x: pos[0],
24704 y: pos[1]
24705 }
24706 });
24707 }
24708 }
24709 } // Check if we are drag panning the entire graph
24710
24711 } else if (r.hoverData.dragging) {
24712 preventDefault = true;
24713
24714 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24715 var deltaP;
24716
24717 if (r.hoverData.justStartedPan) {
24718 var mdPos = r.hoverData.mdownPos;
24719 deltaP = {
24720 x: (pos[0] - mdPos[0]) * zoom,
24721 y: (pos[1] - mdPos[1]) * zoom
24722 };
24723 r.hoverData.justStartedPan = false;
24724 } else {
24725 deltaP = {
24726 x: disp[0] * zoom,
24727 y: disp[1] * zoom
24728 };
24729 }
24730
24731 cy.panBy(deltaP);
24732 r.hoverData.dragged = true;
24733 } // Needs reproject due to pan changing viewport
24734
24735
24736 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24737 } else if (select[4] == 1 && (down == null || down.pannable())) {
24738 if (isOverThresholdDrag) {
24739 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24740 goIntoBoxMode();
24741 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24742 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24743
24744 if (allowPassthrough) {
24745 r.hoverData.dragging = true;
24746 r.hoverData.justStartedPan = true;
24747 select[4] = 0;
24748 r.data.bgActivePosistion = array2point(mdownPos);
24749 r.redrawHint('select', true);
24750 r.redraw();
24751 }
24752 }
24753
24754 if (down && down.pannable() && down.active()) {
24755 down.unactivate();
24756 }
24757 }
24758 } else {
24759 if (down && down.pannable() && down.active()) {
24760 down.unactivate();
24761 }
24762
24763 if ((!down || !down.grabbed()) && near != last) {
24764 if (last) {
24765 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24766 x: pos[0],
24767 y: pos[1]
24768 });
24769 }
24770
24771 if (near) {
24772 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24773 x: pos[0],
24774 y: pos[1]
24775 });
24776 }
24777
24778 r.hoverData.last = near;
24779 }
24780
24781 if (down) {
24782 if (isOverThresholdDrag) {
24783 // then we can take action
24784 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24785 // then selection overrides
24786 if (down && down.grabbed()) {
24787 freeDraggedElements(draggedElements);
24788 down.emit('freeon');
24789 draggedElements.emit('free');
24790
24791 if (r.dragData.didDrag) {
24792 down.emit('dragfreeon');
24793 draggedElements.emit('dragfree');
24794 }
24795 }
24796
24797 goIntoBoxMode();
24798 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24799 // drag node
24800 var justStartedDrag = !r.dragData.didDrag;
24801
24802 if (justStartedDrag) {
24803 r.redrawHint('eles', true);
24804 }
24805
24806 r.dragData.didDrag = true; // indicate that we actually did drag the node
24807
24808 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24809
24810 if (!r.hoverData.draggingEles) {
24811 addNodesToDrag(draggedElements, {
24812 inDragLayer: true
24813 });
24814 }
24815
24816 var totalShift = {
24817 x: 0,
24818 y: 0
24819 };
24820
24821 if (number(disp[0]) && number(disp[1])) {
24822 totalShift.x += disp[0];
24823 totalShift.y += disp[1];
24824
24825 if (justStartedDrag) {
24826 var dragDelta = r.hoverData.dragDelta;
24827
24828 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24829 totalShift.x += dragDelta[0];
24830 totalShift.y += dragDelta[1];
24831 }
24832 }
24833 }
24834
24835 for (var i = 0; i < draggedElements.length; i++) {
24836 var dEle = draggedElements[i];
24837
24838 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24839 toTrigger.push(dEle);
24840 }
24841 }
24842
24843 r.hoverData.draggingEles = true;
24844 toTrigger.silentShift(totalShift).emit('position drag');
24845 r.redrawHint('drag', true);
24846 r.redraw();
24847 }
24848 } else {
24849 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24850 updateDragDelta();
24851 }
24852 } // prevent the dragging from triggering text selection on the page
24853
24854
24855 preventDefault = true;
24856 }
24857
24858 select[2] = pos[0];
24859 select[3] = pos[1];
24860
24861 if (preventDefault) {
24862 if (e.stopPropagation) e.stopPropagation();
24863 if (e.preventDefault) e.preventDefault();
24864 return false;
24865 }
24866 }, false);
24867 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24868 // eslint-disable-line no-undef
24869 var capture = r.hoverData.capture;
24870
24871 if (!capture) {
24872 return;
24873 }
24874
24875 r.hoverData.capture = false;
24876 var cy = r.cy;
24877 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24878 var select = r.selection;
24879 var near = r.findNearestElement(pos[0], pos[1], true, false);
24880 var draggedElements = r.dragData.possibleDragElements;
24881 var down = r.hoverData.down;
24882 var multSelKeyDown = isMultSelKeyDown(e);
24883
24884 if (r.data.bgActivePosistion) {
24885 r.redrawHint('select', true);
24886 r.redraw();
24887 }
24888
24889 r.hoverData.tapholdCancelled = true;
24890 r.data.bgActivePosistion = undefined; // not active bg now
24891
24892 if (down) {
24893 down.unactivate();
24894 }
24895
24896 if (r.hoverData.which === 3) {
24897 var cxtEvt = {
24898 originalEvent: e,
24899 type: 'cxttapend',
24900 position: {
24901 x: pos[0],
24902 y: pos[1]
24903 }
24904 };
24905
24906 if (down) {
24907 down.emit(cxtEvt);
24908 } else {
24909 cy.emit(cxtEvt);
24910 }
24911
24912 if (!r.hoverData.cxtDragged) {
24913 var cxtTap = {
24914 originalEvent: e,
24915 type: 'cxttap',
24916 position: {
24917 x: pos[0],
24918 y: pos[1]
24919 }
24920 };
24921
24922 if (down) {
24923 down.emit(cxtTap);
24924 } else {
24925 cy.emit(cxtTap);
24926 }
24927 }
24928
24929 r.hoverData.cxtDragged = false;
24930 r.hoverData.which = null;
24931 } else if (r.hoverData.which === 1) {
24932 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24933 x: pos[0],
24934 y: pos[1]
24935 });
24936
24937 if (!r.dragData.didDrag // didn't move a node around
24938 && !r.hoverData.dragged // didn't pan
24939 && !r.hoverData.selecting // not box selection
24940 && !r.hoverData.isOverThresholdDrag // didn't move too much
24941 ) {
24942 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24943 x: pos[0],
24944 y: pos[1]
24945 });
24946 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24947
24948
24949 if (down == null && // not mousedown on node
24950 !r.dragData.didDrag // didn't move the node around
24951 && !r.hoverData.selecting // not box selection
24952 && !r.hoverData.dragged // didn't pan
24953 && !isMultSelKeyDown(e)) {
24954 cy.$(isSelected).unselect(['tapunselect']);
24955
24956 if (draggedElements.length > 0) {
24957 r.redrawHint('eles', true);
24958 }
24959
24960 r.dragData.possibleDragElements = draggedElements = cy.collection();
24961 } // Single selection
24962
24963
24964 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24965 if (near != null && near._private.selectable) {
24966 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24967 if (near.selected()) {
24968 near.unselect(['tapunselect']);
24969 } else {
24970 near.select(['tapselect']);
24971 }
24972 } else {
24973 if (!multSelKeyDown) {
24974 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
24975 near.select(['tapselect']);
24976 }
24977 }
24978
24979 r.redrawHint('eles', true);
24980 }
24981 }
24982
24983 if (r.hoverData.selecting) {
24984 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
24985 r.redrawHint('select', true);
24986
24987 if (box.length > 0) {
24988 r.redrawHint('eles', true);
24989 }
24990
24991 cy.emit({
24992 type: 'boxend',
24993 originalEvent: e,
24994 position: {
24995 x: pos[0],
24996 y: pos[1]
24997 }
24998 });
24999
25000 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25001 return ele.selectable() && !ele.selected();
25002 };
25003
25004 if (cy.selectionType() === 'additive') {
25005 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25006 } else {
25007 if (!multSelKeyDown) {
25008 cy.$(isSelected).unmerge(box).unselect();
25009 }
25010
25011 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25012 } // always need redraw in case eles unselectable
25013
25014
25015 r.redraw();
25016 } // Cancel drag pan
25017
25018
25019 if (r.hoverData.dragging) {
25020 r.hoverData.dragging = false;
25021 r.redrawHint('select', true);
25022 r.redrawHint('eles', true);
25023 r.redraw();
25024 }
25025
25026 if (!select[4]) {
25027 r.redrawHint('drag', true);
25028 r.redrawHint('eles', true);
25029 var downWasGrabbed = down && down.grabbed();
25030 freeDraggedElements(draggedElements);
25031
25032 if (downWasGrabbed) {
25033 down.emit('freeon');
25034 draggedElements.emit('free');
25035
25036 if (r.dragData.didDrag) {
25037 down.emit('dragfreeon');
25038 draggedElements.emit('dragfree');
25039 }
25040 }
25041 }
25042 } // else not right mouse
25043
25044
25045 select[4] = 0;
25046 r.hoverData.down = null;
25047 r.hoverData.cxtStarted = false;
25048 r.hoverData.draggingEles = false;
25049 r.hoverData.selecting = false;
25050 r.hoverData.isOverThresholdDrag = false;
25051 r.dragData.didDrag = false;
25052 r.hoverData.dragged = false;
25053 r.hoverData.dragDelta = [];
25054 r.hoverData.mdownPos = null;
25055 r.hoverData.mdownGPos = null;
25056 }, false);
25057
25058 var wheelHandler = function wheelHandler(e) {
25059 if (r.scrollingPage) {
25060 return;
25061 } // while scrolling, ignore wheel-to-zoom
25062
25063
25064 var cy = r.cy;
25065 var zoom = cy.zoom();
25066 var pan = cy.pan();
25067 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25068 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25069
25070 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25071 // if pan dragging or cxt dragging, wheel movements make no zoom
25072 e.preventDefault();
25073 return;
25074 }
25075
25076 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25077 e.preventDefault();
25078 r.data.wheelZooming = true;
25079 clearTimeout(r.data.wheelTimeout);
25080 r.data.wheelTimeout = setTimeout(function () {
25081 r.data.wheelZooming = false;
25082 r.redrawHint('eles', true);
25083 r.redraw();
25084 }, 150);
25085 var diff;
25086
25087 if (e.deltaY != null) {
25088 diff = e.deltaY / -250;
25089 } else if (e.wheelDeltaY != null) {
25090 diff = e.wheelDeltaY / 1000;
25091 } else {
25092 diff = e.wheelDelta / 1000;
25093 }
25094
25095 diff = diff * r.wheelSensitivity;
25096 var needsWheelFix = e.deltaMode === 1;
25097
25098 if (needsWheelFix) {
25099 // fixes slow wheel events on ff/linux and ff/windows
25100 diff *= 33;
25101 }
25102
25103 var newZoom = cy.zoom() * Math.pow(10, diff);
25104
25105 if (e.type === 'gesturechange') {
25106 newZoom = r.gestureStartZoom * e.scale;
25107 }
25108
25109 cy.zoom({
25110 level: newZoom,
25111 renderedPosition: {
25112 x: rpos[0],
25113 y: rpos[1]
25114 }
25115 });
25116 }
25117 }; // Functions to help with whether mouse wheel should trigger zooming
25118 // --
25119
25120
25121 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25122 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25123 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25124 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25125
25126 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25127 // eslint-disable-line no-unused-vars
25128 r.scrollingPage = true;
25129 clearTimeout(r.scrollingPageTimeout);
25130 r.scrollingPageTimeout = setTimeout(function () {
25131 r.scrollingPage = false;
25132 }, 250);
25133 }, true); // desktop safari pinch to zoom start
25134
25135 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25136 r.gestureStartZoom = r.cy.zoom();
25137
25138 if (!r.hasTouchStarted) {
25139 // don't affect touch devices like iphone
25140 e.preventDefault();
25141 }
25142 }, true);
25143 r.registerBinding(r.container, 'gesturechange', function (e) {
25144 if (!r.hasTouchStarted) {
25145 // don't affect touch devices like iphone
25146 wheelHandler(e);
25147 }
25148 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25149 // Handle mouseout on Cytoscape container
25150
25151 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25152 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25153 r.cy.emit({
25154 originalEvent: e,
25155 type: 'mouseout',
25156 position: {
25157 x: pos[0],
25158 y: pos[1]
25159 }
25160 });
25161 }, false);
25162 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25163 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25164 r.cy.emit({
25165 originalEvent: e,
25166 type: 'mouseover',
25167 position: {
25168 x: pos[0],
25169 y: pos[1]
25170 }
25171 });
25172 }, false);
25173 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25174
25175 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25176
25177 var center1, modelCenter1; // center point on start pinch to zoom
25178
25179 var offsetLeft, offsetTop;
25180 var containerWidth, containerHeight;
25181 var twoFingersStartInside;
25182
25183 var distance = function distance(x1, y1, x2, y2) {
25184 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25185 };
25186
25187 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25188 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25189 };
25190
25191 var touchstartHandler;
25192 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25193 r.hasTouchStarted = true;
25194
25195 if (!eventInContainer(e)) {
25196 return;
25197 }
25198
25199 blurActiveDomElement();
25200 r.touchData.capture = true;
25201 r.data.bgActivePosistion = undefined;
25202 var cy = r.cy;
25203 var now = r.touchData.now;
25204 var earlier = r.touchData.earlier;
25205
25206 if (e.touches[0]) {
25207 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25208 now[0] = pos[0];
25209 now[1] = pos[1];
25210 }
25211
25212 if (e.touches[1]) {
25213 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25214 now[2] = pos[0];
25215 now[3] = pos[1];
25216 }
25217
25218 if (e.touches[2]) {
25219 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25220 now[4] = pos[0];
25221 now[5] = pos[1];
25222 } // record starting points for pinch-to-zoom
25223
25224
25225 if (e.touches[1]) {
25226 r.touchData.singleTouchMoved = true;
25227 freeDraggedElements(r.dragData.touchDragEles);
25228 var offsets = r.findContainerClientCoords();
25229 offsetLeft = offsets[0];
25230 offsetTop = offsets[1];
25231 containerWidth = offsets[2];
25232 containerHeight = offsets[3];
25233 f1x1 = e.touches[0].clientX - offsetLeft;
25234 f1y1 = e.touches[0].clientY - offsetTop;
25235 f2x1 = e.touches[1].clientX - offsetLeft;
25236 f2y1 = e.touches[1].clientY - offsetTop;
25237 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25238 var pan = cy.pan();
25239 var zoom = cy.zoom();
25240 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25241 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25242 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25243 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25244
25245 var cxtDistThreshold = 200;
25246 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25247
25248 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25249 var near1 = r.findNearestElement(now[0], now[1], true, true);
25250 var near2 = r.findNearestElement(now[2], now[3], true, true);
25251
25252 if (near1 && near1.isNode()) {
25253 near1.activate().emit({
25254 originalEvent: e,
25255 type: 'cxttapstart',
25256 position: {
25257 x: now[0],
25258 y: now[1]
25259 }
25260 });
25261 r.touchData.start = near1;
25262 } else if (near2 && near2.isNode()) {
25263 near2.activate().emit({
25264 originalEvent: e,
25265 type: 'cxttapstart',
25266 position: {
25267 x: now[0],
25268 y: now[1]
25269 }
25270 });
25271 r.touchData.start = near2;
25272 } else {
25273 cy.emit({
25274 originalEvent: e,
25275 type: 'cxttapstart',
25276 position: {
25277 x: now[0],
25278 y: now[1]
25279 }
25280 });
25281 }
25282
25283 if (r.touchData.start) {
25284 r.touchData.start._private.grabbed = false;
25285 }
25286
25287 r.touchData.cxt = true;
25288 r.touchData.cxtDragged = false;
25289 r.data.bgActivePosistion = undefined;
25290 r.redraw();
25291 return;
25292 }
25293 }
25294
25295 if (e.touches[2]) {
25296 // ignore
25297 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25298 if (cy.boxSelectionEnabled()) {
25299 e.preventDefault();
25300 }
25301 } else if (e.touches[1]) ; else if (e.touches[0]) {
25302 var nears = r.findNearestElements(now[0], now[1], true, true);
25303 var near = nears[0];
25304
25305 if (near != null) {
25306 near.activate();
25307 r.touchData.start = near;
25308 r.touchData.starts = nears;
25309
25310 if (r.nodeIsGrabbable(near)) {
25311 var draggedEles = r.dragData.touchDragEles = cy.collection();
25312 var selectedNodes = null;
25313 r.redrawHint('eles', true);
25314 r.redrawHint('drag', true);
25315
25316 if (near.selected()) {
25317 // reset drag elements, since near will be added again
25318 selectedNodes = cy.$(function (ele) {
25319 return ele.selected() && r.nodeIsGrabbable(ele);
25320 });
25321 addNodesToDrag(selectedNodes, {
25322 addToList: draggedEles
25323 });
25324 } else {
25325 addNodeToDrag(near, {
25326 addToList: draggedEles
25327 });
25328 }
25329
25330 setGrabTarget(near);
25331
25332 var makeEvent = function makeEvent(type) {
25333 return {
25334 originalEvent: e,
25335 type: type,
25336 position: {
25337 x: now[0],
25338 y: now[1]
25339 }
25340 };
25341 };
25342
25343 near.emit(makeEvent('grabon'));
25344
25345 if (selectedNodes) {
25346 selectedNodes.forEach(function (n) {
25347 n.emit(makeEvent('grab'));
25348 });
25349 } else {
25350 near.emit(makeEvent('grab'));
25351 }
25352 }
25353 }
25354
25355 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25356 x: now[0],
25357 y: now[1]
25358 });
25359
25360 if (near == null) {
25361 r.data.bgActivePosistion = {
25362 x: pos[0],
25363 y: pos[1]
25364 };
25365 r.redrawHint('select', true);
25366 r.redraw();
25367 } // Tap, taphold
25368 // -----
25369
25370
25371 r.touchData.singleTouchMoved = false;
25372 r.touchData.singleTouchStartTime = +new Date();
25373 clearTimeout(r.touchData.tapholdTimeout);
25374 r.touchData.tapholdTimeout = setTimeout(function () {
25375 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25376 && !r.touchData.selecting // box selection shouldn't allow taphold through
25377 ) {
25378 triggerEvents(r.touchData.start, ['taphold'], e, {
25379 x: now[0],
25380 y: now[1]
25381 });
25382 }
25383 }, r.tapholdDuration);
25384 }
25385
25386 if (e.touches.length >= 1) {
25387 var sPos = r.touchData.startPosition = [];
25388
25389 for (var i = 0; i < now.length; i++) {
25390 sPos[i] = earlier[i] = now[i];
25391 }
25392
25393 var touch0 = e.touches[0];
25394 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25395 }
25396 }, false);
25397 var touchmoveHandler;
25398 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25399 // eslint-disable-line no-undef
25400 var capture = r.touchData.capture;
25401
25402 if (!capture && !eventInContainer(e)) {
25403 return;
25404 }
25405
25406 var select = r.selection;
25407 var cy = r.cy;
25408 var now = r.touchData.now;
25409 var earlier = r.touchData.earlier;
25410 var zoom = cy.zoom();
25411
25412 if (e.touches[0]) {
25413 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25414 now[0] = pos[0];
25415 now[1] = pos[1];
25416 }
25417
25418 if (e.touches[1]) {
25419 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25420 now[2] = pos[0];
25421 now[3] = pos[1];
25422 }
25423
25424 if (e.touches[2]) {
25425 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25426 now[4] = pos[0];
25427 now[5] = pos[1];
25428 }
25429
25430 var startGPos = r.touchData.startGPosition;
25431 var isOverThresholdDrag;
25432
25433 if (capture && e.touches[0] && startGPos) {
25434 var disp = [];
25435
25436 for (var j = 0; j < now.length; j++) {
25437 disp[j] = now[j] - earlier[j];
25438 }
25439
25440 var dx = e.touches[0].clientX - startGPos[0];
25441 var dx2 = dx * dx;
25442 var dy = e.touches[0].clientY - startGPos[1];
25443 var dy2 = dy * dy;
25444 var dist2 = dx2 + dy2;
25445 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25446 } // context swipe cancelling
25447
25448
25449 if (capture && r.touchData.cxt) {
25450 e.preventDefault();
25451 var f1x2 = e.touches[0].clientX - offsetLeft,
25452 f1y2 = e.touches[0].clientY - offsetTop;
25453 var f2x2 = e.touches[1].clientX - offsetLeft,
25454 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25455
25456 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25457 var factorSq = distance2Sq / distance1Sq;
25458 var distThreshold = 150;
25459 var distThresholdSq = distThreshold * distThreshold;
25460 var factorThreshold = 1.5;
25461 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25462
25463 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25464 r.touchData.cxt = false;
25465 r.data.bgActivePosistion = undefined;
25466 r.redrawHint('select', true);
25467 var cxtEvt = {
25468 originalEvent: e,
25469 type: 'cxttapend',
25470 position: {
25471 x: now[0],
25472 y: now[1]
25473 }
25474 };
25475
25476 if (r.touchData.start) {
25477 r.touchData.start.unactivate().emit(cxtEvt);
25478 r.touchData.start = null;
25479 } else {
25480 cy.emit(cxtEvt);
25481 }
25482 }
25483 } // context swipe
25484
25485
25486 if (capture && r.touchData.cxt) {
25487 var cxtEvt = {
25488 originalEvent: e,
25489 type: 'cxtdrag',
25490 position: {
25491 x: now[0],
25492 y: now[1]
25493 }
25494 };
25495 r.data.bgActivePosistion = undefined;
25496 r.redrawHint('select', true);
25497
25498 if (r.touchData.start) {
25499 r.touchData.start.emit(cxtEvt);
25500 } else {
25501 cy.emit(cxtEvt);
25502 }
25503
25504 if (r.touchData.start) {
25505 r.touchData.start._private.grabbed = false;
25506 }
25507
25508 r.touchData.cxtDragged = true;
25509 var near = r.findNearestElement(now[0], now[1], true, true);
25510
25511 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25512 if (r.touchData.cxtOver) {
25513 r.touchData.cxtOver.emit({
25514 originalEvent: e,
25515 type: 'cxtdragout',
25516 position: {
25517 x: now[0],
25518 y: now[1]
25519 }
25520 });
25521 }
25522
25523 r.touchData.cxtOver = near;
25524
25525 if (near) {
25526 near.emit({
25527 originalEvent: e,
25528 type: 'cxtdragover',
25529 position: {
25530 x: now[0],
25531 y: now[1]
25532 }
25533 });
25534 }
25535 } // box selection
25536
25537 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25538 e.preventDefault();
25539 r.data.bgActivePosistion = undefined;
25540 this.lastThreeTouch = +new Date();
25541
25542 if (!r.touchData.selecting) {
25543 cy.emit({
25544 originalEvent: e,
25545 type: 'boxstart',
25546 position: {
25547 x: now[0],
25548 y: now[1]
25549 }
25550 });
25551 }
25552
25553 r.touchData.selecting = true;
25554 r.touchData.didSelect = true;
25555 select[4] = 1;
25556
25557 if (!select || select.length === 0 || select[0] === undefined) {
25558 select[0] = (now[0] + now[2] + now[4]) / 3;
25559 select[1] = (now[1] + now[3] + now[5]) / 3;
25560 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25561 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25562 } else {
25563 select[2] = (now[0] + now[2] + now[4]) / 3;
25564 select[3] = (now[1] + now[3] + now[5]) / 3;
25565 }
25566
25567 r.redrawHint('select', true);
25568 r.redraw(); // pinch to zoom
25569 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25570 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25571 // two fingers => pinch to zoom
25572 e.preventDefault();
25573 r.data.bgActivePosistion = undefined;
25574 r.redrawHint('select', true);
25575 var draggedEles = r.dragData.touchDragEles;
25576
25577 if (draggedEles) {
25578 r.redrawHint('drag', true);
25579
25580 for (var i = 0; i < draggedEles.length; i++) {
25581 var de_p = draggedEles[i]._private;
25582 de_p.grabbed = false;
25583 de_p.rscratch.inDragLayer = false;
25584 }
25585 }
25586
25587 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25588
25589 var f1x2 = e.touches[0].clientX - offsetLeft,
25590 f1y2 = e.touches[0].clientY - offsetTop;
25591 var f2x2 = e.touches[1].clientX - offsetLeft,
25592 f2y2 = e.touches[1].clientY - offsetTop;
25593 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25594 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25595
25596 var factor = distance2 / distance1;
25597
25598 if (twoFingersStartInside) {
25599 // delta finger1
25600 var df1x = f1x2 - f1x1;
25601 var df1y = f1y2 - f1y1; // delta finger 2
25602
25603 var df2x = f2x2 - f2x1;
25604 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25605 // i.e. so pinching cancels out and moving together pans
25606
25607 var tx = (df1x + df2x) / 2;
25608 var ty = (df1y + df2y) / 2; // now calculate the zoom
25609
25610 var zoom1 = cy.zoom();
25611 var zoom2 = zoom1 * factor;
25612 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25613
25614 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25615 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25616 var pan2 = {
25617 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25618 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25619 }; // remove dragged eles
25620
25621 if (_start && _start.active()) {
25622 var draggedEles = r.dragData.touchDragEles;
25623 freeDraggedElements(draggedEles);
25624 r.redrawHint('drag', true);
25625 r.redrawHint('eles', true);
25626
25627 _start.unactivate().emit('freeon');
25628
25629 draggedEles.emit('free');
25630
25631 if (r.dragData.didDrag) {
25632 _start.emit('dragfreeon');
25633
25634 draggedEles.emit('dragfree');
25635 }
25636 }
25637
25638 cy.viewport({
25639 zoom: zoom2,
25640 pan: pan2,
25641 cancelOnFailedZoom: true
25642 });
25643 distance1 = distance2;
25644 f1x1 = f1x2;
25645 f1y1 = f1y2;
25646 f2x1 = f2x2;
25647 f2y1 = f2y2;
25648 r.pinching = true;
25649 } // Re-project
25650
25651
25652 if (e.touches[0]) {
25653 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25654 now[0] = pos[0];
25655 now[1] = pos[1];
25656 }
25657
25658 if (e.touches[1]) {
25659 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25660 now[2] = pos[0];
25661 now[3] = pos[1];
25662 }
25663
25664 if (e.touches[2]) {
25665 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25666 now[4] = pos[0];
25667 now[5] = pos[1];
25668 }
25669 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25670 ) {
25671 var start = r.touchData.start;
25672 var last = r.touchData.last;
25673 var near;
25674
25675 if (!r.hoverData.draggingEles && !r.swipePanning) {
25676 near = r.findNearestElement(now[0], now[1], true, true);
25677 }
25678
25679 if (capture && start != null) {
25680 e.preventDefault();
25681 } // dragging nodes
25682
25683
25684 if (capture && start != null && r.nodeIsDraggable(start)) {
25685 if (isOverThresholdDrag) {
25686 // then dragging can happen
25687 var draggedEles = r.dragData.touchDragEles;
25688 var justStartedDrag = !r.dragData.didDrag;
25689
25690 if (justStartedDrag) {
25691 addNodesToDrag(draggedEles, {
25692 inDragLayer: true
25693 });
25694 }
25695
25696 r.dragData.didDrag = true;
25697 var totalShift = {
25698 x: 0,
25699 y: 0
25700 };
25701
25702 if (number(disp[0]) && number(disp[1])) {
25703 totalShift.x += disp[0];
25704 totalShift.y += disp[1];
25705
25706 if (justStartedDrag) {
25707 r.redrawHint('eles', true);
25708 var dragDelta = r.touchData.dragDelta;
25709
25710 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25711 totalShift.x += dragDelta[0];
25712 totalShift.y += dragDelta[1];
25713 }
25714 }
25715 }
25716
25717 r.hoverData.draggingEles = true;
25718 draggedEles.silentShift(totalShift).emit('position drag');
25719 r.redrawHint('drag', true);
25720
25721 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25722 r.redrawHint('eles', true);
25723 }
25724
25725 r.redraw();
25726 } else {
25727 // otherise keep track of drag delta for later
25728 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25729
25730 if (dragDelta.length === 0) {
25731 dragDelta.push(disp[0]);
25732 dragDelta.push(disp[1]);
25733 } else {
25734 dragDelta[0] += disp[0];
25735 dragDelta[1] += disp[1];
25736 }
25737 }
25738 } // touchmove
25739
25740
25741 {
25742 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25743 x: now[0],
25744 y: now[1]
25745 });
25746
25747 if ((!start || !start.grabbed()) && near != last) {
25748 if (last) {
25749 last.emit({
25750 originalEvent: e,
25751 type: 'tapdragout',
25752 position: {
25753 x: now[0],
25754 y: now[1]
25755 }
25756 });
25757 }
25758
25759 if (near) {
25760 near.emit({
25761 originalEvent: e,
25762 type: 'tapdragover',
25763 position: {
25764 x: now[0],
25765 y: now[1]
25766 }
25767 });
25768 }
25769 }
25770
25771 r.touchData.last = near;
25772 } // check to cancel taphold
25773
25774 if (capture) {
25775 for (var i = 0; i < now.length; i++) {
25776 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25777 r.touchData.singleTouchMoved = true;
25778 }
25779 }
25780 } // panning
25781
25782
25783 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25784 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25785
25786 if (allowPassthrough) {
25787 e.preventDefault();
25788
25789 if (!r.data.bgActivePosistion) {
25790 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25791 }
25792
25793 if (r.swipePanning) {
25794 cy.panBy({
25795 x: disp[0] * zoom,
25796 y: disp[1] * zoom
25797 });
25798 } else if (isOverThresholdDrag) {
25799 r.swipePanning = true;
25800 cy.panBy({
25801 x: dx * zoom,
25802 y: dy * zoom
25803 });
25804
25805 if (start) {
25806 start.unactivate();
25807 r.redrawHint('select', true);
25808 r.touchData.start = null;
25809 }
25810 }
25811 } // Re-project
25812
25813
25814 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25815 now[0] = pos[0];
25816 now[1] = pos[1];
25817 }
25818 }
25819
25820 for (var j = 0; j < now.length; j++) {
25821 earlier[j] = now[j];
25822 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25823
25824
25825 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25826 r.data.bgActivePosistion = undefined;
25827 r.redrawHint('select', true);
25828 r.redraw();
25829 }
25830 }, false);
25831 var touchcancelHandler;
25832 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25833 // eslint-disable-line no-unused-vars
25834 var start = r.touchData.start;
25835 r.touchData.capture = false;
25836
25837 if (start) {
25838 start.unactivate();
25839 }
25840 });
25841 var touchendHandler;
25842 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25843 // eslint-disable-line no-unused-vars
25844 var start = r.touchData.start;
25845 var capture = r.touchData.capture;
25846
25847 if (capture) {
25848 if (e.touches.length === 0) {
25849 r.touchData.capture = false;
25850 }
25851
25852 e.preventDefault();
25853 } else {
25854 return;
25855 }
25856
25857 var select = r.selection;
25858 r.swipePanning = false;
25859 r.hoverData.draggingEles = false;
25860 var cy = r.cy;
25861 var zoom = cy.zoom();
25862 var now = r.touchData.now;
25863 var earlier = r.touchData.earlier;
25864
25865 if (e.touches[0]) {
25866 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25867 now[0] = pos[0];
25868 now[1] = pos[1];
25869 }
25870
25871 if (e.touches[1]) {
25872 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25873 now[2] = pos[0];
25874 now[3] = pos[1];
25875 }
25876
25877 if (e.touches[2]) {
25878 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25879 now[4] = pos[0];
25880 now[5] = pos[1];
25881 }
25882
25883 if (start) {
25884 start.unactivate();
25885 }
25886
25887 var ctxTapend;
25888
25889 if (r.touchData.cxt) {
25890 ctxTapend = {
25891 originalEvent: e,
25892 type: 'cxttapend',
25893 position: {
25894 x: now[0],
25895 y: now[1]
25896 }
25897 };
25898
25899 if (start) {
25900 start.emit(ctxTapend);
25901 } else {
25902 cy.emit(ctxTapend);
25903 }
25904
25905 if (!r.touchData.cxtDragged) {
25906 var ctxTap = {
25907 originalEvent: e,
25908 type: 'cxttap',
25909 position: {
25910 x: now[0],
25911 y: now[1]
25912 }
25913 };
25914
25915 if (start) {
25916 start.emit(ctxTap);
25917 } else {
25918 cy.emit(ctxTap);
25919 }
25920 }
25921
25922 if (r.touchData.start) {
25923 r.touchData.start._private.grabbed = false;
25924 }
25925
25926 r.touchData.cxt = false;
25927 r.touchData.start = null;
25928 r.redraw();
25929 return;
25930 } // no more box selection if we don't have three fingers
25931
25932
25933 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25934 r.touchData.selecting = false;
25935 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25936 select[0] = undefined;
25937 select[1] = undefined;
25938 select[2] = undefined;
25939 select[3] = undefined;
25940 select[4] = 0;
25941 r.redrawHint('select', true);
25942 cy.emit({
25943 type: 'boxend',
25944 originalEvent: e,
25945 position: {
25946 x: now[0],
25947 y: now[1]
25948 }
25949 });
25950
25951 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25952 return ele.selectable() && !ele.selected();
25953 };
25954
25955 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25956
25957 if (box.nonempty()) {
25958 r.redrawHint('eles', true);
25959 }
25960
25961 r.redraw();
25962 }
25963
25964 if (start != null) {
25965 start.unactivate();
25966 }
25967
25968 if (e.touches[2]) {
25969 r.data.bgActivePosistion = undefined;
25970 r.redrawHint('select', true);
25971 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
25972 r.data.bgActivePosistion = undefined;
25973 r.redrawHint('select', true);
25974 var draggedEles = r.dragData.touchDragEles;
25975
25976 if (start != null) {
25977 var startWasGrabbed = start._private.grabbed;
25978 freeDraggedElements(draggedEles);
25979 r.redrawHint('drag', true);
25980 r.redrawHint('eles', true);
25981
25982 if (startWasGrabbed) {
25983 start.emit('freeon');
25984 draggedEles.emit('free');
25985
25986 if (r.dragData.didDrag) {
25987 start.emit('dragfreeon');
25988 draggedEles.emit('dragfree');
25989 }
25990 }
25991
25992 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25993 x: now[0],
25994 y: now[1]
25995 });
25996 start.unactivate();
25997 r.touchData.start = null;
25998 } else {
25999 var near = r.findNearestElement(now[0], now[1], true, true);
26000 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26001 x: now[0],
26002 y: now[1]
26003 });
26004 }
26005
26006 var dx = r.touchData.startPosition[0] - now[0];
26007 var dx2 = dx * dx;
26008 var dy = r.touchData.startPosition[1] - now[1];
26009 var dy2 = dy * dy;
26010 var dist2 = dx2 + dy2;
26011 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26012
26013 if (!r.touchData.singleTouchMoved) {
26014 if (!start) {
26015 cy.$(':selected').unselect(['tapunselect']);
26016 }
26017
26018 triggerEvents(start, ['tap', 'vclick'], e, {
26019 x: now[0],
26020 y: now[1]
26021 });
26022 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26023
26024
26025 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26026 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26027 ) {
26028 if (cy.selectionType() === 'single') {
26029 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26030 start.select(['tapselect']);
26031 } else {
26032 if (start.selected()) {
26033 start.unselect(['tapunselect']);
26034 } else {
26035 start.select(['tapselect']);
26036 }
26037 }
26038
26039 r.redrawHint('eles', true);
26040 }
26041
26042 r.touchData.singleTouchMoved = true;
26043 }
26044
26045 for (var j = 0; j < now.length; j++) {
26046 earlier[j] = now[j];
26047 }
26048
26049 r.dragData.didDrag = false; // reset for next touchstart
26050
26051 if (e.touches.length === 0) {
26052 r.touchData.dragDelta = [];
26053 r.touchData.startPosition = null;
26054 r.touchData.startGPosition = null;
26055 r.touchData.didSelect = false;
26056 }
26057
26058 if (e.touches.length < 2) {
26059 if (e.touches.length === 1) {
26060 // the old start global pos'n may not be the same finger that remains
26061 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26062 }
26063
26064 r.pinching = false;
26065 r.redrawHint('eles', true);
26066 r.redraw();
26067 } //r.redraw();
26068
26069 }, false); // fallback compatibility layer for ms pointer events
26070
26071 if (typeof TouchEvent === 'undefined') {
26072 var pointers = [];
26073
26074 var makeTouch = function makeTouch(e) {
26075 return {
26076 clientX: e.clientX,
26077 clientY: e.clientY,
26078 force: 1,
26079 identifier: e.pointerId,
26080 pageX: e.pageX,
26081 pageY: e.pageY,
26082 radiusX: e.width / 2,
26083 radiusY: e.height / 2,
26084 screenX: e.screenX,
26085 screenY: e.screenY,
26086 target: e.target
26087 };
26088 };
26089
26090 var makePointer = function makePointer(e) {
26091 return {
26092 event: e,
26093 touch: makeTouch(e)
26094 };
26095 };
26096
26097 var addPointer = function addPointer(e) {
26098 pointers.push(makePointer(e));
26099 };
26100
26101 var removePointer = function removePointer(e) {
26102 for (var i = 0; i < pointers.length; i++) {
26103 var p = pointers[i];
26104
26105 if (p.event.pointerId === e.pointerId) {
26106 pointers.splice(i, 1);
26107 return;
26108 }
26109 }
26110 };
26111
26112 var updatePointer = function updatePointer(e) {
26113 var p = pointers.filter(function (p) {
26114 return p.event.pointerId === e.pointerId;
26115 })[0];
26116 p.event = e;
26117 p.touch = makeTouch(e);
26118 };
26119
26120 var addTouchesToEvent = function addTouchesToEvent(e) {
26121 e.touches = pointers.map(function (p) {
26122 return p.touch;
26123 });
26124 };
26125
26126 var pointerIsMouse = function pointerIsMouse(e) {
26127 return e.pointerType === 'mouse' || e.pointerType === 4;
26128 };
26129
26130 r.registerBinding(r.container, 'pointerdown', function (e) {
26131 if (pointerIsMouse(e)) {
26132 return;
26133 } // mouse already handled
26134
26135
26136 e.preventDefault();
26137 addPointer(e);
26138 addTouchesToEvent(e);
26139 touchstartHandler(e);
26140 });
26141 r.registerBinding(r.container, 'pointerup', function (e) {
26142 if (pointerIsMouse(e)) {
26143 return;
26144 } // mouse already handled
26145
26146
26147 removePointer(e);
26148 addTouchesToEvent(e);
26149 touchendHandler(e);
26150 });
26151 r.registerBinding(r.container, 'pointercancel', function (e) {
26152 if (pointerIsMouse(e)) {
26153 return;
26154 } // mouse already handled
26155
26156
26157 removePointer(e);
26158 addTouchesToEvent(e);
26159 touchcancelHandler(e);
26160 });
26161 r.registerBinding(r.container, 'pointermove', function (e) {
26162 if (pointerIsMouse(e)) {
26163 return;
26164 } // mouse already handled
26165
26166
26167 e.preventDefault();
26168 updatePointer(e);
26169 addTouchesToEvent(e);
26170 touchmoveHandler(e);
26171 });
26172 }
26173};
26174
26175var BRp$d = {};
26176
26177BRp$d.generatePolygon = function (name, points) {
26178 return this.nodeShapes[name] = {
26179 renderer: this,
26180 name: name,
26181 points: points,
26182 draw: function draw(context, centerX, centerY, width, height) {
26183 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26184 },
26185 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26186 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26187 },
26188 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26189 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26190 }
26191 };
26192};
26193
26194BRp$d.generateEllipse = function () {
26195 return this.nodeShapes['ellipse'] = {
26196 renderer: this,
26197 name: 'ellipse',
26198 draw: function draw(context, centerX, centerY, width, height) {
26199 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26200 },
26201 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26202 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26203 },
26204 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26205 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26206 }
26207 };
26208};
26209
26210BRp$d.generateRoundPolygon = function (name, points) {
26211 // Pre-compute control points
26212 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26213 // the unit vectors.
26214 // For simplicity the layout will be:
26215 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26216 var allPoints = new Array(points.length * 2);
26217
26218 for (var i = 0; i < points.length / 2; i++) {
26219 var sourceIndex = i * 2;
26220 var destIndex = void 0;
26221
26222 if (i < points.length / 2 - 1) {
26223 destIndex = (i + 1) * 2;
26224 } else {
26225 destIndex = 0;
26226 }
26227
26228 allPoints[i * 4] = points[sourceIndex];
26229 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26230 var xDest = points[destIndex] - points[sourceIndex];
26231 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26232 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26233 allPoints[i * 4 + 2] = xDest / norm;
26234 allPoints[i * 4 + 3] = yDest / norm;
26235 }
26236
26237 return this.nodeShapes[name] = {
26238 renderer: this,
26239 name: name,
26240 points: allPoints,
26241 draw: function draw(context, centerX, centerY, width, height) {
26242 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26243 },
26244 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26245 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26246 },
26247 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26248 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26249 }
26250 };
26251};
26252
26253BRp$d.generateRoundRectangle = function () {
26254 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26255 renderer: this,
26256 name: 'round-rectangle',
26257 points: generateUnitNgonPointsFitToSquare(4, 0),
26258 draw: function draw(context, centerX, centerY, width, height) {
26259 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26260 },
26261 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26262 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26263 },
26264 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26265 var cornerRadius = getRoundRectangleRadius(width, height);
26266 var diam = cornerRadius * 2; // Check hBox
26267
26268 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26269 return true;
26270 } // Check vBox
26271
26272
26273 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26274 return true;
26275 } // Check top left quarter circle
26276
26277
26278 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26279 return true;
26280 } // Check top right quarter circle
26281
26282
26283 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26284 return true;
26285 } // Check bottom right quarter circle
26286
26287
26288 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26289 return true;
26290 } // Check bottom left quarter circle
26291
26292
26293 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26294 return true;
26295 }
26296
26297 return false;
26298 }
26299 };
26300};
26301
26302BRp$d.generateCutRectangle = function () {
26303 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26304 renderer: this,
26305 name: 'cut-rectangle',
26306 cornerLength: getCutRectangleCornerLength(),
26307 points: generateUnitNgonPointsFitToSquare(4, 0),
26308 draw: function draw(context, centerX, centerY, width, height) {
26309 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26310 },
26311 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26312 var cl = this.cornerLength;
26313 var hh = height / 2;
26314 var hw = width / 2;
26315 var xBegin = centerX - hw;
26316 var xEnd = centerX + hw;
26317 var yBegin = centerY - hh;
26318 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26319
26320 return {
26321 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26322 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26323 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26324 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26325 };
26326 },
26327 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26328 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26329 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26330 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26331 },
26332 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26333 // Check hBox
26334 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26335 return true;
26336 } // Check vBox
26337
26338
26339 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26340 return true;
26341 }
26342
26343 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26344 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26345 }
26346 };
26347};
26348
26349BRp$d.generateBarrel = function () {
26350 return this.nodeShapes['barrel'] = {
26351 renderer: this,
26352 name: 'barrel',
26353 points: generateUnitNgonPointsFitToSquare(4, 0),
26354 draw: function draw(context, centerX, centerY, width, height) {
26355 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26356 },
26357 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26358 // use two fixed t values for the bezier curve approximation
26359 var t0 = 0.15;
26360 var t1 = 0.5;
26361 var t2 = 0.85;
26362 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26363
26364 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26365 // approximate curve pts based on the two t values
26366 var m0 = qbezierPtAt({
26367 x: pts[0],
26368 y: pts[1]
26369 }, {
26370 x: pts[2],
26371 y: pts[3]
26372 }, {
26373 x: pts[4],
26374 y: pts[5]
26375 }, t0);
26376 var m1 = qbezierPtAt({
26377 x: pts[0],
26378 y: pts[1]
26379 }, {
26380 x: pts[2],
26381 y: pts[3]
26382 }, {
26383 x: pts[4],
26384 y: pts[5]
26385 }, t1);
26386 var m2 = qbezierPtAt({
26387 x: pts[0],
26388 y: pts[1]
26389 }, {
26390 x: pts[2],
26391 y: pts[3]
26392 }, {
26393 x: pts[4],
26394 y: pts[5]
26395 }, t2);
26396 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26397 };
26398
26399 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26400 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26401 },
26402 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26403 var hh = height / 2;
26404 var hw = width / 2;
26405 var xBegin = centerX - hw;
26406 var xEnd = centerX + hw;
26407 var yBegin = centerY - hh;
26408 var yEnd = centerY + hh;
26409 var curveConstants = getBarrelCurveConstants(width, height);
26410 var hOffset = curveConstants.heightOffset;
26411 var wOffset = curveConstants.widthOffset;
26412 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26413
26414 var pts = {
26415 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26416 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26417 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26418 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26419 };
26420 pts.topLeft.isTop = true;
26421 pts.topRight.isTop = true;
26422 pts.bottomLeft.isBottom = true;
26423 pts.bottomRight.isBottom = true;
26424 return pts;
26425 },
26426 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26427 var curveConstants = getBarrelCurveConstants(width, height);
26428 var hOffset = curveConstants.heightOffset;
26429 var wOffset = curveConstants.widthOffset; // Check hBox
26430
26431 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26432 return true;
26433 } // Check vBox
26434
26435
26436 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26437 return true;
26438 }
26439
26440 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26441
26442 var getCurveT = function getCurveT(x, y, curvePts) {
26443 var x0 = curvePts[4];
26444 var x1 = curvePts[2];
26445 var x2 = curvePts[0];
26446 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26447
26448 var y2 = curvePts[1];
26449 var xMin = Math.min(x0, x2);
26450 var xMax = Math.max(x0, x2);
26451 var yMin = Math.min(y0, y2);
26452 var yMax = Math.max(y0, y2);
26453
26454 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26455 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26456 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26457 var validRoots = roots.filter(function (r) {
26458 return 0 <= r && r <= 1;
26459 });
26460
26461 if (validRoots.length > 0) {
26462 return validRoots[0];
26463 }
26464 }
26465
26466 return null;
26467 };
26468
26469 var curveRegions = Object.keys(barrelCurvePts);
26470
26471 for (var i = 0; i < curveRegions.length; i++) {
26472 var corner = curveRegions[i];
26473 var cornerPts = barrelCurvePts[corner];
26474 var t = getCurveT(x, y, cornerPts);
26475
26476 if (t == null) {
26477 continue;
26478 }
26479
26480 var y0 = cornerPts[5];
26481 var y1 = cornerPts[3];
26482 var y2 = cornerPts[1];
26483 var bezY = qbezierAt(y0, y1, y2, t);
26484
26485 if (cornerPts.isTop && bezY <= y) {
26486 return true;
26487 }
26488
26489 if (cornerPts.isBottom && y <= bezY) {
26490 return true;
26491 }
26492 }
26493
26494 return false;
26495 }
26496 };
26497};
26498
26499BRp$d.generateBottomRoundrectangle = function () {
26500 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26501 renderer: this,
26502 name: 'bottom-round-rectangle',
26503 points: generateUnitNgonPointsFitToSquare(4, 0),
26504 draw: function draw(context, centerX, centerY, width, height) {
26505 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26506 },
26507 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26508 var topStartX = nodeX - (width / 2 + padding);
26509 var topStartY = nodeY - (height / 2 + padding);
26510 var topEndY = topStartY;
26511 var topEndX = nodeX + (width / 2 + padding);
26512 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26513
26514 if (topIntersections.length > 0) {
26515 return topIntersections;
26516 }
26517
26518 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26519 },
26520 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26521 var cornerRadius = getRoundRectangleRadius(width, height);
26522 var diam = 2 * cornerRadius; // Check hBox
26523
26524 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26525 return true;
26526 } // Check vBox
26527
26528
26529 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26530 return true;
26531 } // check non-rounded top side
26532
26533
26534 var outerWidth = width / 2 + 2 * padding;
26535 var outerHeight = height / 2 + 2 * padding;
26536 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26537
26538 if (pointInsidePolygonPoints(x, y, points)) {
26539 return true;
26540 } // Check bottom right quarter circle
26541
26542
26543 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26544 return true;
26545 } // Check bottom left quarter circle
26546
26547
26548 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26549 return true;
26550 }
26551
26552 return false;
26553 }
26554 };
26555};
26556
26557BRp$d.registerNodeShapes = function () {
26558 var nodeShapes = this.nodeShapes = {};
26559 var renderer = this;
26560 this.generateEllipse();
26561 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26562 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26563 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26564 nodeShapes['square'] = nodeShapes['rectangle'];
26565 this.generateRoundRectangle();
26566 this.generateCutRectangle();
26567 this.generateBarrel();
26568 this.generateBottomRoundrectangle();
26569 {
26570 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26571 this.generatePolygon('diamond', diamondPoints);
26572 this.generateRoundPolygon('round-diamond', diamondPoints);
26573 }
26574 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26575 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26576 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26577 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26578 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26579 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26580 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26581 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26582 var star5Points = new Array(20);
26583 {
26584 var outerPoints = generateUnitNgonPoints(5, 0);
26585 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26586
26587 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26588 innerRadius *= 1.57;
26589
26590 for (var i = 0; i < innerPoints.length / 2; i++) {
26591 innerPoints[i * 2] *= innerRadius;
26592 innerPoints[i * 2 + 1] *= innerRadius;
26593 }
26594
26595 for (var i = 0; i < 20 / 4; i++) {
26596 star5Points[i * 4] = outerPoints[i * 2];
26597 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26598 star5Points[i * 4 + 2] = innerPoints[i * 2];
26599 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26600 }
26601 }
26602 star5Points = fitPolygonToSquare(star5Points);
26603 this.generatePolygon('star', star5Points);
26604 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26605 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26606 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]);
26607 {
26608 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26609 this.generatePolygon('tag', tagPoints);
26610 this.generateRoundPolygon('round-tag', tagPoints);
26611 }
26612
26613 nodeShapes.makePolygon = function (points) {
26614 // use caching on user-specified polygons so they are as fast as native shapes
26615 var key = points.join('$');
26616 var name = 'polygon-' + key;
26617 var shape;
26618
26619 if (shape = this[name]) {
26620 // got cached shape
26621 return shape;
26622 } // create and cache new shape
26623
26624
26625 return renderer.generatePolygon(name, points);
26626 };
26627};
26628
26629var BRp$e = {};
26630
26631BRp$e.timeToRender = function () {
26632 return this.redrawTotalTime / this.redrawCount;
26633};
26634
26635BRp$e.redraw = function (options) {
26636 options = options || staticEmptyObject();
26637 var r = this;
26638
26639 if (r.averageRedrawTime === undefined) {
26640 r.averageRedrawTime = 0;
26641 }
26642
26643 if (r.lastRedrawTime === undefined) {
26644 r.lastRedrawTime = 0;
26645 }
26646
26647 if (r.lastDrawTime === undefined) {
26648 r.lastDrawTime = 0;
26649 }
26650
26651 r.requestedFrame = true;
26652 r.renderOptions = options;
26653};
26654
26655BRp$e.beforeRender = function (fn, priority) {
26656 // the renderer can't add tick callbacks when destroyed
26657 if (this.destroyed) {
26658 return;
26659 }
26660
26661 if (priority == null) {
26662 error('Priority is not optional for beforeRender');
26663 }
26664
26665 var cbs = this.beforeRenderCallbacks;
26666 cbs.push({
26667 fn: fn,
26668 priority: priority
26669 }); // higher priority callbacks executed first
26670
26671 cbs.sort(function (a, b) {
26672 return b.priority - a.priority;
26673 });
26674};
26675
26676var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26677 var cbs = r.beforeRenderCallbacks;
26678
26679 for (var i = 0; i < cbs.length; i++) {
26680 cbs[i].fn(willDraw, startTime);
26681 }
26682};
26683
26684BRp$e.startRenderLoop = function () {
26685 var r = this;
26686 var cy = r.cy;
26687
26688 if (r.renderLoopStarted) {
26689 return;
26690 } else {
26691 r.renderLoopStarted = true;
26692 }
26693
26694 var renderFn = function renderFn(requestTime) {
26695 if (r.destroyed) {
26696 return;
26697 }
26698
26699 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26700 beforeRenderCallbacks(r, true, requestTime);
26701 var startTime = performanceNow();
26702 r.render(r.renderOptions);
26703 var endTime = r.lastDrawTime = performanceNow();
26704
26705 if (r.averageRedrawTime === undefined) {
26706 r.averageRedrawTime = endTime - startTime;
26707 }
26708
26709 if (r.redrawCount === undefined) {
26710 r.redrawCount = 0;
26711 }
26712
26713 r.redrawCount++;
26714
26715 if (r.redrawTotalTime === undefined) {
26716 r.redrawTotalTime = 0;
26717 }
26718
26719 var duration = endTime - startTime;
26720 r.redrawTotalTime += duration;
26721 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26722
26723 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26724 r.requestedFrame = false;
26725 } else {
26726 beforeRenderCallbacks(r, false, requestTime);
26727 }
26728
26729 r.skipFrame = false;
26730 requestAnimationFrame(renderFn);
26731 };
26732
26733 requestAnimationFrame(renderFn);
26734};
26735
26736var BaseRenderer = function BaseRenderer(options) {
26737 this.init(options);
26738};
26739
26740var BR = BaseRenderer;
26741var BRp$f = BR.prototype;
26742BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26743
26744BRp$f.init = function (options) {
26745 var r = this;
26746 r.options = options;
26747 r.cy = options.cy;
26748 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26749
26750 if (window$1) {
26751 var document = window$1.document;
26752 var head = document.head;
26753 var stylesheetId = '__________cytoscape_stylesheet';
26754 var className = '__________cytoscape_container';
26755 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26756
26757 if (ctr.className.indexOf(className) < 0) {
26758 ctr.className = (ctr.className || '') + ' ' + className;
26759 }
26760
26761 if (!stylesheetAlreadyExists) {
26762 var stylesheet = document.createElement('style');
26763 stylesheet.id = stylesheetId;
26764 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26765 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26766 }
26767
26768 var computedStyle = window$1.getComputedStyle(ctr);
26769 var position = computedStyle.getPropertyValue('position');
26770
26771 if (position === 'static') {
26772 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26773 }
26774 }
26775
26776 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26777
26778 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26779
26780 r.hoverData = {
26781 down: null,
26782 last: null,
26783 downTime: null,
26784 triggerMode: null,
26785 dragging: false,
26786 initialPan: [null, null],
26787 capture: false
26788 };
26789 r.dragData = {
26790 possibleDragElements: []
26791 };
26792 r.touchData = {
26793 start: null,
26794 capture: false,
26795 // These 3 fields related to tap, taphold events
26796 startPosition: [null, null, null, null, null, null],
26797 singleTouchStartTime: null,
26798 singleTouchMoved: true,
26799 now: [null, null, null, null, null, null],
26800 earlier: [null, null, null, null, null, null]
26801 };
26802 r.redraws = 0;
26803 r.showFps = options.showFps;
26804 r.debug = options.debug;
26805 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26806 r.textureOnViewport = options.textureOnViewport;
26807 r.wheelSensitivity = options.wheelSensitivity;
26808 r.motionBlurEnabled = options.motionBlur; // on by default
26809
26810 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26811 r.motionBlur = options.motionBlur; // for initial kick off
26812
26813 r.motionBlurOpacity = options.motionBlurOpacity;
26814 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26815 r.motionBlurPxRatio = 1;
26816 r.mbPxRBlurry = 1; //0.8;
26817
26818 r.minMbLowQualFrames = 4;
26819 r.fullQualityMb = false;
26820 r.clearedForMotionBlur = [];
26821 r.desktopTapThreshold = options.desktopTapThreshold;
26822 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26823 r.touchTapThreshold = options.touchTapThreshold;
26824 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26825 r.tapholdDuration = 500;
26826 r.bindings = [];
26827 r.beforeRenderCallbacks = [];
26828 r.beforeRenderPriorities = {
26829 // higher priority execs before lower one
26830 animations: 400,
26831 eleCalcs: 300,
26832 eleTxrDeq: 200,
26833 lyrTxrDeq: 150,
26834 lyrTxrSkip: 100
26835 };
26836 r.registerNodeShapes();
26837 r.registerArrowShapes();
26838 r.registerCalculationListeners();
26839};
26840
26841BRp$f.notify = function (eventName, eles) {
26842 var r = this;
26843 var cy = r.cy; // the renderer can't be notified after it's destroyed
26844
26845 if (this.destroyed) {
26846 return;
26847 }
26848
26849 if (eventName === 'init') {
26850 r.load();
26851 return;
26852 }
26853
26854 if (eventName === 'destroy') {
26855 r.destroy();
26856 return;
26857 }
26858
26859 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26860 r.invalidateCachedZSortedEles();
26861 }
26862
26863 if (eventName === 'viewport') {
26864 r.redrawHint('select', true);
26865 }
26866
26867 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26868 r.invalidateContainerClientCoordsCache();
26869 r.matchCanvasSize(r.container);
26870 }
26871
26872 r.redrawHint('eles', true);
26873 r.redrawHint('drag', true);
26874 this.startRenderLoop();
26875 this.redraw();
26876};
26877
26878BRp$f.destroy = function () {
26879 var r = this;
26880 r.destroyed = true;
26881 r.cy.stopAnimationLoop();
26882
26883 for (var i = 0; i < r.bindings.length; i++) {
26884 var binding = r.bindings[i];
26885 var b = binding;
26886 var tgt = b.target;
26887 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26888 }
26889
26890 r.bindings = [];
26891 r.beforeRenderCallbacks = [];
26892 r.onUpdateEleCalcsFns = [];
26893
26894 if (r.removeObserver) {
26895 r.removeObserver.disconnect();
26896 }
26897
26898 if (r.styleObserver) {
26899 r.styleObserver.disconnect();
26900 }
26901
26902 if (r.resizeObserver) {
26903 r.resizeObserver.disconnect();
26904 }
26905
26906 if (r.labelCalcDiv) {
26907 try {
26908 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26909 } catch (e) {// ie10 issue #1014
26910 }
26911 }
26912};
26913
26914BRp$f.isHeadless = function () {
26915 return false;
26916};
26917
26918[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26919 extend(BRp$f, props);
26920});
26921
26922var fullFpsTime = 1000 / 60; // assume 60 frames per second
26923
26924var defs = {
26925 setupDequeueing: function setupDequeueing(opts) {
26926 return function setupDequeueingImpl() {
26927 var self = this;
26928 var r = this.renderer;
26929
26930 if (self.dequeueingSetup) {
26931 return;
26932 } else {
26933 self.dequeueingSetup = true;
26934 }
26935
26936 var queueRedraw = util(function () {
26937 r.redrawHint('eles', true);
26938 r.redrawHint('drag', true);
26939 r.redraw();
26940 }, opts.deqRedrawThreshold);
26941
26942 var dequeue = function dequeue(willDraw, frameStartTime) {
26943 var startTime = performanceNow();
26944 var avgRenderTime = r.averageRedrawTime;
26945 var renderTime = r.lastRedrawTime;
26946 var deqd = [];
26947 var extent = r.cy.extent();
26948 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26949 // queue won't automatically be flushed before dequeueing starts
26950
26951 if (!willDraw) {
26952 r.flushRenderedStyleQueue();
26953 }
26954
26955 while (true) {
26956 // eslint-disable-line no-constant-condition
26957 var now = performanceNow();
26958 var duration = now - startTime;
26959 var frameDuration = now - frameStartTime;
26960
26961 if (renderTime < fullFpsTime) {
26962 // if we're rendering faster than the ideal fps, then do dequeueing
26963 // during all of the remaining frame time
26964 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26965
26966 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26967 break;
26968 }
26969 } else {
26970 if (willDraw) {
26971 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26972 break;
26973 }
26974 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
26975 break;
26976 }
26977 }
26978
26979 var thisDeqd = opts.deq(self, pixelRatio, extent);
26980
26981 if (thisDeqd.length > 0) {
26982 for (var i = 0; i < thisDeqd.length; i++) {
26983 deqd.push(thisDeqd[i]);
26984 }
26985 } else {
26986 break;
26987 }
26988 } // callbacks on dequeue
26989
26990
26991 if (deqd.length > 0) {
26992 opts.onDeqd(self, deqd);
26993
26994 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
26995 queueRedraw();
26996 }
26997 }
26998 };
26999
27000 var priority = opts.priority || noop;
27001 r.beforeRender(dequeue, priority(self));
27002 };
27003 }
27004};
27005
27006// Uses keys so elements may share the same cache.
27007
27008var ElementTextureCacheLookup =
27009/*#__PURE__*/
27010function () {
27011 function ElementTextureCacheLookup(getKey) {
27012 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27013
27014 _classCallCheck(this, ElementTextureCacheLookup);
27015
27016 this.idsByKey = new Map$1();
27017 this.keyForId = new Map$1();
27018 this.cachesByLvl = new Map$1();
27019 this.lvls = [];
27020 this.getKey = getKey;
27021 this.doesEleInvalidateKey = doesEleInvalidateKey;
27022 }
27023
27024 _createClass(ElementTextureCacheLookup, [{
27025 key: "getIdsFor",
27026 value: function getIdsFor(key) {
27027 if (key == null) {
27028 error("Can not get id list for null key");
27029 }
27030
27031 var idsByKey = this.idsByKey;
27032 var ids = this.idsByKey.get(key);
27033
27034 if (!ids) {
27035 ids = new Set$1();
27036 idsByKey.set(key, ids);
27037 }
27038
27039 return ids;
27040 }
27041 }, {
27042 key: "addIdForKey",
27043 value: function addIdForKey(key, id) {
27044 if (key != null) {
27045 this.getIdsFor(key).add(id);
27046 }
27047 }
27048 }, {
27049 key: "deleteIdForKey",
27050 value: function deleteIdForKey(key, id) {
27051 if (key != null) {
27052 this.getIdsFor(key)["delete"](id);
27053 }
27054 }
27055 }, {
27056 key: "getNumberOfIdsForKey",
27057 value: function getNumberOfIdsForKey(key) {
27058 if (key == null) {
27059 return 0;
27060 } else {
27061 return this.getIdsFor(key).size;
27062 }
27063 }
27064 }, {
27065 key: "updateKeyMappingFor",
27066 value: function updateKeyMappingFor(ele) {
27067 var id = ele.id();
27068 var prevKey = this.keyForId.get(id);
27069 var currKey = this.getKey(ele);
27070 this.deleteIdForKey(prevKey, id);
27071 this.addIdForKey(currKey, id);
27072 this.keyForId.set(id, currKey);
27073 }
27074 }, {
27075 key: "deleteKeyMappingFor",
27076 value: function deleteKeyMappingFor(ele) {
27077 var id = ele.id();
27078 var prevKey = this.keyForId.get(id);
27079 this.deleteIdForKey(prevKey, id);
27080 this.keyForId["delete"](id);
27081 }
27082 }, {
27083 key: "keyHasChangedFor",
27084 value: function keyHasChangedFor(ele) {
27085 var id = ele.id();
27086 var prevKey = this.keyForId.get(id);
27087 var newKey = this.getKey(ele);
27088 return prevKey !== newKey;
27089 }
27090 }, {
27091 key: "isInvalid",
27092 value: function isInvalid(ele) {
27093 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27094 }
27095 }, {
27096 key: "getCachesAt",
27097 value: function getCachesAt(lvl) {
27098 var cachesByLvl = this.cachesByLvl,
27099 lvls = this.lvls;
27100 var caches = cachesByLvl.get(lvl);
27101
27102 if (!caches) {
27103 caches = new Map$1();
27104 cachesByLvl.set(lvl, caches);
27105 lvls.push(lvl);
27106 }
27107
27108 return caches;
27109 }
27110 }, {
27111 key: "getCache",
27112 value: function getCache(key, lvl) {
27113 return this.getCachesAt(lvl).get(key);
27114 }
27115 }, {
27116 key: "get",
27117 value: function get(ele, lvl) {
27118 var key = this.getKey(ele);
27119 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27120
27121 if (cache != null) {
27122 this.updateKeyMappingFor(ele);
27123 }
27124
27125 return cache;
27126 }
27127 }, {
27128 key: "getForCachedKey",
27129 value: function getForCachedKey(ele, lvl) {
27130 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27131
27132 var cache = this.getCache(key, lvl);
27133 return cache;
27134 }
27135 }, {
27136 key: "hasCache",
27137 value: function hasCache(key, lvl) {
27138 return this.getCachesAt(lvl).has(key);
27139 }
27140 }, {
27141 key: "has",
27142 value: function has(ele, lvl) {
27143 var key = this.getKey(ele);
27144 return this.hasCache(key, lvl);
27145 }
27146 }, {
27147 key: "setCache",
27148 value: function setCache(key, lvl, cache) {
27149 cache.key = key;
27150 this.getCachesAt(lvl).set(key, cache);
27151 }
27152 }, {
27153 key: "set",
27154 value: function set(ele, lvl, cache) {
27155 var key = this.getKey(ele);
27156 this.setCache(key, lvl, cache);
27157 this.updateKeyMappingFor(ele);
27158 }
27159 }, {
27160 key: "deleteCache",
27161 value: function deleteCache(key, lvl) {
27162 this.getCachesAt(lvl)["delete"](key);
27163 }
27164 }, {
27165 key: "delete",
27166 value: function _delete(ele, lvl) {
27167 var key = this.getKey(ele);
27168 this.deleteCache(key, lvl);
27169 }
27170 }, {
27171 key: "invalidateKey",
27172 value: function invalidateKey(key) {
27173 var _this = this;
27174
27175 this.lvls.forEach(function (lvl) {
27176 return _this.deleteCache(key, lvl);
27177 });
27178 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27179
27180 }, {
27181 key: "invalidate",
27182 value: function invalidate(ele) {
27183 var id = ele.id();
27184 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27185
27186 this.deleteKeyMappingFor(ele);
27187 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27188
27189 if (entireKeyInvalidated) {
27190 // clear mapping for current key
27191 this.invalidateKey(key);
27192 }
27193
27194 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27195 }
27196 }]);
27197
27198 return ElementTextureCacheLookup;
27199}();
27200
27201var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27202
27203var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27204
27205var minLvl = -4; // when scaling smaller than that we don't need to re-render
27206
27207var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27208
27209var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27210
27211var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27212
27213var defTxrWidth = 1024; // default/minimum texture width
27214
27215var maxTxrW = 1024; // the maximum width of a texture
27216
27217var maxTxrH = 1024; // the maximum height of a texture
27218
27219var minUtility = 0.2; // if usage of texture is less than this, it is retired
27220
27221var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27222
27223var maxFullnessChecks = 10; // dequeued after this many checks
27224
27225var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27226
27227var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27228
27229var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27230
27231var deqFastCost = 0.9; // % of frame time to be used when >60fps
27232
27233var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27234
27235var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27236
27237var getTxrReasons = {
27238 dequeue: 'dequeue',
27239 downscale: 'downscale',
27240 highQuality: 'highQuality'
27241};
27242var initDefaults = defaults({
27243 getKey: null,
27244 doesEleInvalidateKey: falsify,
27245 drawElement: null,
27246 getBoundingBox: null,
27247 getRotationPoint: null,
27248 getRotationOffset: null,
27249 isVisible: trueify,
27250 allowEdgeTxrCaching: true,
27251 allowParentTxrCaching: true
27252});
27253
27254var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27255 var self = this;
27256 self.renderer = renderer;
27257 self.onDequeues = [];
27258 var opts = initDefaults(initOptions);
27259 extend(self, opts);
27260 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27261 self.setupDequeueing();
27262};
27263
27264var ETCp = ElementTextureCache.prototype;
27265ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27266
27267ETCp.getTextureQueue = function (txrH) {
27268 var self = this;
27269 self.eleImgCaches = self.eleImgCaches || {};
27270 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27271}; // the list of usused textures which can be recycled (in use in texture queue)
27272
27273
27274ETCp.getRetiredTextureQueue = function (txrH) {
27275 var self = this;
27276 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27277 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27278 return rtxtrQ;
27279}; // queue of element draw requests at different scale levels
27280
27281
27282ETCp.getElementQueue = function () {
27283 var self = this;
27284 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27285 return b.reqs - a.reqs;
27286 });
27287 return q;
27288}; // queue of element draw requests at different scale levels (element id lookup)
27289
27290
27291ETCp.getElementKeyToQueue = function () {
27292 var self = this;
27293 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27294 return k2q;
27295};
27296
27297ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27298 var self = this;
27299 var r = this.renderer;
27300 var zoom = r.cy.zoom();
27301 var lookup = this.lookup;
27302
27303 if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
27304 return null;
27305 }
27306
27307 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27308 return null;
27309 }
27310
27311 if (lvl == null) {
27312 lvl = Math.ceil(log2(zoom * pxRatio));
27313 }
27314
27315 if (lvl < minLvl) {
27316 lvl = minLvl;
27317 } else if (zoom >= maxZoom || lvl > maxLvl) {
27318 return null;
27319 }
27320
27321 var scale = Math.pow(2, lvl);
27322 var eleScaledH = bb.h * scale;
27323 var eleScaledW = bb.w * scale;
27324 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27325
27326 if (!this.isVisible(ele, scaledLabelShown)) {
27327 return null;
27328 }
27329
27330 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27331
27332 if (eleCache && eleCache.invalidated) {
27333 eleCache.invalidated = false;
27334 eleCache.texture.invalidatedWidth -= eleCache.width;
27335 }
27336
27337 if (eleCache) {
27338 return eleCache;
27339 }
27340
27341 var txrH; // which texture height this ele belongs to
27342
27343 if (eleScaledH <= minTxrH) {
27344 txrH = minTxrH;
27345 } else if (eleScaledH <= txrStepH) {
27346 txrH = txrStepH;
27347 } else {
27348 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27349 }
27350
27351 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27352 return null; // caching large elements is not efficient
27353 }
27354
27355 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27356
27357 var txr = txrQ[txrQ.length - 2];
27358
27359 var addNewTxr = function addNewTxr() {
27360 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27361 }; // try the last one if there is no second last one
27362
27363
27364 if (!txr) {
27365 txr = txrQ[txrQ.length - 1];
27366 } // if the last one doesn't exist, we need a first one
27367
27368
27369 if (!txr) {
27370 txr = addNewTxr();
27371 } // if there's no room in the current texture, we need a new one
27372
27373
27374 if (txr.width - txr.usedWidth < eleScaledW) {
27375 txr = addNewTxr();
27376 }
27377
27378 var scalableFrom = function scalableFrom(otherCache) {
27379 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27380 };
27381
27382 var deqing = reason && reason === getTxrReasons.dequeue;
27383 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27384 var downscaleReq = reason && reason === getTxrReasons.downscale;
27385 var higherCache; // the nearest cache with a higher level
27386
27387 for (var l = lvl + 1; l <= maxLvl; l++) {
27388 var c = lookup.get(ele, l);
27389
27390 if (c) {
27391 higherCache = c;
27392 break;
27393 }
27394 }
27395
27396 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27397
27398 var downscale = function downscale() {
27399 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27400 }; // reset ele area in texture
27401
27402
27403 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27404 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27405
27406 if (scalableFrom(oneUpCache)) {
27407 // then we can relatively cheaply rescale the existing image w/o rerendering
27408 downscale();
27409 } else if (scalableFrom(higherCache)) {
27410 // then use the higher cache for now and queue the next level down
27411 // to cheaply scale towards the smaller level
27412 if (highQualityReq) {
27413 for (var _l = higherCache.level; _l > lvl; _l--) {
27414 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27415 }
27416
27417 downscale();
27418 } else {
27419 self.queueElement(ele, higherCache.level - 1);
27420 return higherCache;
27421 }
27422 } else {
27423 var lowerCache; // the nearest cache with a lower level
27424
27425 if (!deqing && !highQualityReq && !downscaleReq) {
27426 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27427 var _c = lookup.get(ele, _l2);
27428
27429 if (_c) {
27430 lowerCache = _c;
27431 break;
27432 }
27433 }
27434 }
27435
27436 if (scalableFrom(lowerCache)) {
27437 // then use the lower quality cache for now and queue the better one for later
27438 self.queueElement(ele, lvl);
27439 return lowerCache;
27440 }
27441
27442 txr.context.translate(txr.usedWidth, 0);
27443 txr.context.scale(scale, scale);
27444 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27445 txr.context.scale(1 / scale, 1 / scale);
27446 txr.context.translate(-txr.usedWidth, 0);
27447 }
27448
27449 eleCache = {
27450 x: txr.usedWidth,
27451 texture: txr,
27452 level: lvl,
27453 scale: scale,
27454 width: eleScaledW,
27455 height: eleScaledH,
27456 scaledLabelShown: scaledLabelShown
27457 };
27458 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27459 txr.eleCaches.push(eleCache);
27460 lookup.set(ele, lvl, eleCache);
27461 self.checkTextureFullness(txr);
27462 return eleCache;
27463};
27464
27465ETCp.invalidateElements = function (eles) {
27466 for (var i = 0; i < eles.length; i++) {
27467 this.invalidateElement(eles[i]);
27468 }
27469};
27470
27471ETCp.invalidateElement = function (ele) {
27472 var self = this;
27473 var lookup = self.lookup;
27474 var caches = [];
27475 var invalid = lookup.isInvalid(ele);
27476
27477 if (!invalid) {
27478 return; // override the invalidation request if the element key has not changed
27479 }
27480
27481 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27482 var cache = lookup.getForCachedKey(ele, lvl);
27483
27484 if (cache) {
27485 caches.push(cache);
27486 }
27487 }
27488
27489 var noOtherElesUseCache = lookup.invalidate(ele);
27490
27491 if (noOtherElesUseCache) {
27492 for (var i = 0; i < caches.length; i++) {
27493 var _cache = caches[i];
27494 var txr = _cache.texture; // remove space from the texture it belongs to
27495
27496 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27497
27498 _cache.invalidated = true; // retire the texture if its utility is low
27499
27500 self.checkTextureUtility(txr);
27501 }
27502 } // remove from queue since the old req was for the old state
27503
27504
27505 self.removeFromQueue(ele);
27506};
27507
27508ETCp.checkTextureUtility = function (txr) {
27509 // invalidate all entries in the cache if the cache size is small
27510 if (txr.invalidatedWidth >= minUtility * txr.width) {
27511 this.retireTexture(txr);
27512 }
27513};
27514
27515ETCp.checkTextureFullness = function (txr) {
27516 // if texture has been mostly filled and passed over several times, remove
27517 // it from the queue so we don't need to waste time looking at it to put new things
27518 var self = this;
27519 var txrQ = self.getTextureQueue(txr.height);
27520
27521 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27522 removeFromArray(txrQ, txr);
27523 } else {
27524 txr.fullnessChecks++;
27525 }
27526};
27527
27528ETCp.retireTexture = function (txr) {
27529 var self = this;
27530 var txrH = txr.height;
27531 var txrQ = self.getTextureQueue(txrH);
27532 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27533
27534 removeFromArray(txrQ, txr);
27535 txr.retired = true; // remove the refs from the eles to the caches:
27536
27537 var eleCaches = txr.eleCaches;
27538
27539 for (var i = 0; i < eleCaches.length; i++) {
27540 var eleCache = eleCaches[i];
27541 lookup.deleteCache(eleCache.key, eleCache.level);
27542 }
27543
27544 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27545
27546 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27547 rtxtrQ.push(txr);
27548};
27549
27550ETCp.addTexture = function (txrH, minW) {
27551 var self = this;
27552 var txrQ = self.getTextureQueue(txrH);
27553 var txr = {};
27554 txrQ.push(txr);
27555 txr.eleCaches = [];
27556 txr.height = txrH;
27557 txr.width = Math.max(defTxrWidth, minW);
27558 txr.usedWidth = 0;
27559 txr.invalidatedWidth = 0;
27560 txr.fullnessChecks = 0;
27561 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27562 txr.context = txr.canvas.getContext('2d');
27563 return txr;
27564};
27565
27566ETCp.recycleTexture = function (txrH, minW) {
27567 var self = this;
27568 var txrQ = self.getTextureQueue(txrH);
27569 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27570
27571 for (var i = 0; i < rtxtrQ.length; i++) {
27572 var txr = rtxtrQ[i];
27573
27574 if (txr.width >= minW) {
27575 txr.retired = false;
27576 txr.usedWidth = 0;
27577 txr.invalidatedWidth = 0;
27578 txr.fullnessChecks = 0;
27579 clearArray(txr.eleCaches);
27580 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27581 txr.context.clearRect(0, 0, txr.width, txr.height);
27582 removeFromArray(rtxtrQ, txr);
27583 txrQ.push(txr);
27584 return txr;
27585 }
27586 }
27587};
27588
27589ETCp.queueElement = function (ele, lvl) {
27590 var self = this;
27591 var q = self.getElementQueue();
27592 var k2q = self.getElementKeyToQueue();
27593 var key = this.getKey(ele);
27594 var existingReq = k2q[key];
27595
27596 if (existingReq) {
27597 // use the max lvl b/c in between lvls are cheap to make
27598 existingReq.level = Math.max(existingReq.level, lvl);
27599 existingReq.eles.merge(ele);
27600 existingReq.reqs++;
27601 q.updateItem(existingReq);
27602 } else {
27603 var req = {
27604 eles: ele.spawn().merge(ele),
27605 level: lvl,
27606 reqs: 1,
27607 key: key
27608 };
27609 q.push(req);
27610 k2q[key] = req;
27611 }
27612};
27613
27614ETCp.dequeue = function (pxRatio
27615/*, extent*/
27616) {
27617 var self = this;
27618 var q = self.getElementQueue();
27619 var k2q = self.getElementKeyToQueue();
27620 var dequeued = [];
27621 var lookup = self.lookup;
27622
27623 for (var i = 0; i < maxDeqSize; i++) {
27624 if (q.size() > 0) {
27625 var req = q.pop();
27626 var key = req.key;
27627 var ele = req.eles[0]; // all eles have the same key
27628
27629 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27630
27631 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27632
27633 if (cacheExists) {
27634 continue;
27635 }
27636
27637 dequeued.push(req);
27638 var bb = self.getBoundingBox(ele);
27639 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27640 } else {
27641 break;
27642 }
27643 }
27644
27645 return dequeued;
27646};
27647
27648ETCp.removeFromQueue = function (ele) {
27649 var self = this;
27650 var q = self.getElementQueue();
27651 var k2q = self.getElementKeyToQueue();
27652 var key = this.getKey(ele);
27653 var req = k2q[key];
27654
27655 if (req != null) {
27656 if (req.eles.length === 1) {
27657 // remove if last ele in the req
27658 // bring to front of queue
27659 req.reqs = MAX_INT;
27660 q.updateItem(req);
27661 q.pop(); // remove from queue
27662
27663 k2q[key] = null; // remove from lookup map
27664 } else {
27665 // otherwise just remove ele from req
27666 req.eles.unmerge(ele);
27667 }
27668 }
27669};
27670
27671ETCp.onDequeue = function (fn) {
27672 this.onDequeues.push(fn);
27673};
27674
27675ETCp.offDequeue = function (fn) {
27676 removeFromArray(this.onDequeues, fn);
27677};
27678
27679ETCp.setupDequeueing = defs.setupDequeueing({
27680 deqRedrawThreshold: deqRedrawThreshold,
27681 deqCost: deqCost,
27682 deqAvgCost: deqAvgCost,
27683 deqNoDrawCost: deqNoDrawCost,
27684 deqFastCost: deqFastCost,
27685 deq: function deq(self, pxRatio, extent) {
27686 return self.dequeue(pxRatio, extent);
27687 },
27688 onDeqd: function onDeqd(self, deqd) {
27689 for (var i = 0; i < self.onDequeues.length; i++) {
27690 var fn = self.onDequeues[i];
27691 fn(deqd);
27692 }
27693 },
27694 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27695 for (var i = 0; i < deqd.length; i++) {
27696 var eles = deqd[i].eles;
27697
27698 for (var j = 0; j < eles.length; j++) {
27699 var bb = eles[j].boundingBox();
27700
27701 if (boundingBoxesIntersect(bb, extent)) {
27702 return true;
27703 }
27704 }
27705 }
27706
27707 return false;
27708 },
27709 priority: function priority(self) {
27710 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27711 }
27712});
27713
27714var defNumLayers = 1; // default number of layers to use
27715
27716var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27717
27718var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27719
27720var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27721
27722var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27723
27724var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27725
27726var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27727
27728var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27729
27730var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27731
27732var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27733
27734var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27735
27736var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27737
27738var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27739
27740var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27741// var log = function(){ console.log.apply( console, arguments ); };
27742
27743var LayeredTextureCache = function LayeredTextureCache(renderer) {
27744 var self = this;
27745 var r = self.renderer = renderer;
27746 var cy = r.cy;
27747 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27748
27749 self.firstGet = true;
27750 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27751 self.skipping = false;
27752 self.eleTxrDeqs = cy.collection();
27753 self.scheduleElementRefinement = util(function () {
27754 self.refineElementTextures(self.eleTxrDeqs);
27755 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27756 }, refineEleDebounceTime);
27757 r.beforeRender(function (willDraw, now) {
27758 if (now - self.lastInvalidationTime <= invalidThreshold) {
27759 self.skipping = true;
27760 } else {
27761 self.skipping = false;
27762 }
27763 }, r.beforeRenderPriorities.lyrTxrSkip);
27764
27765 var qSort = function qSort(a, b) {
27766 return b.reqs - a.reqs;
27767 };
27768
27769 self.layersQueue = new Heap(qSort);
27770 self.setupDequeueing();
27771};
27772
27773var LTCp = LayeredTextureCache.prototype;
27774var layerIdPool = 0;
27775var MAX_INT$1 = Math.pow(2, 53) - 1;
27776
27777LTCp.makeLayer = function (bb, lvl) {
27778 var scale = Math.pow(2, lvl);
27779 var w = Math.ceil(bb.w * scale);
27780 var h = Math.ceil(bb.h * scale);
27781 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27782 var layer = {
27783 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27784 bb: bb,
27785 level: lvl,
27786 width: w,
27787 height: h,
27788 canvas: canvas,
27789 context: canvas.getContext('2d'),
27790 eles: [],
27791 elesQueue: [],
27792 reqs: 0
27793 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27794
27795 var cxt = layer.context;
27796 var dx = -layer.bb.x1;
27797 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27798
27799 cxt.scale(scale, scale);
27800 cxt.translate(dx, dy);
27801 return layer;
27802};
27803
27804LTCp.getLayers = function (eles, pxRatio, lvl) {
27805 var self = this;
27806 var r = self.renderer;
27807 var cy = r.cy;
27808 var zoom = cy.zoom();
27809 var firstGet = self.firstGet;
27810 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27811 //log eles.map(function(ele){ return ele.id() }) );
27812
27813 if (lvl == null) {
27814 lvl = Math.ceil(log2(zoom * pxRatio));
27815
27816 if (lvl < minLvl$1) {
27817 lvl = minLvl$1;
27818 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27819 return null;
27820 }
27821 }
27822
27823 self.validateLayersElesOrdering(lvl, eles);
27824 var layersByLvl = self.layersByLevel;
27825 var scale = Math.pow(2, lvl);
27826 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27827 var bb;
27828 var lvlComplete = self.levelIsComplete(lvl, eles);
27829 var tmpLayers;
27830
27831 var checkTempLevels = function checkTempLevels() {
27832 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27833 self.validateLayersElesOrdering(l, eles);
27834
27835 if (self.levelIsComplete(l, eles)) {
27836 tmpLayers = layersByLvl[l];
27837 return true;
27838 }
27839 };
27840
27841 var checkLvls = function checkLvls(dir) {
27842 if (tmpLayers) {
27843 return;
27844 }
27845
27846 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27847 if (canUseAsTmpLvl(l)) {
27848 break;
27849 }
27850 }
27851 };
27852
27853 checkLvls(+1);
27854 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27855
27856 for (var i = layers.length - 1; i >= 0; i--) {
27857 var layer = layers[i];
27858
27859 if (layer.invalid) {
27860 removeFromArray(layers, layer);
27861 }
27862 }
27863 };
27864
27865 if (!lvlComplete) {
27866 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27867 // and later queue the current layerset so we can get the proper quality level soon
27868 checkTempLevels();
27869 } else {
27870 // log('level complete, using existing layers\n--');
27871 return layers;
27872 }
27873
27874 var getBb = function getBb() {
27875 if (!bb) {
27876 bb = makeBoundingBox();
27877
27878 for (var i = 0; i < eles.length; i++) {
27879 updateBoundingBox(bb, eles[i].boundingBox());
27880 }
27881 }
27882
27883 return bb;
27884 };
27885
27886 var makeLayer = function makeLayer(opts) {
27887 opts = opts || {};
27888 var after = opts.after;
27889 getBb();
27890 var area = bb.w * scale * (bb.h * scale);
27891
27892 if (area > maxLayerArea) {
27893 return null;
27894 }
27895
27896 var layer = self.makeLayer(bb, lvl);
27897
27898 if (after != null) {
27899 var index = layers.indexOf(after) + 1;
27900 layers.splice(index, 0, layer);
27901 } else if (opts.insert === undefined || opts.insert) {
27902 // no after specified => first layer made so put at start
27903 layers.unshift(layer);
27904 } // if( tmpLayers ){
27905 //self.queueLayer( layer );
27906 // }
27907
27908
27909 return layer;
27910 };
27911
27912 if (self.skipping && !firstGet) {
27913 // log('skip layers');
27914 return null;
27915 } // log('do layers');
27916
27917
27918 var layer = null;
27919 var maxElesPerLayer = eles.length / defNumLayers;
27920 var allowLazyQueueing = !firstGet;
27921
27922 for (var i = 0; i < eles.length; i++) {
27923 var ele = eles[i];
27924 var rs = ele._private.rscratch;
27925 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27926
27927 var existingLayer = caches[lvl];
27928
27929 if (existingLayer) {
27930 // reuse layer for later eles
27931 // log('reuse layer for', ele.id());
27932 layer = existingLayer;
27933 continue;
27934 }
27935
27936 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27937 // log('make new layer for ele %s', ele.id());
27938 layer = makeLayer({
27939 insert: true,
27940 after: layer
27941 }); // if now layer can be built then we can't use layers at this level
27942
27943 if (!layer) {
27944 return null;
27945 } // log('new layer with id %s', layer.id);
27946
27947 }
27948
27949 if (tmpLayers || allowLazyQueueing) {
27950 // log('queue ele %s in layer %s', ele.id(), layer.id);
27951 self.queueLayer(layer, ele);
27952 } else {
27953 // log('draw ele %s in layer %s', ele.id(), layer.id);
27954 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27955 }
27956
27957 layer.eles.push(ele);
27958 caches[lvl] = layer;
27959 } // log('--');
27960
27961
27962 if (tmpLayers) {
27963 // then we only queued the current layerset and can't draw it yet
27964 return tmpLayers;
27965 }
27966
27967 if (allowLazyQueueing) {
27968 // log('lazy queue level', lvl);
27969 return null;
27970 }
27971
27972 return layers;
27973}; // a layer may want to use an ele cache of a higher level to avoid blurriness
27974// so the layer level might not equal the ele level
27975
27976
27977LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
27978 return lvl;
27979};
27980
27981LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
27982 var self = this;
27983 var r = this.renderer;
27984 var context = layer.context;
27985 var bb = ele.boundingBox();
27986
27987 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
27988 return;
27989 }
27990
27991 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
27992
27993 {
27994 r.setImgSmoothing(context, false);
27995 }
27996
27997 {
27998 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
27999 }
28000
28001 {
28002 r.setImgSmoothing(context, true);
28003 }
28004};
28005
28006LTCp.levelIsComplete = function (lvl, eles) {
28007 var self = this;
28008 var layers = self.layersByLevel[lvl];
28009
28010 if (!layers || layers.length === 0) {
28011 return false;
28012 }
28013
28014 var numElesInLayers = 0;
28015
28016 for (var i = 0; i < layers.length; i++) {
28017 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28018
28019 if (layer.reqs > 0) {
28020 return false;
28021 } // if the layer is invalid, the level is not complete
28022
28023
28024 if (layer.invalid) {
28025 return false;
28026 }
28027
28028 numElesInLayers += layer.eles.length;
28029 } // we should have exactly the number of eles passed in to be complete
28030
28031
28032 if (numElesInLayers !== eles.length) {
28033 return false;
28034 }
28035
28036 return true;
28037};
28038
28039LTCp.validateLayersElesOrdering = function (lvl, eles) {
28040 var layers = this.layersByLevel[lvl];
28041
28042 if (!layers) {
28043 return;
28044 } // if in a layer the eles are not in the same order, then the layer is invalid
28045 // (i.e. there is an ele in between the eles in the layer)
28046
28047
28048 for (var i = 0; i < layers.length; i++) {
28049 var layer = layers[i];
28050 var offset = -1; // find the offset
28051
28052 for (var j = 0; j < eles.length; j++) {
28053 if (layer.eles[0] === eles[j]) {
28054 offset = j;
28055 break;
28056 }
28057 }
28058
28059 if (offset < 0) {
28060 // then the layer has nonexistant elements and is invalid
28061 this.invalidateLayer(layer);
28062 continue;
28063 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28064
28065
28066 var o = offset;
28067
28068 for (var j = 0; j < layer.eles.length; j++) {
28069 if (layer.eles[j] !== eles[o + j]) {
28070 // log('invalidate based on ordering', layer.id);
28071 this.invalidateLayer(layer);
28072 break;
28073 }
28074 }
28075 }
28076};
28077
28078LTCp.updateElementsInLayers = function (eles, update) {
28079 var self = this;
28080 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28081 // layer itself along the way
28082
28083 for (var i = 0; i < eles.length; i++) {
28084 var req = isEles ? null : eles[i];
28085 var ele = isEles ? eles[i] : eles[i].ele;
28086 var rs = ele._private.rscratch;
28087 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28088
28089 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28090 var layer = caches[l];
28091
28092 if (!layer) {
28093 continue;
28094 } // if update is a request from the ele cache, then it affects only
28095 // the matching level
28096
28097
28098 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28099 continue;
28100 }
28101
28102 update(layer, ele, req);
28103 }
28104 }
28105};
28106
28107LTCp.haveLayers = function () {
28108 var self = this;
28109 var haveLayers = false;
28110
28111 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28112 var layers = self.layersByLevel[l];
28113
28114 if (layers && layers.length > 0) {
28115 haveLayers = true;
28116 break;
28117 }
28118 }
28119
28120 return haveLayers;
28121};
28122
28123LTCp.invalidateElements = function (eles) {
28124 var self = this;
28125
28126 if (eles.length === 0) {
28127 return;
28128 }
28129
28130 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28131
28132 if (eles.length === 0 || !self.haveLayers()) {
28133 return;
28134 }
28135
28136 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28137 self.invalidateLayer(layer);
28138 });
28139};
28140
28141LTCp.invalidateLayer = function (layer) {
28142 // log('update invalidate layer time');
28143 this.lastInvalidationTime = performanceNow();
28144
28145 if (layer.invalid) {
28146 return;
28147 } // save cycles
28148
28149
28150 var lvl = layer.level;
28151 var eles = layer.eles;
28152 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28153
28154 removeFromArray(layers, layer); // layer.eles = [];
28155
28156 layer.elesQueue = [];
28157 layer.invalid = true;
28158
28159 if (layer.replacement) {
28160 layer.replacement.invalid = true;
28161 }
28162
28163 for (var i = 0; i < eles.length; i++) {
28164 var caches = eles[i]._private.rscratch.imgLayerCaches;
28165
28166 if (caches) {
28167 caches[lvl] = null;
28168 }
28169 }
28170};
28171
28172LTCp.refineElementTextures = function (eles) {
28173 var self = this; // log('refine', eles.length);
28174
28175 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28176 var rLyr = layer.replacement;
28177
28178 if (!rLyr) {
28179 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28180 rLyr.replaces = layer;
28181 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28182 }
28183
28184 if (!rLyr.reqs) {
28185 for (var i = 0; i < rLyr.eles.length; i++) {
28186 self.queueLayer(rLyr, rLyr.eles[i]);
28187 } // log('queue replacement layer refinement', rLyr.id);
28188
28189 }
28190 });
28191};
28192
28193LTCp.enqueueElementRefinement = function (ele) {
28194
28195 this.eleTxrDeqs.merge(ele);
28196 this.scheduleElementRefinement();
28197};
28198
28199LTCp.queueLayer = function (layer, ele) {
28200 var self = this;
28201 var q = self.layersQueue;
28202 var elesQ = layer.elesQueue;
28203 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28204
28205 if (layer.replacement) {
28206 return;
28207 }
28208
28209 if (ele) {
28210 if (hasId[ele.id()]) {
28211 return;
28212 }
28213
28214 elesQ.push(ele);
28215 hasId[ele.id()] = true;
28216 }
28217
28218 if (layer.reqs) {
28219 layer.reqs++;
28220 q.updateItem(layer);
28221 } else {
28222 layer.reqs = 1;
28223 q.push(layer);
28224 }
28225};
28226
28227LTCp.dequeue = function (pxRatio) {
28228 var self = this;
28229 var q = self.layersQueue;
28230 var deqd = [];
28231 var eleDeqs = 0;
28232
28233 while (eleDeqs < maxDeqSize$1) {
28234 if (q.size() === 0) {
28235 break;
28236 }
28237
28238 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28239
28240 if (layer.replacement) {
28241 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28242 q.pop();
28243 continue;
28244 } // if this is a replacement layer that has been superceded, then forget it
28245
28246
28247 if (layer.replaces && layer !== layer.replaces.replacement) {
28248 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28249 q.pop();
28250 continue;
28251 }
28252
28253 if (layer.invalid) {
28254 // log('replacement layer %s is invalid; dequeued', layer.id);
28255 q.pop();
28256 continue;
28257 }
28258
28259 var ele = layer.elesQueue.shift();
28260
28261 if (ele) {
28262 // log('dequeue layer %s', layer.id);
28263 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28264 eleDeqs++;
28265 }
28266
28267 if (deqd.length === 0) {
28268 // we need only one entry in deqd to queue redrawing etc
28269 deqd.push(true);
28270 } // if the layer has all its eles done, then remove from the queue
28271
28272
28273 if (layer.elesQueue.length === 0) {
28274 q.pop();
28275 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28276 // when a replacement layer is dequeued, it replaces the old layer in the level
28277
28278 if (layer.replaces) {
28279 self.applyLayerReplacement(layer);
28280 }
28281
28282 self.requestRedraw();
28283 }
28284 }
28285
28286 return deqd;
28287};
28288
28289LTCp.applyLayerReplacement = function (layer) {
28290 var self = this;
28291 var layersInLevel = self.layersByLevel[layer.level];
28292 var replaced = layer.replaces;
28293 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28294 // refs would be a mistake (i.e. overwriting the true active layer)
28295
28296 if (index < 0 || replaced.invalid) {
28297 // log('replacement layer would have no effect', layer.id);
28298 return;
28299 }
28300
28301 layersInLevel[index] = layer; // replace level ref
28302 // replace refs in eles
28303
28304 for (var i = 0; i < layer.eles.length; i++) {
28305 var _p = layer.eles[i]._private;
28306 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28307
28308 if (cache) {
28309 cache[layer.level] = layer;
28310 }
28311 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28312
28313
28314 self.requestRedraw();
28315};
28316
28317LTCp.requestRedraw = util(function () {
28318 var r = this.renderer;
28319 r.redrawHint('eles', true);
28320 r.redrawHint('drag', true);
28321 r.redraw();
28322}, 100);
28323LTCp.setupDequeueing = defs.setupDequeueing({
28324 deqRedrawThreshold: deqRedrawThreshold$1,
28325 deqCost: deqCost$1,
28326 deqAvgCost: deqAvgCost$1,
28327 deqNoDrawCost: deqNoDrawCost$1,
28328 deqFastCost: deqFastCost$1,
28329 deq: function deq(self, pxRatio) {
28330 return self.dequeue(pxRatio);
28331 },
28332 onDeqd: noop,
28333 shouldRedraw: trueify,
28334 priority: function priority(self) {
28335 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28336 }
28337});
28338
28339var CRp = {};
28340var impl;
28341
28342function polygon(context, points) {
28343 for (var i = 0; i < points.length; i++) {
28344 var pt = points[i];
28345 context.lineTo(pt.x, pt.y);
28346 }
28347}
28348
28349function triangleBackcurve(context, points, controlPoint) {
28350 var firstPt;
28351
28352 for (var i = 0; i < points.length; i++) {
28353 var pt = points[i];
28354
28355 if (i === 0) {
28356 firstPt = pt;
28357 }
28358
28359 context.lineTo(pt.x, pt.y);
28360 }
28361
28362 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28363}
28364
28365function triangleTee(context, trianglePoints, teePoints) {
28366 if (context.beginPath) {
28367 context.beginPath();
28368 }
28369
28370 var triPts = trianglePoints;
28371
28372 for (var i = 0; i < triPts.length; i++) {
28373 var pt = triPts[i];
28374 context.lineTo(pt.x, pt.y);
28375 }
28376
28377 var teePts = teePoints;
28378 var firstTeePt = teePoints[0];
28379 context.moveTo(firstTeePt.x, firstTeePt.y);
28380
28381 for (var i = 1; i < teePts.length; i++) {
28382 var pt = teePts[i];
28383 context.lineTo(pt.x, pt.y);
28384 }
28385
28386 if (context.closePath) {
28387 context.closePath();
28388 }
28389}
28390
28391function circleTriangle(context, trianglePoints, rx, ry, r) {
28392 if (context.beginPath) {
28393 context.beginPath();
28394 }
28395
28396 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28397 var triPts = trianglePoints;
28398 var firstTrPt = triPts[0];
28399 context.moveTo(firstTrPt.x, firstTrPt.y);
28400
28401 for (var i = 0; i < triPts.length; i++) {
28402 var pt = triPts[i];
28403 context.lineTo(pt.x, pt.y);
28404 }
28405
28406 if (context.closePath) {
28407 context.closePath();
28408 }
28409}
28410
28411function circle(context, rx, ry, r) {
28412 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28413}
28414
28415CRp.arrowShapeImpl = function (name) {
28416 return (impl || (impl = {
28417 'polygon': polygon,
28418 'triangle-backcurve': triangleBackcurve,
28419 'triangle-tee': triangleTee,
28420 'circle-triangle': circleTriangle,
28421 'triangle-cross': triangleTee,
28422 'circle': circle
28423 }))[name];
28424};
28425
28426var CRp$1 = {};
28427
28428CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28429 var r = this;
28430
28431 if (ele.isNode()) {
28432 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28433 } else {
28434 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28435 }
28436};
28437
28438CRp$1.drawElementOverlay = function (context, ele) {
28439 var r = this;
28440
28441 if (ele.isNode()) {
28442 r.drawNodeOverlay(context, ele);
28443 } else {
28444 r.drawEdgeOverlay(context, ele);
28445 }
28446};
28447
28448CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28449 var r = this;
28450 var bb = eleTxrCache.getBoundingBox(ele);
28451
28452 if (bb.w === 0 || bb.h === 0) {
28453 return;
28454 } // ignore zero size case
28455
28456
28457 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28458
28459 if (eleCache != null) {
28460 var opacity = getOpacity(r, ele);
28461
28462 if (opacity === 0) {
28463 return;
28464 }
28465
28466 var theta = getRotation(r, ele);
28467 var x1 = bb.x1,
28468 y1 = bb.y1,
28469 w = bb.w,
28470 h = bb.h;
28471 var x, y, sx, sy, smooth;
28472
28473 if (theta !== 0) {
28474 var rotPt = eleTxrCache.getRotationPoint(ele);
28475 sx = rotPt.x;
28476 sy = rotPt.y;
28477 context.translate(sx, sy);
28478 context.rotate(theta);
28479 smooth = r.getImgSmoothing(context);
28480
28481 if (!smooth) {
28482 r.setImgSmoothing(context, true);
28483 }
28484
28485 var off = eleTxrCache.getRotationOffset(ele);
28486 x = off.x;
28487 y = off.y;
28488 } else {
28489 x = x1;
28490 y = y1;
28491 }
28492
28493 var oldGlobalAlpha;
28494
28495 if (opacity !== 1) {
28496 oldGlobalAlpha = context.globalAlpha;
28497 context.globalAlpha = oldGlobalAlpha * opacity;
28498 }
28499
28500 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28501
28502 if (opacity !== 1) {
28503 context.globalAlpha = oldGlobalAlpha;
28504 }
28505
28506 if (theta !== 0) {
28507 context.rotate(-theta);
28508 context.translate(-sx, -sy);
28509
28510 if (!smooth) {
28511 r.setImgSmoothing(context, false);
28512 }
28513 }
28514 } else {
28515 eleTxrCache.drawElement(context, ele); // direct draw fallback
28516 }
28517};
28518
28519var getZeroRotation = function getZeroRotation() {
28520 return 0;
28521};
28522
28523var getLabelRotation = function getLabelRotation(r, ele) {
28524 return r.getTextAngle(ele, null);
28525};
28526
28527var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28528 return r.getTextAngle(ele, 'source');
28529};
28530
28531var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28532 return r.getTextAngle(ele, 'target');
28533};
28534
28535var getOpacity = function getOpacity(r, ele) {
28536 return ele.effectiveOpacity();
28537};
28538
28539var getTextOpacity = function getTextOpacity(e, ele) {
28540 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28541};
28542
28543CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28544 var r = this;
28545 var _r$data = r.data,
28546 eleTxrCache = _r$data.eleTxrCache,
28547 lblTxrCache = _r$data.lblTxrCache,
28548 slbTxrCache = _r$data.slbTxrCache,
28549 tlbTxrCache = _r$data.tlbTxrCache;
28550 var bb = ele.boundingBox();
28551 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28552
28553 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28554 return;
28555 }
28556
28557 if (!extent || boundingBoxesIntersect(bb, extent)) {
28558 var isEdge = ele.isEdge();
28559
28560 var badLine = ele.element()._private.rscratch.badLine;
28561
28562 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28563
28564 if (!isEdge || !badLine) {
28565 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28566 }
28567
28568 if (isEdge && !badLine) {
28569 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28570 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28571 }
28572
28573 r.drawElementOverlay(context, ele);
28574 }
28575};
28576
28577CRp$1.drawElements = function (context, eles) {
28578 var r = this;
28579
28580 for (var i = 0; i < eles.length; i++) {
28581 var ele = eles[i];
28582 r.drawElement(context, ele);
28583 }
28584};
28585
28586CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28587 var r = this;
28588
28589 for (var i = 0; i < eles.length; i++) {
28590 var ele = eles[i];
28591 r.drawCachedElement(context, ele, pxRatio, extent);
28592 }
28593};
28594
28595CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28596 var r = this;
28597
28598 for (var i = 0; i < eles.length; i++) {
28599 var ele = eles[i];
28600
28601 if (!ele.isNode()) {
28602 continue;
28603 }
28604
28605 r.drawCachedElement(context, ele, pxRatio, extent);
28606 }
28607};
28608
28609CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28610 var r = this;
28611 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28612
28613 if (layers) {
28614 for (var i = 0; i < layers.length; i++) {
28615 var layer = layers[i];
28616 var bb = layer.bb;
28617
28618 if (bb.w === 0 || bb.h === 0) {
28619 continue;
28620 }
28621
28622 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28623 }
28624 } else {
28625 // fall back on plain caching if no layers
28626 r.drawCachedElements(context, eles, pxRatio, extent);
28627 }
28628};
28629
28630/* global Path2D */
28631var CRp$2 = {};
28632
28633CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28634 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28635 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28636 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28637 var r = this;
28638 var rs = edge._private.rscratch;
28639
28640 if (shouldDrawOpacity && !edge.visible()) {
28641 return;
28642 } // if bezier ctrl pts can not be calculated, then die
28643
28644
28645 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28646 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28647 return;
28648 }
28649
28650 var bb;
28651
28652 if (shiftToOriginWithBb) {
28653 bb = shiftToOriginWithBb;
28654 context.translate(-bb.x1, -bb.y1);
28655 }
28656
28657 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28658 var lineStyle = edge.pstyle('line-style').value;
28659 var edgeWidth = edge.pstyle('width').pfValue;
28660 var lineCap = edge.pstyle('line-cap').value;
28661
28662 var drawLine = function drawLine() {
28663 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28664 context.lineWidth = edgeWidth;
28665 context.lineCap = lineCap;
28666 r.eleStrokeStyle(context, edge, strokeOpacity);
28667 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28668 context.lineCap = 'butt'; // reset for other drawing functions
28669 };
28670
28671 var drawOverlay = function drawOverlay() {
28672 if (!shouldDrawOverlay) {
28673 return;
28674 }
28675
28676 r.drawEdgeOverlay(context, edge);
28677 };
28678
28679 var drawArrows = function drawArrows() {
28680 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28681 r.drawArrowheads(context, edge, arrowOpacity);
28682 };
28683
28684 var drawText = function drawText() {
28685 r.drawElementText(context, edge, null, drawLabel);
28686 };
28687
28688 context.lineJoin = 'round';
28689 var ghost = edge.pstyle('ghost').value === 'yes';
28690
28691 if (ghost) {
28692 var gx = edge.pstyle('ghost-offset-x').pfValue;
28693 var gy = edge.pstyle('ghost-offset-y').pfValue;
28694 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28695 var effectiveGhostOpacity = opacity * ghostOpacity;
28696 context.translate(gx, gy);
28697 drawLine(effectiveGhostOpacity);
28698 drawArrows(effectiveGhostOpacity);
28699 context.translate(-gx, -gy);
28700 }
28701
28702 drawLine();
28703 drawArrows();
28704 drawOverlay();
28705 drawText();
28706
28707 if (shiftToOriginWithBb) {
28708 context.translate(bb.x1, bb.y1);
28709 }
28710};
28711
28712CRp$2.drawEdgeOverlay = function (context, edge) {
28713 if (!edge.visible()) {
28714 return;
28715 }
28716
28717 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28718
28719 if (overlayOpacity === 0) {
28720 return;
28721 }
28722
28723 var r = this;
28724 var usePaths = r.usePaths();
28725 var rs = edge._private.rscratch;
28726 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28727 var overlayWidth = 2 * overlayPadding;
28728 var overlayColor = edge.pstyle('overlay-color').value;
28729 context.lineWidth = overlayWidth;
28730
28731 if (rs.edgeType === 'self' && !usePaths) {
28732 context.lineCap = 'butt';
28733 } else {
28734 context.lineCap = 'round';
28735 }
28736
28737 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28738 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28739};
28740
28741CRp$2.drawEdgePath = function (edge, context, pts, type) {
28742 var rs = edge._private.rscratch;
28743 var canvasCxt = context;
28744 var path;
28745 var pathCacheHit = false;
28746 var usePaths = this.usePaths();
28747 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28748 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28749
28750 if (usePaths) {
28751 var pathCacheKey = pts.join('$');
28752 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28753
28754 if (keyMatches) {
28755 path = context = rs.pathCache;
28756 pathCacheHit = true;
28757 } else {
28758 path = context = new Path2D();
28759 rs.pathCacheKey = pathCacheKey;
28760 rs.pathCache = path;
28761 }
28762 }
28763
28764 if (canvasCxt.setLineDash) {
28765 // for very outofdate browsers
28766 switch (type) {
28767 case 'dotted':
28768 canvasCxt.setLineDash([1, 1]);
28769 break;
28770
28771 case 'dashed':
28772 canvasCxt.setLineDash(lineDashPattern);
28773 canvasCxt.lineDashOffset = lineDashOffset;
28774 break;
28775
28776 case 'solid':
28777 canvasCxt.setLineDash([]);
28778 break;
28779 }
28780 }
28781
28782 if (!pathCacheHit && !rs.badLine) {
28783 if (context.beginPath) {
28784 context.beginPath();
28785 }
28786
28787 context.moveTo(pts[0], pts[1]);
28788
28789 switch (rs.edgeType) {
28790 case 'bezier':
28791 case 'self':
28792 case 'compound':
28793 case 'multibezier':
28794 for (var i = 2; i + 3 < pts.length; i += 4) {
28795 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28796 }
28797
28798 break;
28799
28800 case 'straight':
28801 case 'segments':
28802 case 'haystack':
28803 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28804 context.lineTo(pts[_i], pts[_i + 1]);
28805 }
28806
28807 break;
28808 }
28809 }
28810
28811 context = canvasCxt;
28812
28813 if (usePaths) {
28814 context.stroke(path);
28815 } else {
28816 context.stroke();
28817 } // reset any line dashes
28818
28819
28820 if (context.setLineDash) {
28821 // for very outofdate browsers
28822 context.setLineDash([]);
28823 }
28824};
28825
28826CRp$2.drawArrowheads = function (context, edge, opacity) {
28827 var rs = edge._private.rscratch;
28828 var isHaystack = rs.edgeType === 'haystack';
28829
28830 if (!isHaystack) {
28831 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28832 }
28833
28834 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28835 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28836
28837 if (!isHaystack) {
28838 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28839 }
28840};
28841
28842CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28843 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28844 return;
28845 }
28846
28847 var self = this;
28848 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28849
28850 if (arrowShape === 'none') {
28851 return;
28852 }
28853
28854 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28855 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28856 var edgeWidth = edge.pstyle('width').pfValue;
28857 var edgeOpacity = edge.pstyle('opacity').value;
28858
28859 if (opacity === undefined) {
28860 opacity = edgeOpacity;
28861 }
28862
28863 var gco = context.globalCompositeOperation;
28864
28865 if (opacity !== 1 || arrowFill === 'hollow') {
28866 // then extra clear is needed
28867 context.globalCompositeOperation = 'destination-out';
28868 self.colorFillStyle(context, 255, 255, 255, 1);
28869 self.colorStrokeStyle(context, 255, 255, 255, 1);
28870 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28871 context.globalCompositeOperation = gco;
28872 } // otherwise, the opaque arrow clears it for free :)
28873
28874
28875 var color = edge.pstyle(prefix + '-arrow-color').value;
28876 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28877 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28878 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28879};
28880
28881CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28882 var r = this;
28883 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28884 var pathCacheHit = false;
28885 var path;
28886 var canvasContext = context;
28887 var translation = {
28888 x: x,
28889 y: y
28890 };
28891 var scale = edge.pstyle('arrow-scale').value;
28892 var size = this.getArrowWidth(edgeWidth, scale);
28893 var shapeImpl = r.arrowShapes[shape];
28894
28895 if (usePaths) {
28896 var cache = r.arrowPathCache = r.arrowPathCache || [];
28897 var key = hashString(shape);
28898 var cachedPath = cache[key];
28899
28900 if (cachedPath != null) {
28901 path = context = cachedPath;
28902 pathCacheHit = true;
28903 } else {
28904 path = context = new Path2D();
28905 cache[key] = path;
28906 }
28907 }
28908
28909 if (!pathCacheHit) {
28910 if (context.beginPath) {
28911 context.beginPath();
28912 }
28913
28914 if (usePaths) {
28915 // store in the path cache with values easily manipulated later
28916 shapeImpl.draw(context, 1, 0, {
28917 x: 0,
28918 y: 0
28919 }, 1);
28920 } else {
28921 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28922 }
28923
28924 if (context.closePath) {
28925 context.closePath();
28926 }
28927 }
28928
28929 context = canvasContext;
28930
28931 if (usePaths) {
28932 // set transform to arrow position/orientation
28933 context.translate(x, y);
28934 context.rotate(angle);
28935 context.scale(size, size);
28936 }
28937
28938 if (fill === 'filled' || fill === 'both') {
28939 if (usePaths) {
28940 context.fill(path);
28941 } else {
28942 context.fill();
28943 }
28944 }
28945
28946 if (fill === 'hollow' || fill === 'both') {
28947 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28948 context.lineJoin = 'miter';
28949
28950 if (usePaths) {
28951 context.stroke(path);
28952 } else {
28953 context.stroke();
28954 }
28955 }
28956
28957 if (usePaths) {
28958 // reset transform by applying inverse
28959 context.scale(1 / size, 1 / size);
28960 context.rotate(-angle);
28961 context.translate(-x, -y);
28962 }
28963};
28964
28965var CRp$3 = {};
28966
28967CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28968 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28969 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
28970 return;
28971 }
28972
28973 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
28974};
28975
28976CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
28977 var r = this;
28978 var pos = node.position();
28979 var nodeX = pos.x;
28980 var nodeY = pos.y;
28981 var styleObj = node.cy().style();
28982 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
28983 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
28984 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
28985 var nodeW = node.width();
28986 var nodeH = node.height();
28987 var paddingX2 = node.padding() * 2;
28988 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28989 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28990 var rs = node._private.rscratch;
28991 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
28992 var shouldClip = clip === 'node';
28993 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
28994 var imgW = img.width || img.cachedW;
28995 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
28996
28997 if (null == imgW || null == imgH) {
28998 document.body.appendChild(img); // eslint-disable-line no-undef
28999
29000 imgW = img.cachedW = img.width || img.offsetWidth;
29001 imgH = img.cachedH = img.height || img.offsetHeight;
29002 document.body.removeChild(img); // eslint-disable-line no-undef
29003 }
29004
29005 var w = imgW;
29006 var h = imgH;
29007
29008 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29009 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29010 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29011 } else {
29012 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29013 }
29014 }
29015
29016 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29017 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29018 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29019 } else {
29020 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29021 }
29022 }
29023
29024 if (w === 0 || h === 0) {
29025 return; // no point in drawing empty image (and chrome is broken in this case)
29026 }
29027
29028 if (fit === 'contain') {
29029 var scale = Math.min(nodeTW / w, nodeTH / h);
29030 w *= scale;
29031 h *= scale;
29032 } else if (fit === 'cover') {
29033 var scale = Math.max(nodeTW / w, nodeTH / h);
29034 w *= scale;
29035 h *= scale;
29036 }
29037
29038 var x = nodeX - nodeTW / 2; // left
29039
29040 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29041 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29042
29043 if (posXUnits === '%') {
29044 x += (nodeTW - w) * posXPfVal;
29045 } else {
29046 x += posXPfVal;
29047 }
29048
29049 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29050 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29051
29052 if (offXUnits === '%') {
29053 x += (nodeTW - w) * offXPfVal;
29054 } else {
29055 x += offXPfVal;
29056 }
29057
29058 var y = nodeY - nodeTH / 2; // top
29059
29060 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29061 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29062
29063 if (posYUnits === '%') {
29064 y += (nodeTH - h) * posYPfVal;
29065 } else {
29066 y += posYPfVal;
29067 }
29068
29069 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29070 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29071
29072 if (offYUnits === '%') {
29073 y += (nodeTH - h) * offYPfVal;
29074 } else {
29075 y += offYPfVal;
29076 }
29077
29078 if (rs.pathCache) {
29079 x -= nodeX;
29080 y -= nodeY;
29081 nodeX = 0;
29082 nodeY = 0;
29083 }
29084
29085 var gAlpha = context.globalAlpha;
29086 context.globalAlpha = imgOpacity;
29087
29088 if (repeat === 'no-repeat') {
29089 if (shouldClip) {
29090 context.save();
29091
29092 if (rs.pathCache) {
29093 context.clip(rs.pathCache);
29094 } else {
29095 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29096 context.clip();
29097 }
29098 }
29099
29100 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29101
29102 if (shouldClip) {
29103 context.restore();
29104 }
29105 } else {
29106 var pattern = context.createPattern(img, repeat);
29107 context.fillStyle = pattern;
29108 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29109 context.translate(x, y);
29110 context.fill();
29111 context.translate(-x, -y);
29112 }
29113
29114 context.globalAlpha = gAlpha;
29115};
29116
29117var CRp$4 = {};
29118
29119CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29120 if (!scale) {
29121 var zoom = ele.cy().zoom();
29122 var pxRatio = this.getPixelRatio();
29123 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29124
29125 scale = Math.pow(2, lvl);
29126 }
29127
29128 var computedSize = ele.pstyle('font-size').pfValue * scale;
29129 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29130
29131 if (computedSize < minSize) {
29132 return false;
29133 }
29134
29135 return true;
29136};
29137
29138CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29139 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29140 var r = this;
29141
29142 if (force == null) {
29143 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29144 return;
29145 }
29146 } else if (force === false) {
29147 return;
29148 }
29149
29150 if (ele.isNode()) {
29151 var label = ele.pstyle('label');
29152
29153 if (!label || !label.value) {
29154 return;
29155 }
29156
29157 var justification = r.getLabelJustification(ele);
29158 context.textAlign = justification;
29159 context.textBaseline = 'bottom';
29160 } else {
29161 var badLine = ele.element()._private.rscratch.badLine;
29162
29163 var _label = ele.pstyle('label');
29164
29165 var srcLabel = ele.pstyle('source-label');
29166 var tgtLabel = ele.pstyle('target-label');
29167
29168 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29169 return;
29170 }
29171
29172 context.textAlign = 'center';
29173 context.textBaseline = 'bottom';
29174 }
29175
29176 var applyRotation = !shiftToOriginWithBb;
29177 var bb;
29178
29179 if (shiftToOriginWithBb) {
29180 bb = shiftToOriginWithBb;
29181 context.translate(-bb.x1, -bb.y1);
29182 }
29183
29184 if (prefix == null) {
29185 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29186
29187 if (ele.isEdge()) {
29188 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29189 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29190 }
29191 } else {
29192 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29193 }
29194
29195 if (shiftToOriginWithBb) {
29196 context.translate(bb.x1, bb.y1);
29197 }
29198};
29199
29200CRp$4.getFontCache = function (context) {
29201 var cache;
29202 this.fontCaches = this.fontCaches || [];
29203
29204 for (var i = 0; i < this.fontCaches.length; i++) {
29205 cache = this.fontCaches[i];
29206
29207 if (cache.context === context) {
29208 return cache;
29209 }
29210 }
29211
29212 cache = {
29213 context: context
29214 };
29215 this.fontCaches.push(cache);
29216 return cache;
29217}; // set up canvas context with font
29218// returns transformed text string
29219
29220
29221CRp$4.setupTextStyle = function (context, ele) {
29222 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29223 // Font style
29224 var labelStyle = ele.pstyle('font-style').strValue;
29225 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29226 var labelFamily = ele.pstyle('font-family').strValue;
29227 var labelWeight = ele.pstyle('font-weight').strValue;
29228 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29229 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29230 var color = ele.pstyle('color').value;
29231 var outlineColor = ele.pstyle('text-outline-color').value;
29232 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29233 context.lineJoin = 'round'; // so text outlines aren't jagged
29234
29235 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29236 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29237}; // TODO ensure re-used
29238
29239
29240function roundRect(ctx, x, y, width, height) {
29241 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29242 ctx.beginPath();
29243 ctx.moveTo(x + radius, y);
29244 ctx.lineTo(x + width - radius, y);
29245 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29246 ctx.lineTo(x + width, y + height - radius);
29247 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29248 ctx.lineTo(x + radius, y + height);
29249 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29250 ctx.lineTo(x, y + radius);
29251 ctx.quadraticCurveTo(x, y, x + radius, y);
29252 ctx.closePath();
29253 ctx.fill();
29254}
29255
29256CRp$4.getTextAngle = function (ele, prefix) {
29257 var theta;
29258 var _p = ele._private;
29259 var rscratch = _p.rscratch;
29260 var pdash = prefix ? prefix + '-' : '';
29261 var rotation = ele.pstyle(pdash + 'text-rotation');
29262 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29263
29264 if (rotation.strValue === 'autorotate') {
29265 theta = ele.isEdge() ? textAngle : 0;
29266 } else if (rotation.strValue === 'none') {
29267 theta = 0;
29268 } else {
29269 theta = rotation.pfValue;
29270 }
29271
29272 return theta;
29273};
29274
29275CRp$4.drawText = function (context, ele, prefix) {
29276 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29277 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29278 var _p = ele._private;
29279 var rscratch = _p.rscratch;
29280 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29281
29282 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29283 return;
29284 } // use 'main' as an alias for the main label (i.e. null prefix)
29285
29286
29287 if (prefix === 'main') {
29288 prefix = null;
29289 }
29290
29291 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29292 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29293 var orgTextX, orgTextY; // used for rotation
29294
29295 var text = this.getLabelText(ele, prefix);
29296
29297 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29298 this.setupTextStyle(context, ele, useEleOpacity);
29299 var pdash = prefix ? prefix + '-' : '';
29300 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29301 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29302 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29303 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29304 var isEdge = ele.isEdge();
29305 var halign = ele.pstyle('text-halign').value;
29306 var valign = ele.pstyle('text-valign').value;
29307
29308 if (isEdge) {
29309 halign = 'center';
29310 valign = 'center';
29311 }
29312
29313 textX += marginX;
29314 textY += marginY;
29315 var theta;
29316
29317 if (!applyRotation) {
29318 theta = 0;
29319 } else {
29320 theta = this.getTextAngle(ele, prefix);
29321 }
29322
29323 if (theta !== 0) {
29324 orgTextX = textX;
29325 orgTextY = textY;
29326 context.translate(orgTextX, orgTextY);
29327 context.rotate(theta);
29328 textX = 0;
29329 textY = 0;
29330 }
29331
29332 switch (valign) {
29333 case 'top':
29334 break;
29335
29336 case 'center':
29337 textY += textH / 2;
29338 break;
29339
29340 case 'bottom':
29341 textY += textH;
29342 break;
29343 }
29344
29345 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29346 var borderOpacity = ele.pstyle('text-border-opacity').value;
29347 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29348 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29349
29350 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29351 var bgX = textX - backgroundPadding;
29352
29353 switch (halign) {
29354 case 'left':
29355 bgX -= textW;
29356 break;
29357
29358 case 'center':
29359 bgX -= textW / 2;
29360 break;
29361 }
29362
29363 var bgY = textY - textH - backgroundPadding;
29364 var bgW = textW + 2 * backgroundPadding;
29365 var bgH = textH + 2 * backgroundPadding;
29366
29367 if (backgroundOpacity > 0) {
29368 var textFill = context.fillStyle;
29369 var textBackgroundColor = ele.pstyle('text-background-color').value;
29370 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29371 var styleShape = ele.pstyle('text-background-shape').strValue;
29372
29373 if (styleShape.indexOf('round') === 0) {
29374 roundRect(context, bgX, bgY, bgW, bgH, 2);
29375 } else {
29376 context.fillRect(bgX, bgY, bgW, bgH);
29377 }
29378
29379 context.fillStyle = textFill;
29380 }
29381
29382 if (textBorderWidth > 0 && borderOpacity > 0) {
29383 var textStroke = context.strokeStyle;
29384 var textLineWidth = context.lineWidth;
29385 var textBorderColor = ele.pstyle('text-border-color').value;
29386 var textBorderStyle = ele.pstyle('text-border-style').value;
29387 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29388 context.lineWidth = textBorderWidth;
29389
29390 if (context.setLineDash) {
29391 // for very outofdate browsers
29392 switch (textBorderStyle) {
29393 case 'dotted':
29394 context.setLineDash([1, 1]);
29395 break;
29396
29397 case 'dashed':
29398 context.setLineDash([4, 2]);
29399 break;
29400
29401 case 'double':
29402 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29403
29404 context.setLineDash([]);
29405 break;
29406
29407 case 'solid':
29408 context.setLineDash([]);
29409 break;
29410 }
29411 }
29412
29413 context.strokeRect(bgX, bgY, bgW, bgH);
29414
29415 if (textBorderStyle === 'double') {
29416 var whiteWidth = textBorderWidth / 2;
29417 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29418 }
29419
29420 if (context.setLineDash) {
29421 // for very outofdate browsers
29422 context.setLineDash([]);
29423 }
29424
29425 context.lineWidth = textLineWidth;
29426 context.strokeStyle = textStroke;
29427 }
29428 }
29429
29430 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29431
29432 if (lineWidth > 0) {
29433 context.lineWidth = lineWidth;
29434 }
29435
29436 if (ele.pstyle('text-wrap').value === 'wrap') {
29437 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29438 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29439 var halfTextW = textW / 2;
29440 var justification = this.getLabelJustification(ele);
29441
29442 if (justification === 'auto') ; else if (halign === 'left') {
29443 // auto justification : right
29444 if (justification === 'left') {
29445 textX += -textW;
29446 } else if (justification === 'center') {
29447 textX += -halfTextW;
29448 } // else same as auto
29449
29450 } else if (halign === 'center') {
29451 // auto justfication : center
29452 if (justification === 'left') {
29453 textX += -halfTextW;
29454 } else if (justification === 'right') {
29455 textX += halfTextW;
29456 } // else same as auto
29457
29458 } else if (halign === 'right') {
29459 // auto justification : left
29460 if (justification === 'center') {
29461 textX += halfTextW;
29462 } else if (justification === 'right') {
29463 textX += textW;
29464 } // else same as auto
29465
29466 }
29467
29468 switch (valign) {
29469 case 'top':
29470 textY -= (lines.length - 1) * lineHeight;
29471 break;
29472
29473 case 'center':
29474 case 'bottom':
29475 textY -= (lines.length - 1) * lineHeight;
29476 break;
29477 }
29478
29479 for (var l = 0; l < lines.length; l++) {
29480 if (lineWidth > 0) {
29481 context.strokeText(lines[l], textX, textY);
29482 }
29483
29484 context.fillText(lines[l], textX, textY);
29485 textY += lineHeight;
29486 }
29487 } else {
29488 if (lineWidth > 0) {
29489 context.strokeText(text, textX, textY);
29490 }
29491
29492 context.fillText(text, textX, textY);
29493 }
29494
29495 if (theta !== 0) {
29496 context.rotate(-theta);
29497 context.translate(-orgTextX, -orgTextY);
29498 }
29499 }
29500};
29501
29502/* global Path2D */
29503var CRp$5 = {};
29504
29505CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29506 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29507 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29508 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29509 var r = this;
29510 var nodeWidth, nodeHeight;
29511 var _p = node._private;
29512 var rs = _p.rscratch;
29513 var pos = node.position();
29514
29515 if (!number(pos.x) || !number(pos.y)) {
29516 return; // can't draw node with undefined position
29517 }
29518
29519 if (shouldDrawOpacity && !node.visible()) {
29520 return;
29521 }
29522
29523 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29524 var usePaths = r.usePaths();
29525 var path;
29526 var pathCacheHit = false;
29527 var padding = node.padding();
29528 nodeWidth = node.width() + 2 * padding;
29529 nodeHeight = node.height() + 2 * padding; //
29530 // setup shift
29531
29532 var bb;
29533
29534 if (shiftToOriginWithBb) {
29535 bb = shiftToOriginWithBb;
29536 context.translate(-bb.x1, -bb.y1);
29537 } //
29538 // load bg image
29539
29540
29541 var bgImgProp = node.pstyle('background-image');
29542 var urls = bgImgProp.value;
29543 var urlDefined = new Array(urls.length);
29544 var image = new Array(urls.length);
29545 var numImages = 0;
29546
29547 for (var i = 0; i < urls.length; i++) {
29548 var url = urls[i];
29549 var defd = urlDefined[i] = url != null && url !== 'none';
29550
29551 if (defd) {
29552 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29553 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29554
29555 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29556 _p.backgroundTimestamp = Date.now();
29557 node.emitAndNotify('background');
29558 });
29559 }
29560 } //
29561 // setup styles
29562
29563
29564 var darkness = node.pstyle('background-blacken').value;
29565 var borderWidth = node.pstyle('border-width').pfValue;
29566 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29567 var borderColor = node.pstyle('border-color').value;
29568 var borderStyle = node.pstyle('border-style').value;
29569 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29570 context.lineJoin = 'miter'; // so borders are square with the node shape
29571
29572 var setupShapeColor = function setupShapeColor() {
29573 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29574 r.eleFillStyle(context, node, bgOpy);
29575 };
29576
29577 var setupBorderColor = function setupBorderColor() {
29578 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29579 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29580 }; //
29581 // setup shape
29582
29583
29584 var styleShape = node.pstyle('shape').strValue;
29585 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29586
29587 if (usePaths) {
29588 context.translate(pos.x, pos.y);
29589 var pathCache = r.nodePathCache = r.nodePathCache || [];
29590 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29591 var cachedPath = pathCache[key];
29592
29593 if (cachedPath != null) {
29594 path = cachedPath;
29595 pathCacheHit = true;
29596 rs.pathCache = path;
29597 } else {
29598 path = new Path2D();
29599 pathCache[key] = rs.pathCache = path;
29600 }
29601 }
29602
29603 var drawShape = function drawShape() {
29604 if (!pathCacheHit) {
29605 var npos = pos;
29606
29607 if (usePaths) {
29608 npos = {
29609 x: 0,
29610 y: 0
29611 };
29612 }
29613
29614 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29615 }
29616
29617 if (usePaths) {
29618 context.fill(path);
29619 } else {
29620 context.fill();
29621 }
29622 };
29623
29624 var drawImages = function drawImages() {
29625 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29626 var prevBging = _p.backgrounding;
29627 var totalCompleted = 0;
29628
29629 for (var _i = 0; _i < image.length; _i++) {
29630 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29631 totalCompleted++;
29632 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29633 }
29634 }
29635
29636 _p.backgrounding = !(totalCompleted === numImages);
29637
29638 if (prevBging !== _p.backgrounding) {
29639 // update style b/c :backgrounding state changed
29640 node.updateStyle(false);
29641 }
29642 };
29643
29644 var drawPie = function drawPie() {
29645 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29646 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29647
29648 if (r.hasPie(node)) {
29649 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29650
29651 if (redrawShape) {
29652 if (!usePaths) {
29653 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29654 }
29655 }
29656 }
29657 };
29658
29659 var darken = function darken() {
29660 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29661 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29662 var c = darkness > 0 ? 0 : 255;
29663
29664 if (darkness !== 0) {
29665 r.colorFillStyle(context, c, c, c, opacity);
29666
29667 if (usePaths) {
29668 context.fill(path);
29669 } else {
29670 context.fill();
29671 }
29672 }
29673 };
29674
29675 var drawBorder = function drawBorder() {
29676 if (borderWidth > 0) {
29677 context.lineWidth = borderWidth;
29678 context.lineCap = 'butt';
29679
29680 if (context.setLineDash) {
29681 // for very outofdate browsers
29682 switch (borderStyle) {
29683 case 'dotted':
29684 context.setLineDash([1, 1]);
29685 break;
29686
29687 case 'dashed':
29688 context.setLineDash([4, 2]);
29689 break;
29690
29691 case 'solid':
29692 case 'double':
29693 context.setLineDash([]);
29694 break;
29695 }
29696 }
29697
29698 if (usePaths) {
29699 context.stroke(path);
29700 } else {
29701 context.stroke();
29702 }
29703
29704 if (borderStyle === 'double') {
29705 context.lineWidth = borderWidth / 3;
29706 var gco = context.globalCompositeOperation;
29707 context.globalCompositeOperation = 'destination-out';
29708
29709 if (usePaths) {
29710 context.stroke(path);
29711 } else {
29712 context.stroke();
29713 }
29714
29715 context.globalCompositeOperation = gco;
29716 } // reset in case we changed the border style
29717
29718
29719 if (context.setLineDash) {
29720 // for very outofdate browsers
29721 context.setLineDash([]);
29722 }
29723 }
29724 };
29725
29726 var drawOverlay = function drawOverlay() {
29727 if (shouldDrawOverlay) {
29728 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29729 }
29730 };
29731
29732 var drawText = function drawText() {
29733 r.drawElementText(context, node, null, drawLabel);
29734 };
29735
29736 var ghost = node.pstyle('ghost').value === 'yes';
29737
29738 if (ghost) {
29739 var gx = node.pstyle('ghost-offset-x').pfValue;
29740 var gy = node.pstyle('ghost-offset-y').pfValue;
29741 var ghostOpacity = node.pstyle('ghost-opacity').value;
29742 var effGhostOpacity = ghostOpacity * eleOpacity;
29743 context.translate(gx, gy);
29744 setupShapeColor(ghostOpacity * bgOpacity);
29745 drawShape();
29746 drawImages(effGhostOpacity);
29747 drawPie(darkness !== 0 || borderWidth !== 0);
29748 darken(effGhostOpacity);
29749 setupBorderColor(ghostOpacity * borderOpacity);
29750 drawBorder();
29751 context.translate(-gx, -gy);
29752 }
29753
29754 setupShapeColor();
29755 drawShape();
29756 drawImages();
29757 drawPie(darkness !== 0 || borderWidth !== 0);
29758 darken();
29759 setupBorderColor();
29760 drawBorder();
29761
29762 if (usePaths) {
29763 context.translate(-pos.x, -pos.y);
29764 }
29765
29766 drawText();
29767 drawOverlay(); //
29768 // clean up shift
29769
29770 if (shiftToOriginWithBb) {
29771 context.translate(bb.x1, bb.y1);
29772 }
29773};
29774
29775CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29776 var r = this;
29777
29778 if (!node.visible()) {
29779 return;
29780 }
29781
29782 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29783 var overlayOpacity = node.pstyle('overlay-opacity').value;
29784 var overlayColor = node.pstyle('overlay-color').value;
29785
29786 if (overlayOpacity > 0) {
29787 pos = pos || node.position();
29788
29789 if (nodeWidth == null || nodeHeight == null) {
29790 var padding = node.padding();
29791 nodeWidth = node.width() + 2 * padding;
29792 nodeHeight = node.height() + 2 * padding;
29793 }
29794
29795 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29796 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29797 context.fill();
29798 }
29799}; // does the node have at least one pie piece?
29800
29801
29802CRp$5.hasPie = function (node) {
29803 node = node[0]; // ensure ele ref
29804
29805 return node._private.hasPie;
29806};
29807
29808CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29809 node = node[0]; // ensure ele ref
29810
29811 pos = pos || node.position();
29812 var cyStyle = node.cy().style();
29813 var pieSize = node.pstyle('pie-size');
29814 var x = pos.x;
29815 var y = pos.y;
29816 var nodeW = node.width();
29817 var nodeH = node.height();
29818 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29819
29820 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29821
29822 var usePaths = this.usePaths();
29823
29824 if (usePaths) {
29825 x = 0;
29826 y = 0;
29827 }
29828
29829 if (pieSize.units === '%') {
29830 radius = radius * pieSize.pfValue;
29831 } else if (pieSize.pfValue !== undefined) {
29832 radius = pieSize.pfValue / 2;
29833 }
29834
29835 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29836 // 1..N
29837 var size = node.pstyle('pie-' + i + '-background-size').value;
29838 var color = node.pstyle('pie-' + i + '-background-color').value;
29839 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29840 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29841 // percent can't push beyond 1
29842
29843 if (percent + lastPercent > 1) {
29844 percent = 1 - lastPercent;
29845 }
29846
29847 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29848
29849 var angleDelta = 2 * Math.PI * percent;
29850 var angleEnd = angleStart + angleDelta; // ignore if
29851 // - zero size
29852 // - we're already beyond the full circle
29853 // - adding the current slice would go beyond the full circle
29854
29855 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29856 continue;
29857 }
29858
29859 context.beginPath();
29860 context.moveTo(x, y);
29861 context.arc(x, y, radius, angleStart, angleEnd);
29862 context.closePath();
29863 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29864 context.fill();
29865 lastPercent += percent;
29866 }
29867};
29868
29869var CRp$6 = {};
29870var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29871
29872CRp$6.getPixelRatio = function () {
29873 var context = this.data.contexts[0];
29874
29875 if (this.forcedPixelRatio != null) {
29876 return this.forcedPixelRatio;
29877 }
29878
29879 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29880 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29881};
29882
29883CRp$6.paintCache = function (context) {
29884 var caches = this.paintCaches = this.paintCaches || [];
29885 var needToCreateCache = true;
29886 var cache;
29887
29888 for (var i = 0; i < caches.length; i++) {
29889 cache = caches[i];
29890
29891 if (cache.context === context) {
29892 needToCreateCache = false;
29893 break;
29894 }
29895 }
29896
29897 if (needToCreateCache) {
29898 cache = {
29899 context: context
29900 };
29901 caches.push(cache);
29902 }
29903
29904 return cache;
29905};
29906
29907CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29908 var gradientStyle;
29909 var usePaths = this.usePaths();
29910 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29911 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29912
29913 if (fill === 'radial-gradient') {
29914 if (ele.isEdge()) {
29915 var start = ele.sourceEndpoint(),
29916 end = ele.targetEndpoint(),
29917 mid = ele.midpoint();
29918 var d1 = dist(start, mid);
29919 var d2 = dist(end, mid);
29920 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29921 } else {
29922 var pos = usePaths ? {
29923 x: 0,
29924 y: 0
29925 } : ele.position(),
29926 width = ele.paddedWidth(),
29927 height = ele.paddedHeight();
29928 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29929 }
29930 } else {
29931 if (ele.isEdge()) {
29932 var _start = ele.sourceEndpoint(),
29933 _end = ele.targetEndpoint();
29934
29935 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29936 } else {
29937 var _pos = usePaths ? {
29938 x: 0,
29939 y: 0
29940 } : ele.position(),
29941 _width = ele.paddedWidth(),
29942 _height = ele.paddedHeight(),
29943 halfWidth = _width / 2,
29944 halfHeight = _height / 2;
29945
29946 var direction = ele.pstyle('background-gradient-direction').value;
29947
29948 switch (direction) {
29949 case 'to-bottom':
29950 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
29951 break;
29952
29953 case 'to-top':
29954 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
29955 break;
29956
29957 case 'to-left':
29958 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
29959 break;
29960
29961 case 'to-right':
29962 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
29963 break;
29964
29965 case 'to-bottom-right':
29966 case 'to-right-bottom':
29967 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
29968 break;
29969
29970 case 'to-top-right':
29971 case 'to-right-top':
29972 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
29973 break;
29974
29975 case 'to-bottom-left':
29976 case 'to-left-bottom':
29977 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
29978 break;
29979
29980 case 'to-top-left':
29981 case 'to-left-top':
29982 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
29983 break;
29984 }
29985 }
29986 }
29987
29988 if (!gradientStyle) return null; // invalid gradient style
29989
29990 var hasPositions = positions.length === colors.length;
29991 var length = colors.length;
29992
29993 for (var i = 0; i < length; i++) {
29994 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
29995 }
29996
29997 return gradientStyle;
29998};
29999
30000CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30001 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30002 if (!gradientStyle) return null; // error
30003
30004 context.fillStyle = gradientStyle;
30005};
30006
30007CRp$6.colorFillStyle = function (context, r, g, b, a) {
30008 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30009 // var cache = this.paintCache(context);
30010 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30011 // if( cache.fillStyle !== fillStyle ){
30012 // context.fillStyle = cache.fillStyle = fillStyle;
30013 // }
30014};
30015
30016CRp$6.eleFillStyle = function (context, ele, opacity) {
30017 var backgroundFill = ele.pstyle('background-fill').value;
30018
30019 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30020 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30021 } else {
30022 var backgroundColor = ele.pstyle('background-color').value;
30023 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30024 }
30025};
30026
30027CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30028 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30029 if (!gradientStyle) return null; // error
30030
30031 context.strokeStyle = gradientStyle;
30032};
30033
30034CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30035 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30036 // var cache = this.paintCache(context);
30037 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30038 // if( cache.strokeStyle !== strokeStyle ){
30039 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30040 // }
30041};
30042
30043CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30044 var lineFill = ele.pstyle('line-fill').value;
30045
30046 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30047 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30048 } else {
30049 var lineColor = ele.pstyle('line-color').value;
30050 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30051 }
30052}; // Resize canvas
30053
30054
30055CRp$6.matchCanvasSize = function (container) {
30056 var r = this;
30057 var data = r.data;
30058 var bb = r.findContainerClientCoords();
30059 var width = bb[2];
30060 var height = bb[3];
30061 var pixelRatio = r.getPixelRatio();
30062 var mbPxRatio = r.motionBlurPxRatio;
30063
30064 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30065 pixelRatio = mbPxRatio;
30066 }
30067
30068 var canvasWidth = width * pixelRatio;
30069 var canvasHeight = height * pixelRatio;
30070 var canvas;
30071
30072 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30073 return; // save cycles if same
30074 }
30075
30076 r.fontCaches = null; // resizing resets the style
30077
30078 var canvasContainer = data.canvasContainer;
30079 canvasContainer.style.width = width + 'px';
30080 canvasContainer.style.height = height + 'px';
30081
30082 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30083 canvas = data.canvases[i];
30084 canvas.width = canvasWidth;
30085 canvas.height = canvasHeight;
30086 canvas.style.width = width + 'px';
30087 canvas.style.height = height + 'px';
30088 }
30089
30090 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30091 canvas = data.bufferCanvases[i];
30092 canvas.width = canvasWidth;
30093 canvas.height = canvasHeight;
30094 canvas.style.width = width + 'px';
30095 canvas.style.height = height + 'px';
30096 }
30097
30098 r.textureMult = 1;
30099
30100 if (pixelRatio <= 1) {
30101 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30102 r.textureMult = 2;
30103 canvas.width = canvasWidth * r.textureMult;
30104 canvas.height = canvasHeight * r.textureMult;
30105 }
30106
30107 r.canvasWidth = canvasWidth;
30108 r.canvasHeight = canvasHeight;
30109};
30110
30111CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30112 this.render({
30113 forcedContext: cxt,
30114 forcedZoom: zoom,
30115 forcedPan: pan,
30116 drawAllLayers: true,
30117 forcedPxRatio: pxRatio
30118 });
30119};
30120
30121CRp$6.render = function (options) {
30122 options = options || staticEmptyObject();
30123 var forcedContext = options.forcedContext;
30124 var drawAllLayers = options.drawAllLayers;
30125 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30126 var forcedZoom = options.forcedZoom;
30127 var forcedPan = options.forcedPan;
30128 var r = this;
30129 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30130 var cy = r.cy;
30131 var data = r.data;
30132 var needDraw = data.canvasNeedsRedraw;
30133 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30134 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30135 var mbPxRatio = r.motionBlurPxRatio;
30136 var hasCompoundNodes = cy.hasCompoundNodes();
30137 var inNodeDragGesture = r.hoverData.draggingEles;
30138 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30139 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30140 var motionBlurFadeEffect = motionBlur;
30141
30142 if (!forcedContext) {
30143 if (r.prevPxRatio !== pixelRatio) {
30144 r.invalidateContainerClientCoordsCache();
30145 r.matchCanvasSize(r.container);
30146 r.redrawHint('eles', true);
30147 r.redrawHint('drag', true);
30148 }
30149
30150 r.prevPxRatio = pixelRatio;
30151 }
30152
30153 if (!forcedContext && r.motionBlurTimeout) {
30154 clearTimeout(r.motionBlurTimeout);
30155 }
30156
30157 if (motionBlur) {
30158 if (r.mbFrames == null) {
30159 r.mbFrames = 0;
30160 }
30161
30162 r.mbFrames++;
30163
30164 if (r.mbFrames < 3) {
30165 // need several frames before even high quality motionblur
30166 motionBlurFadeEffect = false;
30167 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30168
30169
30170 if (r.mbFrames > r.minMbLowQualFrames) {
30171 //r.fullQualityMb = false;
30172 r.motionBlurPxRatio = r.mbPxRBlurry;
30173 }
30174 }
30175
30176 if (r.clearingMotionBlur) {
30177 r.motionBlurPxRatio = 1;
30178 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30179 // because a rogue async texture frame would clear needDraw
30180
30181
30182 if (r.textureDrawLastFrame && !textureDraw) {
30183 needDraw[r.NODE] = true;
30184 needDraw[r.SELECT_BOX] = true;
30185 }
30186
30187 var style = cy.style();
30188 var zoom = cy.zoom();
30189 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30190 var pan = cy.pan();
30191 var effectivePan = {
30192 x: pan.x,
30193 y: pan.y
30194 };
30195 var vp = {
30196 zoom: zoom,
30197 pan: {
30198 x: pan.x,
30199 y: pan.y
30200 }
30201 };
30202 var prevVp = r.prevViewport;
30203 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)
30204
30205 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30206 r.motionBlurPxRatio = 1;
30207 }
30208
30209 if (forcedPan) {
30210 effectivePan = forcedPan;
30211 } // apply pixel ratio
30212
30213
30214 effectiveZoom *= pixelRatio;
30215 effectivePan.x *= pixelRatio;
30216 effectivePan.y *= pixelRatio;
30217 var eles = r.getCachedZSortedEles();
30218
30219 function mbclear(context, x, y, w, h) {
30220 var gco = context.globalCompositeOperation;
30221 context.globalCompositeOperation = 'destination-out';
30222 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30223 context.fillRect(x, y, w, h);
30224 context.globalCompositeOperation = gco;
30225 }
30226
30227 function setContextTransform(context, clear) {
30228 var ePan, eZoom, w, h;
30229
30230 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30231 ePan = {
30232 x: pan.x * mbPxRatio,
30233 y: pan.y * mbPxRatio
30234 };
30235 eZoom = zoom * mbPxRatio;
30236 w = r.canvasWidth * mbPxRatio;
30237 h = r.canvasHeight * mbPxRatio;
30238 } else {
30239 ePan = effectivePan;
30240 eZoom = effectiveZoom;
30241 w = r.canvasWidth;
30242 h = r.canvasHeight;
30243 }
30244
30245 context.setTransform(1, 0, 0, 1, 0, 0);
30246
30247 if (clear === 'motionBlur') {
30248 mbclear(context, 0, 0, w, h);
30249 } else if (!forcedContext && (clear === undefined || clear)) {
30250 context.clearRect(0, 0, w, h);
30251 }
30252
30253 if (!drawAllLayers) {
30254 context.translate(ePan.x, ePan.y);
30255 context.scale(eZoom, eZoom);
30256 }
30257
30258 if (forcedPan) {
30259 context.translate(forcedPan.x, forcedPan.y);
30260 }
30261
30262 if (forcedZoom) {
30263 context.scale(forcedZoom, forcedZoom);
30264 }
30265 }
30266
30267 if (!textureDraw) {
30268 r.textureDrawLastFrame = false;
30269 }
30270
30271 if (textureDraw) {
30272 r.textureDrawLastFrame = true;
30273
30274 if (!r.textureCache) {
30275 r.textureCache = {};
30276 r.textureCache.bb = cy.mutableElements().boundingBox();
30277 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30278 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30279 cxt.setTransform(1, 0, 0, 1, 0, 0);
30280 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30281 r.render({
30282 forcedContext: cxt,
30283 drawOnlyNodeLayer: true,
30284 forcedPxRatio: pixelRatio * r.textureMult
30285 });
30286 var vp = r.textureCache.viewport = {
30287 zoom: cy.zoom(),
30288 pan: cy.pan(),
30289 width: r.canvasWidth,
30290 height: r.canvasHeight
30291 };
30292 vp.mpan = {
30293 x: (0 - vp.pan.x) / vp.zoom,
30294 y: (0 - vp.pan.y) / vp.zoom
30295 };
30296 }
30297
30298 needDraw[r.DRAG] = false;
30299 needDraw[r.NODE] = false;
30300 var context = data.contexts[r.NODE];
30301 var texture = r.textureCache.texture;
30302 var vp = r.textureCache.viewport;
30303 context.setTransform(1, 0, 0, 1, 0, 0);
30304
30305 if (motionBlur) {
30306 mbclear(context, 0, 0, vp.width, vp.height);
30307 } else {
30308 context.clearRect(0, 0, vp.width, vp.height);
30309 }
30310
30311 var outsideBgColor = style.core('outside-texture-bg-color').value;
30312 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30313 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30314 context.fillRect(0, 0, vp.width, vp.height);
30315 var zoom = cy.zoom();
30316 setContextTransform(context, false);
30317 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30318 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30319 } else if (r.textureOnViewport && !forcedContext) {
30320 // clear the cache since we don't need it
30321 r.textureCache = null;
30322 }
30323
30324 var extent = cy.extent();
30325 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30326 var hideEdges = r.hideEdgesOnViewport && vpManip;
30327 var needMbClear = [];
30328 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30329
30330 if (needMbClear[r.NODE]) {
30331 r.clearedForMotionBlur[r.NODE] = true;
30332 }
30333
30334 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30335
30336 if (needMbClear[r.DRAG]) {
30337 r.clearedForMotionBlur[r.DRAG] = true;
30338 }
30339
30340 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30341 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30342 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30343 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30344 setContextTransform(context, clear);
30345
30346 if (hideEdges) {
30347 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30348 } else {
30349 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30350 }
30351
30352 if (r.debug) {
30353 r.drawDebugPoints(context, eles.nondrag);
30354 }
30355
30356 if (!drawAllLayers && !motionBlur) {
30357 needDraw[r.NODE] = false;
30358 }
30359 }
30360
30361 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30362 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30363 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30364 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30365
30366 if (hideEdges) {
30367 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30368 } else {
30369 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30370 }
30371
30372 if (r.debug) {
30373 r.drawDebugPoints(context, eles.drag);
30374 }
30375
30376 if (!drawAllLayers && !motionBlur) {
30377 needDraw[r.DRAG] = false;
30378 }
30379 }
30380
30381 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30382 var context = forcedContext || data.contexts[r.SELECT_BOX];
30383 setContextTransform(context);
30384
30385 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30386 var zoom = r.cy.zoom();
30387 var borderWidth = style.core('selection-box-border-width').value / zoom;
30388 context.lineWidth = borderWidth;
30389 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 + ')';
30390 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30391
30392 if (borderWidth > 0) {
30393 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 + ')';
30394 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30395 }
30396 }
30397
30398 if (data.bgActivePosistion && !r.hoverData.selecting) {
30399 var zoom = r.cy.zoom();
30400 var pos = data.bgActivePosistion;
30401 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 + ')';
30402 context.beginPath();
30403 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30404 context.fill();
30405 }
30406
30407 var timeToRender = r.lastRedrawTime;
30408
30409 if (r.showFps && timeToRender) {
30410 timeToRender = Math.round(timeToRender);
30411 var fps = Math.round(1000 / timeToRender);
30412 context.setTransform(1, 0, 0, 1, 0, 0);
30413 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30414 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30415 context.lineWidth = 1;
30416 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30417 var maxFps = 60;
30418 context.strokeRect(0, 30, 250, 20);
30419 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30420 }
30421
30422 if (!drawAllLayers) {
30423 needDraw[r.SELECT_BOX] = false;
30424 }
30425 } // motionblur: blit rendered blurry frames
30426
30427
30428 if (motionBlur && mbPxRatio !== 1) {
30429 var cxtNode = data.contexts[r.NODE];
30430 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30431 var cxtDrag = data.contexts[r.DRAG];
30432 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30433
30434 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30435 cxt.setTransform(1, 0, 0, 1, 0, 0);
30436
30437 if (needClear || !motionBlurFadeEffect) {
30438 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30439 } else {
30440 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30441 }
30442
30443 var pxr = mbPxRatio;
30444 cxt.drawImage(txt, // img
30445 0, 0, // sx, sy
30446 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30447 0, 0, // x, y
30448 r.canvasWidth, r.canvasHeight // w, h
30449 );
30450 };
30451
30452 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30453 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30454 needDraw[r.NODE] = false;
30455 }
30456
30457 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30458 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30459 needDraw[r.DRAG] = false;
30460 }
30461 }
30462
30463 r.prevViewport = vp;
30464
30465 if (r.clearingMotionBlur) {
30466 r.clearingMotionBlur = false;
30467 r.motionBlurCleared = true;
30468 r.motionBlur = true;
30469 }
30470
30471 if (motionBlur) {
30472 r.motionBlurTimeout = setTimeout(function () {
30473 r.motionBlurTimeout = null;
30474 r.clearedForMotionBlur[r.NODE] = false;
30475 r.clearedForMotionBlur[r.DRAG] = false;
30476 r.motionBlur = false;
30477 r.clearingMotionBlur = !textureDraw;
30478 r.mbFrames = 0;
30479 needDraw[r.NODE] = true;
30480 needDraw[r.DRAG] = true;
30481 r.redraw();
30482 }, motionBlurDelay);
30483 }
30484
30485 if (!forcedContext) {
30486 cy.emit('render');
30487 }
30488};
30489
30490var CRp$7 = {}; // @O Polygon drawing
30491
30492CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30493 var halfW = width / 2;
30494 var halfH = height / 2;
30495
30496 if (context.beginPath) {
30497 context.beginPath();
30498 }
30499
30500 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30501
30502 for (var i = 1; i < points.length / 2; i++) {
30503 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30504 }
30505
30506 context.closePath();
30507};
30508
30509CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30510 var halfW = width / 2;
30511 var halfH = height / 2;
30512 var cornerRadius = getRoundPolygonRadius(width, height);
30513
30514 if (context.beginPath) {
30515 context.beginPath();
30516 }
30517
30518 for (var _i = 0; _i < points.length / 4; _i++) {
30519 var sourceUv = void 0,
30520 destUv = void 0;
30521
30522 if (_i === 0) {
30523 sourceUv = points.length - 2;
30524 } else {
30525 sourceUv = _i * 4 - 2;
30526 }
30527
30528 destUv = _i * 4 + 2;
30529 var px = x + halfW * points[_i * 4];
30530 var py = y + halfH * points[_i * 4 + 1];
30531 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30532 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30533 var cp0x = px - offset * points[sourceUv];
30534 var cp0y = py - offset * points[sourceUv + 1];
30535 var cp1x = px + offset * points[destUv];
30536 var cp1y = py + offset * points[destUv + 1];
30537
30538 if (_i === 0) {
30539 context.moveTo(cp0x, cp0y);
30540 } else {
30541 context.lineTo(cp0x, cp0y);
30542 }
30543
30544 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30545 }
30546
30547 context.closePath();
30548}; // Round rectangle drawing
30549
30550
30551CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30552 var halfWidth = width / 2;
30553 var halfHeight = height / 2;
30554 var cornerRadius = getRoundRectangleRadius(width, height);
30555
30556 if (context.beginPath) {
30557 context.beginPath();
30558 } // Start at top middle
30559
30560
30561 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30562
30563 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30564
30565 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30566
30567 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30568
30569 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30570
30571 context.lineTo(x, y - halfHeight);
30572 context.closePath();
30573};
30574
30575CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30576 var halfWidth = width / 2;
30577 var halfHeight = height / 2;
30578 var cornerRadius = getRoundRectangleRadius(width, height);
30579
30580 if (context.beginPath) {
30581 context.beginPath();
30582 } // Start at top middle
30583
30584
30585 context.moveTo(x, y - halfHeight);
30586 context.lineTo(x + halfWidth, y - halfHeight);
30587 context.lineTo(x + halfWidth, y);
30588 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30589 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30590 context.lineTo(x - halfWidth, y - halfHeight);
30591 context.lineTo(x, y - halfHeight);
30592 context.closePath();
30593};
30594
30595CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30596 var halfWidth = width / 2;
30597 var halfHeight = height / 2;
30598 var cornerLength = getCutRectangleCornerLength();
30599
30600 if (context.beginPath) {
30601 context.beginPath();
30602 }
30603
30604 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30605 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30606 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30607 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30608 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30609 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30610 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30611 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30612 context.closePath();
30613};
30614
30615CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30616 var halfWidth = width / 2;
30617 var halfHeight = height / 2;
30618 var xBegin = x - halfWidth;
30619 var xEnd = x + halfWidth;
30620 var yBegin = y - halfHeight;
30621 var yEnd = y + halfHeight;
30622 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30623 var wOffset = barrelCurveConstants.widthOffset;
30624 var hOffset = barrelCurveConstants.heightOffset;
30625 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30626
30627 if (context.beginPath) {
30628 context.beginPath();
30629 }
30630
30631 context.moveTo(xBegin, yBegin + hOffset);
30632 context.lineTo(xBegin, yEnd - hOffset);
30633 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30634 context.lineTo(xEnd - wOffset, yEnd);
30635 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30636 context.lineTo(xEnd, yBegin + hOffset);
30637 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30638 context.lineTo(xBegin + wOffset, yBegin);
30639 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30640 context.closePath();
30641};
30642
30643var sin0 = Math.sin(0);
30644var cos0 = Math.cos(0);
30645var sin = {};
30646var cos = {};
30647var ellipseStepSize = Math.PI / 40;
30648
30649for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30650 sin[i] = Math.sin(i);
30651 cos[i] = Math.cos(i);
30652}
30653
30654CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30655 if (context.beginPath) {
30656 context.beginPath();
30657 }
30658
30659 if (context.ellipse) {
30660 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30661 } else {
30662 var xPos, yPos;
30663 var rw = width / 2;
30664 var rh = height / 2;
30665
30666 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30667 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30668 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30669
30670 if (i === 0) {
30671 context.moveTo(xPos, yPos);
30672 } else {
30673 context.lineTo(xPos, yPos);
30674 }
30675 }
30676 }
30677
30678 context.closePath();
30679};
30680
30681/* global atob, ArrayBuffer, Uint8Array, Blob */
30682var CRp$8 = {};
30683
30684CRp$8.createBuffer = function (w, h) {
30685 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30686
30687 buffer.width = w;
30688 buffer.height = h;
30689 return [buffer, buffer.getContext('2d')];
30690};
30691
30692CRp$8.bufferCanvasImage = function (options) {
30693 var cy = this.cy;
30694 var eles = cy.mutableElements();
30695 var bb = eles.boundingBox();
30696 var ctrRect = this.findContainerClientCoords();
30697 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30698 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30699 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30700 var pxRatio = this.getPixelRatio();
30701 var scale = 1;
30702
30703 if (options.scale !== undefined) {
30704 width *= options.scale;
30705 height *= options.scale;
30706 scale = options.scale;
30707 } else if (specdMaxDims) {
30708 var maxScaleW = Infinity;
30709 var maxScaleH = Infinity;
30710
30711 if (number(options.maxWidth)) {
30712 maxScaleW = scale * options.maxWidth / width;
30713 }
30714
30715 if (number(options.maxHeight)) {
30716 maxScaleH = scale * options.maxHeight / height;
30717 }
30718
30719 scale = Math.min(maxScaleW, maxScaleH);
30720 width *= scale;
30721 height *= scale;
30722 }
30723
30724 if (!specdMaxDims) {
30725 width *= pxRatio;
30726 height *= pxRatio;
30727 scale *= pxRatio;
30728 }
30729
30730 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30731
30732 buffCanvas.width = width;
30733 buffCanvas.height = height;
30734 buffCanvas.style.width = width + 'px';
30735 buffCanvas.style.height = height + 'px';
30736 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30737
30738 if (width > 0 && height > 0) {
30739 buffCxt.clearRect(0, 0, width, height);
30740 buffCxt.globalCompositeOperation = 'source-over';
30741 var zsortedEles = this.getCachedZSortedEles();
30742
30743 if (options.full) {
30744 // draw the full bounds of the graph
30745 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30746 buffCxt.scale(scale, scale);
30747 this.drawElements(buffCxt, zsortedEles);
30748 buffCxt.scale(1 / scale, 1 / scale);
30749 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30750 } else {
30751 // draw the current view
30752 var pan = cy.pan();
30753 var translation = {
30754 x: pan.x * scale,
30755 y: pan.y * scale
30756 };
30757 scale *= cy.zoom();
30758 buffCxt.translate(translation.x, translation.y);
30759 buffCxt.scale(scale, scale);
30760 this.drawElements(buffCxt, zsortedEles);
30761 buffCxt.scale(1 / scale, 1 / scale);
30762 buffCxt.translate(-translation.x, -translation.y);
30763 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30764
30765
30766 if (options.bg) {
30767 buffCxt.globalCompositeOperation = 'destination-over';
30768 buffCxt.fillStyle = options.bg;
30769 buffCxt.rect(0, 0, width, height);
30770 buffCxt.fill();
30771 }
30772 }
30773
30774 return buffCanvas;
30775};
30776
30777function b64ToBlob(b64, mimeType) {
30778 var bytes = atob(b64);
30779 var buff = new ArrayBuffer(bytes.length);
30780 var buffUint8 = new Uint8Array(buff);
30781
30782 for (var i = 0; i < bytes.length; i++) {
30783 buffUint8[i] = bytes.charCodeAt(i);
30784 }
30785
30786 return new Blob([buff], {
30787 type: mimeType
30788 });
30789}
30790
30791function b64UriToB64(b64uri) {
30792 var i = b64uri.indexOf(',');
30793 return b64uri.substr(i + 1);
30794}
30795
30796function output(options, canvas, mimeType) {
30797 var getB64Uri = function getB64Uri() {
30798 return canvas.toDataURL(mimeType, options.quality);
30799 };
30800
30801 switch (options.output) {
30802 case 'blob-promise':
30803 return new Promise$1(function (resolve, reject) {
30804 try {
30805 canvas.toBlob(function (blob) {
30806 if (blob != null) {
30807 resolve(blob);
30808 } else {
30809 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30810 }
30811 }, mimeType, options.quality);
30812 } catch (err) {
30813 reject(err);
30814 }
30815 });
30816
30817 case 'blob':
30818 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30819
30820 case 'base64':
30821 return b64UriToB64(getB64Uri());
30822
30823 case 'base64uri':
30824 default:
30825 return getB64Uri();
30826 }
30827}
30828
30829CRp$8.png = function (options) {
30830 return output(options, this.bufferCanvasImage(options), 'image/png');
30831};
30832
30833CRp$8.jpg = function (options) {
30834 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30835};
30836
30837var CRp$9 = {};
30838
30839CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30840 switch (name) {
30841 case 'ellipse':
30842 return this.drawEllipsePath(context, centerX, centerY, width, height);
30843
30844 case 'polygon':
30845 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30846
30847 case 'round-polygon':
30848 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30849
30850 case 'roundrectangle':
30851 case 'round-rectangle':
30852 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30853
30854 case 'cutrectangle':
30855 case 'cut-rectangle':
30856 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30857
30858 case 'bottomroundrectangle':
30859 case 'bottom-round-rectangle':
30860 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30861
30862 case 'barrel':
30863 return this.drawBarrelPath(context, centerX, centerY, width, height);
30864 }
30865};
30866
30867var CR = CanvasRenderer;
30868var CRp$a = CanvasRenderer.prototype;
30869CRp$a.CANVAS_LAYERS = 3; //
30870
30871CRp$a.SELECT_BOX = 0;
30872CRp$a.DRAG = 1;
30873CRp$a.NODE = 2;
30874CRp$a.BUFFER_COUNT = 3; //
30875
30876CRp$a.TEXTURE_BUFFER = 0;
30877CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30878CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30879
30880function CanvasRenderer(options) {
30881 var r = this;
30882 r.data = {
30883 canvases: new Array(CRp$a.CANVAS_LAYERS),
30884 contexts: new Array(CRp$a.CANVAS_LAYERS),
30885 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30886 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30887 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30888 };
30889 var tapHlOffAttr = '-webkit-tap-highlight-color';
30890 var tapHlOffStyle = 'rgba(0,0,0,0)';
30891 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30892
30893 var containerStyle = r.data.canvasContainer.style;
30894 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30895 containerStyle.position = 'relative';
30896 containerStyle.zIndex = '0';
30897 containerStyle.overflow = 'hidden';
30898 var container = options.cy.container();
30899 container.appendChild(r.data.canvasContainer);
30900 container.style[tapHlOffAttr] = tapHlOffStyle;
30901 var styleMap = {
30902 '-webkit-user-select': 'none',
30903 '-moz-user-select': '-moz-none',
30904 'user-select': 'none',
30905 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30906 'outline-style': 'none'
30907 };
30908
30909 if (ms()) {
30910 styleMap['-ms-touch-action'] = 'none';
30911 styleMap['touch-action'] = 'none';
30912 }
30913
30914 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30915 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30916
30917 r.data.contexts[i] = canvas.getContext('2d');
30918 Object.keys(styleMap).forEach(function (k) {
30919 canvas.style[k] = styleMap[k];
30920 });
30921 canvas.style.position = 'absolute';
30922 canvas.setAttribute('data-id', 'layer' + i);
30923 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30924 r.data.canvasContainer.appendChild(canvas);
30925 r.data.canvasNeedsRedraw[i] = false;
30926 }
30927
30928 r.data.topCanvas = r.data.canvases[0];
30929 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30930 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30931 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30932
30933 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30934 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30935
30936 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30937 r.data.bufferCanvases[i].style.position = 'absolute';
30938 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30939 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30940 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30941 }
30942
30943 r.pathsEnabled = true;
30944 var emptyBb = makeBoundingBox();
30945
30946 var getBoxCenter = function getBoxCenter(bb) {
30947 return {
30948 x: (bb.x1 + bb.x2) / 2,
30949 y: (bb.y1 + bb.y2) / 2
30950 };
30951 };
30952
30953 var getCenterOffset = function getCenterOffset(bb) {
30954 return {
30955 x: -bb.w / 2,
30956 y: -bb.h / 2
30957 };
30958 };
30959
30960 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
30961 var _p = ele[0]._private;
30962 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
30963 return !same;
30964 };
30965
30966 var getStyleKey = function getStyleKey(ele) {
30967 return ele[0]._private.nodeKey;
30968 };
30969
30970 var getLabelKey = function getLabelKey(ele) {
30971 return ele[0]._private.labelStyleKey;
30972 };
30973
30974 var getSourceLabelKey = function getSourceLabelKey(ele) {
30975 return ele[0]._private.sourceLabelStyleKey;
30976 };
30977
30978 var getTargetLabelKey = function getTargetLabelKey(ele) {
30979 return ele[0]._private.targetLabelStyleKey;
30980 };
30981
30982 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
30983 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
30984 };
30985
30986 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30987 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
30988 };
30989
30990 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30991 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
30992 };
30993
30994 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30995 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
30996 };
30997
30998 var getElementBox = function getElementBox(ele) {
30999 ele.boundingBox();
31000 return ele[0]._private.bodyBounds;
31001 };
31002
31003 var getLabelBox = function getLabelBox(ele) {
31004 ele.boundingBox();
31005 return ele[0]._private.labelBounds.main || emptyBb;
31006 };
31007
31008 var getSourceLabelBox = function getSourceLabelBox(ele) {
31009 ele.boundingBox();
31010 return ele[0]._private.labelBounds.source || emptyBb;
31011 };
31012
31013 var getTargetLabelBox = function getTargetLabelBox(ele) {
31014 ele.boundingBox();
31015 return ele[0]._private.labelBounds.target || emptyBb;
31016 };
31017
31018 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31019 return scaledLabelShown;
31020 };
31021
31022 var getElementRotationPoint = function getElementRotationPoint(ele) {
31023 return getBoxCenter(getElementBox(ele));
31024 };
31025
31026 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31027 var pre = prefix ? prefix + '-' : '';
31028 return {
31029 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31030 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31031 };
31032 };
31033
31034 var getRsPt = function getRsPt(ele, x, y) {
31035 var rs = ele[0]._private.rscratch;
31036 return {
31037 x: rs[x],
31038 y: rs[y]
31039 };
31040 };
31041
31042 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31043 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31044 };
31045
31046 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31047 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31048 };
31049
31050 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31051 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31052 };
31053
31054 var getElementRotationOffset = function getElementRotationOffset(ele) {
31055 return getCenterOffset(getElementBox(ele));
31056 };
31057
31058 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31059 return getCenterOffset(getSourceLabelBox(ele));
31060 };
31061
31062 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31063 return getCenterOffset(getTargetLabelBox(ele));
31064 };
31065
31066 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31067 var bb = getLabelBox(ele);
31068 var p = getCenterOffset(getLabelBox(ele));
31069
31070 if (ele.isNode()) {
31071 switch (ele.pstyle('text-halign').value) {
31072 case 'left':
31073 p.x = -bb.w;
31074 break;
31075
31076 case 'right':
31077 p.x = 0;
31078 break;
31079 }
31080
31081 switch (ele.pstyle('text-valign').value) {
31082 case 'top':
31083 p.y = -bb.h;
31084 break;
31085
31086 case 'bottom':
31087 p.y = 0;
31088 break;
31089 }
31090 }
31091
31092 return p;
31093 };
31094
31095 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31096 getKey: getStyleKey,
31097 doesEleInvalidateKey: backgroundTimestampHasChanged,
31098 drawElement: drawElement,
31099 getBoundingBox: getElementBox,
31100 getRotationPoint: getElementRotationPoint,
31101 getRotationOffset: getElementRotationOffset,
31102 allowEdgeTxrCaching: false,
31103 allowParentTxrCaching: false
31104 });
31105 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31106 getKey: getLabelKey,
31107 drawElement: drawLabel,
31108 getBoundingBox: getLabelBox,
31109 getRotationPoint: getLabelRotationPoint,
31110 getRotationOffset: getLabelRotationOffset,
31111 isVisible: isLabelVisibleAtScale
31112 });
31113 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31114 getKey: getSourceLabelKey,
31115 drawElement: drawSourceLabel,
31116 getBoundingBox: getSourceLabelBox,
31117 getRotationPoint: getSourceLabelRotationPoint,
31118 getRotationOffset: getSourceLabelRotationOffset,
31119 isVisible: isLabelVisibleAtScale
31120 });
31121 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31122 getKey: getTargetLabelKey,
31123 drawElement: drawTargetLabel,
31124 getBoundingBox: getTargetLabelBox,
31125 getRotationPoint: getTargetLabelRotationPoint,
31126 getRotationOffset: getTargetLabelRotationOffset,
31127 isVisible: isLabelVisibleAtScale
31128 });
31129 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31130 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31131 // each cache should check for sub-key diff to see that the update affects that cache particularly
31132 eleTxrCache.invalidateElements(eles);
31133 lblTxrCache.invalidateElements(eles);
31134 slbTxrCache.invalidateElements(eles);
31135 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31136
31137 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31138
31139 for (var _i = 0; _i < eles.length; _i++) {
31140 var _p = eles[_i]._private;
31141 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31142 }
31143 });
31144
31145 var refineInLayers = function refineInLayers(reqs) {
31146 for (var i = 0; i < reqs.length; i++) {
31147 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31148 }
31149 };
31150
31151 eleTxrCache.onDequeue(refineInLayers);
31152 lblTxrCache.onDequeue(refineInLayers);
31153 slbTxrCache.onDequeue(refineInLayers);
31154 tlbTxrCache.onDequeue(refineInLayers);
31155}
31156
31157CRp$a.redrawHint = function (group, bool) {
31158 var r = this;
31159
31160 switch (group) {
31161 case 'eles':
31162 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31163 break;
31164
31165 case 'drag':
31166 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31167 break;
31168
31169 case 'select':
31170 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31171 break;
31172 }
31173}; // whether to use Path2D caching for drawing
31174
31175
31176var pathsImpld = typeof Path2D !== 'undefined';
31177
31178CRp$a.path2dEnabled = function (on) {
31179 if (on === undefined) {
31180 return this.pathsEnabled;
31181 }
31182
31183 this.pathsEnabled = on ? true : false;
31184};
31185
31186CRp$a.usePaths = function () {
31187 return pathsImpld && this.pathsEnabled;
31188};
31189
31190CRp$a.setImgSmoothing = function (context, bool) {
31191 if (context.imageSmoothingEnabled != null) {
31192 context.imageSmoothingEnabled = bool;
31193 } else {
31194 context.webkitImageSmoothingEnabled = bool;
31195 context.mozImageSmoothingEnabled = bool;
31196 context.msImageSmoothingEnabled = bool;
31197 }
31198};
31199
31200CRp$a.getImgSmoothing = function (context) {
31201 if (context.imageSmoothingEnabled != null) {
31202 return context.imageSmoothingEnabled;
31203 } else {
31204 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31205 }
31206};
31207
31208CRp$a.makeOffscreenCanvas = function (width, height) {
31209 var canvas;
31210
31211 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31212 canvas = new OffscreenCanvas(width, height);
31213 } else {
31214 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31215
31216 canvas.width = width;
31217 canvas.height = height;
31218 }
31219
31220 return canvas;
31221};
31222
31223[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31224 extend(CRp$a, props);
31225});
31226
31227var renderer = [{
31228 name: 'null',
31229 impl: NullRenderer
31230}, {
31231 name: 'base',
31232 impl: BR
31233}, {
31234 name: 'canvas',
31235 impl: CR
31236}];
31237
31238var incExts = [{
31239 type: 'layout',
31240 extensions: layout
31241}, {
31242 type: 'renderer',
31243 extensions: renderer
31244}];
31245
31246var extensions = {}; // registered modules for extensions, indexed by name
31247
31248var modules = {};
31249
31250function setExtension(type, name, registrant) {
31251 var ext = registrant;
31252
31253 var overrideErr = function overrideErr(field) {
31254 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31255 };
31256
31257 if (type === 'core') {
31258 if (Core.prototype[name]) {
31259 return overrideErr(name);
31260 } else {
31261 Core.prototype[name] = registrant;
31262 }
31263 } else if (type === 'collection') {
31264 if (Collection.prototype[name]) {
31265 return overrideErr(name);
31266 } else {
31267 Collection.prototype[name] = registrant;
31268 }
31269 } else if (type === 'layout') {
31270 // fill in missing layout functions in the prototype
31271 var Layout = function Layout(options) {
31272 this.options = options;
31273 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31274
31275 if (!plainObject(this._private)) {
31276 this._private = {};
31277 }
31278
31279 this._private.cy = options.cy;
31280 this._private.listeners = [];
31281 this.createEmitter();
31282 };
31283
31284 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31285 var optLayoutFns = [];
31286
31287 for (var i = 0; i < optLayoutFns.length; i++) {
31288 var fnName = optLayoutFns[i];
31289
31290 layoutProto[fnName] = layoutProto[fnName] || function () {
31291 return this;
31292 };
31293 } // either .start() or .run() is defined, so autogen the other
31294
31295
31296 if (layoutProto.start && !layoutProto.run) {
31297 layoutProto.run = function () {
31298 this.start();
31299 return this;
31300 };
31301 } else if (!layoutProto.start && layoutProto.run) {
31302 layoutProto.start = function () {
31303 this.run();
31304 return this;
31305 };
31306 }
31307
31308 var regStop = registrant.prototype.stop;
31309
31310 layoutProto.stop = function () {
31311 var opts = this.options;
31312
31313 if (opts && opts.animate) {
31314 var anis = this.animations;
31315
31316 if (anis) {
31317 for (var _i = 0; _i < anis.length; _i++) {
31318 anis[_i].stop();
31319 }
31320 }
31321 }
31322
31323 if (regStop) {
31324 regStop.call(this);
31325 } else {
31326 this.emit('layoutstop');
31327 }
31328
31329 return this;
31330 };
31331
31332 if (!layoutProto.destroy) {
31333 layoutProto.destroy = function () {
31334 return this;
31335 };
31336 }
31337
31338 layoutProto.cy = function () {
31339 return this._private.cy;
31340 };
31341
31342 var getCy = function getCy(layout) {
31343 return layout._private.cy;
31344 };
31345
31346 var emitterOpts = {
31347 addEventFields: function addEventFields(layout, evt) {
31348 evt.layout = layout;
31349 evt.cy = getCy(layout);
31350 evt.target = layout;
31351 },
31352 bubble: function bubble() {
31353 return true;
31354 },
31355 parent: function parent(layout) {
31356 return getCy(layout);
31357 }
31358 };
31359 extend(layoutProto, {
31360 createEmitter: function createEmitter() {
31361 this._private.emitter = new Emitter(emitterOpts, this);
31362 return this;
31363 },
31364 emitter: function emitter() {
31365 return this._private.emitter;
31366 },
31367 on: function on(evt, cb) {
31368 this.emitter().on(evt, cb);
31369 return this;
31370 },
31371 one: function one(evt, cb) {
31372 this.emitter().one(evt, cb);
31373 return this;
31374 },
31375 once: function once(evt, cb) {
31376 this.emitter().one(evt, cb);
31377 return this;
31378 },
31379 removeListener: function removeListener(evt, cb) {
31380 this.emitter().removeListener(evt, cb);
31381 return this;
31382 },
31383 removeAllListeners: function removeAllListeners() {
31384 this.emitter().removeAllListeners();
31385 return this;
31386 },
31387 emit: function emit(evt, params) {
31388 this.emitter().emit(evt, params);
31389 return this;
31390 }
31391 });
31392 define$3.eventAliasesOn(layoutProto);
31393 ext = Layout; // replace with our wrapped layout
31394 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31395 // user registered renderers inherit from base
31396 var BaseRenderer = getExtension('renderer', 'base');
31397 var bProto = BaseRenderer.prototype;
31398 var RegistrantRenderer = registrant;
31399 var rProto = registrant.prototype;
31400
31401 var Renderer = function Renderer() {
31402 BaseRenderer.apply(this, arguments);
31403 RegistrantRenderer.apply(this, arguments);
31404 };
31405
31406 var proto = Renderer.prototype;
31407
31408 for (var pName in bProto) {
31409 var pVal = bProto[pName];
31410 var existsInR = rProto[pName] != null;
31411
31412 if (existsInR) {
31413 return overrideErr(pName);
31414 }
31415
31416 proto[pName] = pVal; // take impl from base
31417 }
31418
31419 for (var _pName in rProto) {
31420 proto[_pName] = rProto[_pName]; // take impl from registrant
31421 }
31422
31423 bProto.clientFunctions.forEach(function (name) {
31424 proto[name] = proto[name] || function () {
31425 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31426 };
31427 });
31428 ext = Renderer;
31429 }
31430
31431 return setMap({
31432 map: extensions,
31433 keys: [type, name],
31434 value: ext
31435 });
31436}
31437
31438function getExtension(type, name) {
31439 return getMap({
31440 map: extensions,
31441 keys: [type, name]
31442 });
31443}
31444
31445function setModule(type, name, moduleType, moduleName, registrant) {
31446 return setMap({
31447 map: modules,
31448 keys: [type, name, moduleType, moduleName],
31449 value: registrant
31450 });
31451}
31452
31453function getModule(type, name, moduleType, moduleName) {
31454 return getMap({
31455 map: modules,
31456 keys: [type, name, moduleType, moduleName]
31457 });
31458}
31459
31460var extension = function extension() {
31461 // e.g. extension('renderer', 'svg')
31462 if (arguments.length === 2) {
31463 return getExtension.apply(null, arguments);
31464 } // e.g. extension('renderer', 'svg', { ... })
31465 else if (arguments.length === 3) {
31466 return setExtension.apply(null, arguments);
31467 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31468 else if (arguments.length === 4) {
31469 return getModule.apply(null, arguments);
31470 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31471 else if (arguments.length === 5) {
31472 return setModule.apply(null, arguments);
31473 } else {
31474 error('Invalid extension access syntax');
31475 }
31476}; // allows a core instance to access extensions internally
31477
31478
31479Core.prototype.extension = extension; // included extensions
31480
31481incExts.forEach(function (group) {
31482 group.extensions.forEach(function (ext) {
31483 setExtension(group.type, ext.name, ext.impl);
31484 });
31485});
31486
31487// (useful for init)
31488
31489var Stylesheet = function Stylesheet() {
31490 if (!(this instanceof Stylesheet)) {
31491 return new Stylesheet();
31492 }
31493
31494 this.length = 0;
31495};
31496
31497var sheetfn = Stylesheet.prototype;
31498
31499sheetfn.instanceString = function () {
31500 return 'stylesheet';
31501}; // just store the selector to be parsed later
31502
31503
31504sheetfn.selector = function (selector) {
31505 var i = this.length++;
31506 this[i] = {
31507 selector: selector,
31508 properties: []
31509 };
31510 return this; // chaining
31511}; // just store the property to be parsed later
31512
31513
31514sheetfn.css = function (name, value) {
31515 var i = this.length - 1;
31516
31517 if (string(name)) {
31518 this[i].properties.push({
31519 name: name,
31520 value: value
31521 });
31522 } else if (plainObject(name)) {
31523 var map = name;
31524 var propNames = Object.keys(map);
31525
31526 for (var j = 0; j < propNames.length; j++) {
31527 var key = propNames[j];
31528 var mapVal = map[key];
31529
31530 if (mapVal == null) {
31531 continue;
31532 }
31533
31534 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31535
31536 if (prop == null) {
31537 continue;
31538 }
31539
31540 var _name = prop.name;
31541 var _value = mapVal;
31542 this[i].properties.push({
31543 name: _name,
31544 value: _value
31545 });
31546 }
31547 }
31548
31549 return this; // chaining
31550};
31551
31552sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31553
31554sheetfn.generateStyle = function (cy) {
31555 var style = new Style(cy);
31556 return this.appendToStyle(style);
31557}; // append a dummy stylesheet object on a real style object
31558
31559
31560sheetfn.appendToStyle = function (style) {
31561 for (var i = 0; i < this.length; i++) {
31562 var context = this[i];
31563 var selector = context.selector;
31564 var props = context.properties;
31565 style.selector(selector); // apply selector
31566
31567 for (var j = 0; j < props.length; j++) {
31568 var prop = props[j];
31569 style.css(prop.name, prop.value); // apply property
31570 }
31571 }
31572
31573 return style;
31574};
31575
31576var version = "3.16.0";
31577
31578var cytoscape = function cytoscape(options) {
31579 // if no options specified, use default
31580 if (options === undefined) {
31581 options = {};
31582 } // create instance
31583
31584
31585 if (plainObject(options)) {
31586 return new Core(options);
31587 } // allow for registration of extensions
31588 else if (string(options)) {
31589 return extension.apply(extension, arguments);
31590 }
31591}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31592
31593
31594cytoscape.use = function (ext) {
31595 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31596
31597 args.unshift(cytoscape); // cytoscape is first arg to ext
31598
31599 ext.apply(null, args);
31600 return this;
31601};
31602
31603cytoscape.warnings = function (bool) {
31604 return warnings(bool);
31605}; // replaced by build system
31606
31607
31608cytoscape.version = version; // expose public apis (mostly for extensions)
31609
31610cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31611
31612module.exports = cytoscape;