UNPKG

863 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2021, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23'use strict';
24
25function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
26
27var util = _interopDefault(require('lodash.debounce'));
28var Heap = _interopDefault(require('heap'));
29
30function _typeof(obj) {
31 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
32 _typeof = function (obj) {
33 return typeof obj;
34 };
35 } else {
36 _typeof = function (obj) {
37 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
38 };
39 }
40
41 return _typeof(obj);
42}
43
44function _classCallCheck(instance, Constructor) {
45 if (!(instance instanceof Constructor)) {
46 throw new TypeError("Cannot call a class as a function");
47 }
48}
49
50function _defineProperties(target, props) {
51 for (var i = 0; i < props.length; i++) {
52 var descriptor = props[i];
53 descriptor.enumerable = descriptor.enumerable || false;
54 descriptor.configurable = true;
55 if ("value" in descriptor) descriptor.writable = true;
56 Object.defineProperty(target, descriptor.key, descriptor);
57 }
58}
59
60function _createClass(Constructor, protoProps, staticProps) {
61 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
62 if (staticProps) _defineProperties(Constructor, staticProps);
63 return Constructor;
64}
65
66function _defineProperty(obj, key, value) {
67 if (key in obj) {
68 Object.defineProperty(obj, key, {
69 value: value,
70 enumerable: true,
71 configurable: true,
72 writable: true
73 });
74 } else {
75 obj[key] = value;
76 }
77
78 return obj;
79}
80
81function _slicedToArray(arr, i) {
82 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
83}
84
85function _arrayWithHoles(arr) {
86 if (Array.isArray(arr)) return arr;
87}
88
89function _iterableToArrayLimit(arr, i) {
90 var _arr = [];
91 var _n = true;
92 var _d = false;
93 var _e = undefined;
94
95 try {
96 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
97 _arr.push(_s.value);
98
99 if (i && _arr.length === i) break;
100 }
101 } catch (err) {
102 _d = true;
103 _e = err;
104 } finally {
105 try {
106 if (!_n && _i["return"] != null) _i["return"]();
107 } finally {
108 if (_d) throw _e;
109 }
110 }
111
112 return _arr;
113}
114
115function _nonIterableRest() {
116 throw new TypeError("Invalid attempt to destructure non-iterable instance");
117}
118
119var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
120
121var navigator = window$1 ? window$1.navigator : null;
122var document$1 = window$1 ? window$1.document : null;
123
124var typeofstr = _typeof('');
125
126var typeofobj = _typeof({});
127
128var typeoffn = _typeof(function () {});
129
130var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
131
132var instanceStr = function instanceStr(obj) {
133 return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
134};
135
136var string = function string(obj) {
137 return obj != null && _typeof(obj) == typeofstr;
138};
139var fn = function fn(obj) {
140 return obj != null && _typeof(obj) === typeoffn;
141};
142var array = function array(obj) {
143 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
144};
145var plainObject = function plainObject(obj) {
146 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
147};
148var object = function object(obj) {
149 return obj != null && _typeof(obj) === typeofobj;
150};
151var number = function number(obj) {
152 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
153};
154var integer = function integer(obj) {
155 return number(obj) && Math.floor(obj) === obj;
156};
157var htmlElement = function htmlElement(obj) {
158 if ('undefined' === typeofhtmlele) {
159 return undefined;
160 } else {
161 return null != obj && obj instanceof HTMLElement;
162 }
163};
164var elementOrCollection = function elementOrCollection(obj) {
165 return element(obj) || collection(obj);
166};
167var element = function element(obj) {
168 return instanceStr(obj) === 'collection' && obj._private.single;
169};
170var collection = function collection(obj) {
171 return instanceStr(obj) === 'collection' && !obj._private.single;
172};
173var core = function core(obj) {
174 return instanceStr(obj) === 'core';
175};
176var stylesheet = function stylesheet(obj) {
177 return instanceStr(obj) === 'stylesheet';
178};
179var event = function event(obj) {
180 return instanceStr(obj) === 'event';
181};
182var emptyString = function emptyString(obj) {
183 if (obj === undefined || obj === null) {
184 // null is empty
185 return true;
186 } else if (obj === '' || obj.match(/^\s+$/)) {
187 return true; // empty string is empty
188 }
189
190 return false; // otherwise, we don't know what we've got
191};
192var domElement = function domElement(obj) {
193 if (typeof HTMLElement === 'undefined') {
194 return false; // we're not in a browser so it doesn't matter
195 } else {
196 return obj instanceof HTMLElement;
197 }
198};
199var boundingBox = function boundingBox(obj) {
200 return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
201};
202var promise = function promise(obj) {
203 return object(obj) && fn(obj.then);
204};
205var ms = function ms() {
206 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
207}; // probably a better way to detect this...
208
209var memoize = function memoize(fn, keyFn) {
210 if (!keyFn) {
211 keyFn = function keyFn() {
212 if (arguments.length === 1) {
213 return arguments[0];
214 } else if (arguments.length === 0) {
215 return 'undefined';
216 }
217
218 var args = [];
219
220 for (var i = 0; i < arguments.length; i++) {
221 args.push(arguments[i]);
222 }
223
224 return args.join('$');
225 };
226 }
227
228 var memoizedFn = function memoizedFn() {
229 var self = this;
230 var args = arguments;
231 var ret;
232 var k = keyFn.apply(self, args);
233 var cache = memoizedFn.cache;
234
235 if (!(ret = cache[k])) {
236 ret = cache[k] = fn.apply(self, args);
237 }
238
239 return ret;
240 };
241
242 memoizedFn.cache = {};
243 return memoizedFn;
244};
245
246var camel2dash = memoize(function (str) {
247 return str.replace(/([A-Z])/g, function (v) {
248 return '-' + v.toLowerCase();
249 });
250});
251var dash2camel = memoize(function (str) {
252 return str.replace(/(-\w)/g, function (v) {
253 return v[1].toUpperCase();
254 });
255});
256var prependCamel = memoize(function (prefix, str) {
257 return prefix + str[0].toUpperCase() + str.substring(1);
258}, function (prefix, str) {
259 return prefix + '$' + str;
260});
261var capitalize = function capitalize(str) {
262 if (emptyString(str)) {
263 return str;
264 }
265
266 return str.charAt(0).toUpperCase() + str.substring(1);
267};
268
269var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
270var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
271var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
272var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
273var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
274var hex3 = '\\#[0-9a-fA-F]{3}';
275var hex6 = '\\#[0-9a-fA-F]{6}';
276
277var ascending = function ascending(a, b) {
278 if (a < b) {
279 return -1;
280 } else if (a > b) {
281 return 1;
282 } else {
283 return 0;
284 }
285};
286var descending = function descending(a, b) {
287 return -1 * ascending(a, b);
288};
289
290var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
291 var args = arguments;
292
293 for (var i = 1; i < args.length; i++) {
294 var obj = args[i];
295
296 if (obj == null) {
297 continue;
298 }
299
300 var keys = Object.keys(obj);
301
302 for (var j = 0; j < keys.length; j++) {
303 var k = keys[j];
304 tgt[k] = obj[k];
305 }
306 }
307
308 return tgt;
309};
310
311var hex2tuple = function hex2tuple(hex) {
312 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
313 return;
314 }
315
316 var shortHex = hex.length === 4;
317 var r, g, b;
318 var base = 16;
319
320 if (shortHex) {
321 r = parseInt(hex[1] + hex[1], base);
322 g = parseInt(hex[2] + hex[2], base);
323 b = parseInt(hex[3] + hex[3], base);
324 } else {
325 r = parseInt(hex[1] + hex[2], base);
326 g = parseInt(hex[3] + hex[4], base);
327 b = parseInt(hex[5] + hex[6], base);
328 }
329
330 return [r, g, b];
331}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
332
333var hsl2tuple = function hsl2tuple(hsl) {
334 var ret;
335 var h, s, l, a, r, g, b;
336
337 function hue2rgb(p, q, t) {
338 if (t < 0) t += 1;
339 if (t > 1) t -= 1;
340 if (t < 1 / 6) return p + (q - p) * 6 * t;
341 if (t < 1 / 2) return q;
342 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
343 return p;
344 }
345
346 var m = new RegExp('^' + hsla + '$').exec(hsl);
347
348 if (m) {
349 // get hue
350 h = parseInt(m[1]);
351
352 if (h < 0) {
353 h = (360 - -1 * h % 360) % 360;
354 } else if (h > 360) {
355 h = h % 360;
356 }
357
358 h /= 360; // normalise on [0, 1]
359
360 s = parseFloat(m[2]);
361
362 if (s < 0 || s > 100) {
363 return;
364 } // saturation is [0, 100]
365
366
367 s = s / 100; // normalise on [0, 1]
368
369 l = parseFloat(m[3]);
370
371 if (l < 0 || l > 100) {
372 return;
373 } // lightness is [0, 100]
374
375
376 l = l / 100; // normalise on [0, 1]
377
378 a = m[4];
379
380 if (a !== undefined) {
381 a = parseFloat(a);
382
383 if (a < 0 || a > 1) {
384 return;
385 } // alpha is [0, 1]
386
387 } // now, convert to rgb
388 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
389
390
391 if (s === 0) {
392 r = g = b = Math.round(l * 255); // achromatic
393 } else {
394 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
395 var p = 2 * l - q;
396 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
397 g = Math.round(255 * hue2rgb(p, q, h));
398 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
399 }
400
401 ret = [r, g, b, a];
402 }
403
404 return ret;
405}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
406
407var rgb2tuple = function rgb2tuple(rgb) {
408 var ret;
409 var m = new RegExp('^' + rgba + '$').exec(rgb);
410
411 if (m) {
412 ret = [];
413 var isPct = [];
414
415 for (var i = 1; i <= 3; i++) {
416 var channel = m[i];
417
418 if (channel[channel.length - 1] === '%') {
419 isPct[i] = true;
420 }
421
422 channel = parseFloat(channel);
423
424 if (isPct[i]) {
425 channel = channel / 100 * 255; // normalise to [0, 255]
426 }
427
428 if (channel < 0 || channel > 255) {
429 return;
430 } // invalid channel value
431
432
433 ret.push(Math.floor(channel));
434 }
435
436 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
437 var allArePct = isPct[1] && isPct[2] && isPct[3];
438
439 if (atLeastOneIsPct && !allArePct) {
440 return;
441 } // must all be percent values if one is
442
443
444 var alpha = m[4];
445
446 if (alpha !== undefined) {
447 alpha = parseFloat(alpha);
448
449 if (alpha < 0 || alpha > 1) {
450 return;
451 } // invalid alpha value
452
453
454 ret.push(alpha);
455 }
456 }
457
458 return ret;
459};
460var colorname2tuple = function colorname2tuple(color) {
461 return colors[color.toLowerCase()];
462};
463var color2tuple = function color2tuple(color) {
464 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
465};
466var colors = {
467 // special colour names
468 transparent: [0, 0, 0, 0],
469 // NB alpha === 0
470 // regular colours
471 aliceblue: [240, 248, 255],
472 antiquewhite: [250, 235, 215],
473 aqua: [0, 255, 255],
474 aquamarine: [127, 255, 212],
475 azure: [240, 255, 255],
476 beige: [245, 245, 220],
477 bisque: [255, 228, 196],
478 black: [0, 0, 0],
479 blanchedalmond: [255, 235, 205],
480 blue: [0, 0, 255],
481 blueviolet: [138, 43, 226],
482 brown: [165, 42, 42],
483 burlywood: [222, 184, 135],
484 cadetblue: [95, 158, 160],
485 chartreuse: [127, 255, 0],
486 chocolate: [210, 105, 30],
487 coral: [255, 127, 80],
488 cornflowerblue: [100, 149, 237],
489 cornsilk: [255, 248, 220],
490 crimson: [220, 20, 60],
491 cyan: [0, 255, 255],
492 darkblue: [0, 0, 139],
493 darkcyan: [0, 139, 139],
494 darkgoldenrod: [184, 134, 11],
495 darkgray: [169, 169, 169],
496 darkgreen: [0, 100, 0],
497 darkgrey: [169, 169, 169],
498 darkkhaki: [189, 183, 107],
499 darkmagenta: [139, 0, 139],
500 darkolivegreen: [85, 107, 47],
501 darkorange: [255, 140, 0],
502 darkorchid: [153, 50, 204],
503 darkred: [139, 0, 0],
504 darksalmon: [233, 150, 122],
505 darkseagreen: [143, 188, 143],
506 darkslateblue: [72, 61, 139],
507 darkslategray: [47, 79, 79],
508 darkslategrey: [47, 79, 79],
509 darkturquoise: [0, 206, 209],
510 darkviolet: [148, 0, 211],
511 deeppink: [255, 20, 147],
512 deepskyblue: [0, 191, 255],
513 dimgray: [105, 105, 105],
514 dimgrey: [105, 105, 105],
515 dodgerblue: [30, 144, 255],
516 firebrick: [178, 34, 34],
517 floralwhite: [255, 250, 240],
518 forestgreen: [34, 139, 34],
519 fuchsia: [255, 0, 255],
520 gainsboro: [220, 220, 220],
521 ghostwhite: [248, 248, 255],
522 gold: [255, 215, 0],
523 goldenrod: [218, 165, 32],
524 gray: [128, 128, 128],
525 grey: [128, 128, 128],
526 green: [0, 128, 0],
527 greenyellow: [173, 255, 47],
528 honeydew: [240, 255, 240],
529 hotpink: [255, 105, 180],
530 indianred: [205, 92, 92],
531 indigo: [75, 0, 130],
532 ivory: [255, 255, 240],
533 khaki: [240, 230, 140],
534 lavender: [230, 230, 250],
535 lavenderblush: [255, 240, 245],
536 lawngreen: [124, 252, 0],
537 lemonchiffon: [255, 250, 205],
538 lightblue: [173, 216, 230],
539 lightcoral: [240, 128, 128],
540 lightcyan: [224, 255, 255],
541 lightgoldenrodyellow: [250, 250, 210],
542 lightgray: [211, 211, 211],
543 lightgreen: [144, 238, 144],
544 lightgrey: [211, 211, 211],
545 lightpink: [255, 182, 193],
546 lightsalmon: [255, 160, 122],
547 lightseagreen: [32, 178, 170],
548 lightskyblue: [135, 206, 250],
549 lightslategray: [119, 136, 153],
550 lightslategrey: [119, 136, 153],
551 lightsteelblue: [176, 196, 222],
552 lightyellow: [255, 255, 224],
553 lime: [0, 255, 0],
554 limegreen: [50, 205, 50],
555 linen: [250, 240, 230],
556 magenta: [255, 0, 255],
557 maroon: [128, 0, 0],
558 mediumaquamarine: [102, 205, 170],
559 mediumblue: [0, 0, 205],
560 mediumorchid: [186, 85, 211],
561 mediumpurple: [147, 112, 219],
562 mediumseagreen: [60, 179, 113],
563 mediumslateblue: [123, 104, 238],
564 mediumspringgreen: [0, 250, 154],
565 mediumturquoise: [72, 209, 204],
566 mediumvioletred: [199, 21, 133],
567 midnightblue: [25, 25, 112],
568 mintcream: [245, 255, 250],
569 mistyrose: [255, 228, 225],
570 moccasin: [255, 228, 181],
571 navajowhite: [255, 222, 173],
572 navy: [0, 0, 128],
573 oldlace: [253, 245, 230],
574 olive: [128, 128, 0],
575 olivedrab: [107, 142, 35],
576 orange: [255, 165, 0],
577 orangered: [255, 69, 0],
578 orchid: [218, 112, 214],
579 palegoldenrod: [238, 232, 170],
580 palegreen: [152, 251, 152],
581 paleturquoise: [175, 238, 238],
582 palevioletred: [219, 112, 147],
583 papayawhip: [255, 239, 213],
584 peachpuff: [255, 218, 185],
585 peru: [205, 133, 63],
586 pink: [255, 192, 203],
587 plum: [221, 160, 221],
588 powderblue: [176, 224, 230],
589 purple: [128, 0, 128],
590 red: [255, 0, 0],
591 rosybrown: [188, 143, 143],
592 royalblue: [65, 105, 225],
593 saddlebrown: [139, 69, 19],
594 salmon: [250, 128, 114],
595 sandybrown: [244, 164, 96],
596 seagreen: [46, 139, 87],
597 seashell: [255, 245, 238],
598 sienna: [160, 82, 45],
599 silver: [192, 192, 192],
600 skyblue: [135, 206, 235],
601 slateblue: [106, 90, 205],
602 slategray: [112, 128, 144],
603 slategrey: [112, 128, 144],
604 snow: [255, 250, 250],
605 springgreen: [0, 255, 127],
606 steelblue: [70, 130, 180],
607 tan: [210, 180, 140],
608 teal: [0, 128, 128],
609 thistle: [216, 191, 216],
610 tomato: [255, 99, 71],
611 turquoise: [64, 224, 208],
612 violet: [238, 130, 238],
613 wheat: [245, 222, 179],
614 white: [255, 255, 255],
615 whitesmoke: [245, 245, 245],
616 yellow: [255, 255, 0],
617 yellowgreen: [154, 205, 50]
618};
619
620var setMap = function setMap(options) {
621 var obj = options.map;
622 var keys = options.keys;
623 var l = keys.length;
624
625 for (var i = 0; i < l; i++) {
626 var key = keys[i];
627
628 if (plainObject(key)) {
629 throw Error('Tried to set map with object key');
630 }
631
632 if (i < keys.length - 1) {
633 // extend the map if necessary
634 if (obj[key] == null) {
635 obj[key] = {};
636 }
637
638 obj = obj[key];
639 } else {
640 // set the value
641 obj[key] = options.value;
642 }
643 }
644}; // gets the value in a map even if it's not built in places
645
646var getMap = function getMap(options) {
647 var obj = options.map;
648 var keys = options.keys;
649 var l = keys.length;
650
651 for (var i = 0; i < l; i++) {
652 var key = keys[i];
653
654 if (plainObject(key)) {
655 throw Error('Tried to get map with object key');
656 }
657
658 obj = obj[key];
659
660 if (obj == null) {
661 return obj;
662 }
663 }
664
665 return obj;
666}; // deletes the entry in the map
667
668var performance = window$1 ? window$1.performance : null;
669var pnow = performance && performance.now ? function () {
670 return performance.now();
671} : function () {
672 return Date.now();
673};
674
675var raf = function () {
676 if (window$1) {
677 if (window$1.requestAnimationFrame) {
678 return function (fn) {
679 window$1.requestAnimationFrame(fn);
680 };
681 } else if (window$1.mozRequestAnimationFrame) {
682 return function (fn) {
683 window$1.mozRequestAnimationFrame(fn);
684 };
685 } else if (window$1.webkitRequestAnimationFrame) {
686 return function (fn) {
687 window$1.webkitRequestAnimationFrame(fn);
688 };
689 } else if (window$1.msRequestAnimationFrame) {
690 return function (fn) {
691 window$1.msRequestAnimationFrame(fn);
692 };
693 }
694 }
695
696 return function (fn) {
697 if (fn) {
698 setTimeout(function () {
699 fn(pnow());
700 }, 1000 / 60);
701 }
702 };
703}();
704
705var requestAnimationFrame = function requestAnimationFrame(fn) {
706 return raf(fn);
707};
708var performanceNow = pnow;
709
710var DEFAULT_HASH_SEED = 9261;
711var K = 65599; // 37 also works pretty well
712
713var DEFAULT_HASH_SEED_ALT = 5381;
714var hashIterableInts = function hashIterableInts(iterator) {
715 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
716 // sdbm/string-hash
717 var hash = seed;
718 var entry;
719
720 for (;;) {
721 entry = iterator.next();
722
723 if (entry.done) {
724 break;
725 }
726
727 hash = hash * K + entry.value | 0;
728 }
729
730 return hash;
731};
732var hashInt = function hashInt(num) {
733 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
734 // sdbm/string-hash
735 return seed * K + num | 0;
736};
737var hashIntAlt = function hashIntAlt(num) {
738 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
739 // djb2/string-hash
740 return (seed << 5) + seed + num | 0;
741};
742var combineHashes = function combineHashes(hash1, hash2) {
743 return hash1 * 0x200000 + hash2;
744};
745var combineHashesArray = function combineHashesArray(hashes) {
746 return hashes[0] * 0x200000 + hashes[1];
747};
748var hashArrays = function hashArrays(hashes1, hashes2) {
749 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
750};
751var hashIntsArray = function hashIntsArray(ints, seed) {
752 var entry = {
753 value: 0,
754 done: false
755 };
756 var i = 0;
757 var length = ints.length;
758 var iterator = {
759 next: function next() {
760 if (i < length) {
761 entry.value = ints[i++];
762 } else {
763 entry.done = true;
764 }
765
766 return entry;
767 }
768 };
769 return hashIterableInts(iterator, seed);
770};
771var hashString = function hashString(str, seed) {
772 var entry = {
773 value: 0,
774 done: false
775 };
776 var i = 0;
777 var length = str.length;
778 var iterator = {
779 next: function next() {
780 if (i < length) {
781 entry.value = str.charCodeAt(i++);
782 } else {
783 entry.done = true;
784 }
785
786 return entry;
787 }
788 };
789 return hashIterableInts(iterator, seed);
790};
791var hashStrings = function hashStrings() {
792 return hashStringsArray(arguments);
793};
794var hashStringsArray = function hashStringsArray(strs) {
795 var hash;
796
797 for (var i = 0; i < strs.length; i++) {
798 var str = strs[i];
799
800 if (i === 0) {
801 hash = hashString(str);
802 } else {
803 hash = hashString(str, hash);
804 }
805 }
806
807 return hash;
808};
809
810/*global console */
811var warningsEnabled = true;
812var warnSupported = console.warn != null; // eslint-disable-line no-console
813
814var traceSupported = console.trace != null; // eslint-disable-line no-console
815
816var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
817var trueify = function trueify() {
818 return true;
819};
820var falsify = function falsify() {
821 return false;
822};
823var zeroify = function zeroify() {
824 return 0;
825};
826var noop = function noop() {};
827var error = function error(msg) {
828 throw new Error(msg);
829};
830var warnings = function warnings(enabled) {
831 if (enabled !== undefined) {
832 warningsEnabled = !!enabled;
833 } else {
834 return warningsEnabled;
835 }
836};
837var warn = function warn(msg) {
838 /* eslint-disable no-console */
839 if (!warnings()) {
840 return;
841 }
842
843 if (warnSupported) {
844 console.warn(msg);
845 } else {
846 console.log(msg);
847
848 if (traceSupported) {
849 console.trace();
850 }
851 }
852};
853/* eslint-enable */
854
855var clone = function clone(obj) {
856 return extend({}, obj);
857}; // gets a shallow copy of the argument
858
859var copy = function copy(obj) {
860 if (obj == null) {
861 return obj;
862 }
863
864 if (array(obj)) {
865 return obj.slice();
866 } else if (plainObject(obj)) {
867 return clone(obj);
868 } else {
869 return obj;
870 }
871};
872var copyArray = function copyArray(arr) {
873 return arr.slice();
874};
875var uuid = function uuid(a, b
876/* placeholders */
877) {
878 for ( // loop :)
879 b = a = ''; // b - result , a - numeric letiable
880 a++ < 36; //
881 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
882 ? // return a random number or 4
883 (a ^ 15 // if "a" is not 15
884 ? // genetate a random number from 0 to 15
885 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
886 : 4 // otherwise 4
887 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
888 ) {
889 }
890
891 return b;
892};
893var _staticEmptyObject = {};
894var staticEmptyObject = function staticEmptyObject() {
895 return _staticEmptyObject;
896};
897var defaults = function defaults(_defaults) {
898 var keys = Object.keys(_defaults);
899 return function (opts) {
900 var filledOpts = {};
901
902 for (var i = 0; i < keys.length; i++) {
903 var key = keys[i];
904 var optVal = opts == null ? undefined : opts[key];
905 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
906 }
907
908 return filledOpts;
909 };
910};
911var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
912 for (var i = arr.length - 1; i >= 0; i--) {
913 if (arr[i] === ele) {
914 arr.splice(i, 1);
915
916 if (oneCopy) {
917 break;
918 }
919 }
920 }
921};
922var clearArray = function clearArray(arr) {
923 arr.splice(0, arr.length);
924};
925var push = function push(arr, otherArr) {
926 for (var i = 0; i < otherArr.length; i++) {
927 var el = otherArr[i];
928 arr.push(el);
929 }
930};
931var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
932 if (prefix) {
933 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
934 }
935
936 return obj[propName];
937};
938var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
939 if (prefix) {
940 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
941 }
942
943 obj[propName] = value;
944};
945
946/* global Map */
947var ObjectMap =
948/*#__PURE__*/
949function () {
950 function ObjectMap() {
951 _classCallCheck(this, ObjectMap);
952
953 this._obj = {};
954 }
955
956 _createClass(ObjectMap, [{
957 key: "set",
958 value: function set(key, val) {
959 this._obj[key] = val;
960 return this;
961 }
962 }, {
963 key: "delete",
964 value: function _delete(key) {
965 this._obj[key] = undefined;
966 return this;
967 }
968 }, {
969 key: "clear",
970 value: function clear() {
971 this._obj = {};
972 }
973 }, {
974 key: "has",
975 value: function has(key) {
976 return this._obj[key] !== undefined;
977 }
978 }, {
979 key: "get",
980 value: function get(key) {
981 return this._obj[key];
982 }
983 }]);
984
985 return ObjectMap;
986}();
987
988var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
989
990/* global Set */
991var undef = "undefined" ;
992
993var ObjectSet =
994/*#__PURE__*/
995function () {
996 function ObjectSet(arrayOrObjectSet) {
997 _classCallCheck(this, ObjectSet);
998
999 this._obj = Object.create(null);
1000 this.size = 0;
1001
1002 if (arrayOrObjectSet != null) {
1003 var arr;
1004
1005 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1006 arr = arrayOrObjectSet.toArray();
1007 } else {
1008 arr = arrayOrObjectSet;
1009 }
1010
1011 for (var i = 0; i < arr.length; i++) {
1012 this.add(arr[i]);
1013 }
1014 }
1015 }
1016
1017 _createClass(ObjectSet, [{
1018 key: "instanceString",
1019 value: function instanceString() {
1020 return 'set';
1021 }
1022 }, {
1023 key: "add",
1024 value: function add(val) {
1025 var o = this._obj;
1026
1027 if (o[val] !== 1) {
1028 o[val] = 1;
1029 this.size++;
1030 }
1031 }
1032 }, {
1033 key: "delete",
1034 value: function _delete(val) {
1035 var o = this._obj;
1036
1037 if (o[val] === 1) {
1038 o[val] = 0;
1039 this.size--;
1040 }
1041 }
1042 }, {
1043 key: "clear",
1044 value: function clear() {
1045 this._obj = Object.create(null);
1046 }
1047 }, {
1048 key: "has",
1049 value: function has(val) {
1050 return this._obj[val] === 1;
1051 }
1052 }, {
1053 key: "toArray",
1054 value: function toArray() {
1055 var _this = this;
1056
1057 return Object.keys(this._obj).filter(function (key) {
1058 return _this.has(key);
1059 });
1060 }
1061 }, {
1062 key: "forEach",
1063 value: function forEach(callback, thisArg) {
1064 return this.toArray().forEach(callback, thisArg);
1065 }
1066 }]);
1067
1068 return ObjectSet;
1069}();
1070
1071var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1072
1073var Element = function Element(cy, params) {
1074 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1075
1076 if (cy === undefined || params === undefined || !core(cy)) {
1077 error('An element must have a core reference and parameters set');
1078 return;
1079 }
1080
1081 var group = params.group; // try to automatically infer the group if unspecified
1082
1083 if (group == null) {
1084 if (params.data && params.data.source != null && params.data.target != null) {
1085 group = 'edges';
1086 } else {
1087 group = 'nodes';
1088 }
1089 } // validate group
1090
1091
1092 if (group !== 'nodes' && group !== 'edges') {
1093 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1094 return;
1095 } // make the element array-like, just like a collection
1096
1097
1098 this.length = 1;
1099 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1100
1101 var _p = this._private = {
1102 cy: cy,
1103 single: true,
1104 // indicates this is an element
1105 data: params.data || {},
1106 // data object
1107 position: params.position || {
1108 x: 0,
1109 y: 0
1110 },
1111 // (x, y) position pair
1112 autoWidth: undefined,
1113 // width and height of nodes calculated by the renderer when set to special 'auto' value
1114 autoHeight: undefined,
1115 autoPadding: undefined,
1116 compoundBoundsClean: false,
1117 // whether the compound dimensions need to be recalculated the next time dimensions are read
1118 listeners: [],
1119 // array of bound listeners
1120 group: group,
1121 // string; 'nodes' or 'edges'
1122 style: {},
1123 // properties as set by the style
1124 rstyle: {},
1125 // properties for style sent from the renderer to the core
1126 styleCxts: [],
1127 // applied style contexts from the styler
1128 styleKeys: {},
1129 // per-group keys of style property values
1130 removed: true,
1131 // whether it's inside the vis; true if removed (set true here since we call restore)
1132 selected: params.selected ? true : false,
1133 // whether it's selected
1134 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1135 // whether it's selectable
1136 locked: params.locked ? true : false,
1137 // whether the element is locked (cannot be moved)
1138 grabbed: false,
1139 // whether the element is grabbed by the mouse; renderer sets this privately
1140 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1141 // whether the element can be grabbed
1142 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1143 // whether the element has passthrough panning enabled
1144 active: false,
1145 // whether the element is active from user interaction
1146 classes: new Set$1(),
1147 // map ( className => true )
1148 animation: {
1149 // object for currently-running animations
1150 current: [],
1151 queue: []
1152 },
1153 rscratch: {},
1154 // object in which the renderer can store information
1155 scratch: params.scratch || {},
1156 // scratch objects
1157 edges: [],
1158 // array of connected edges
1159 children: [],
1160 // array of children
1161 parent: null,
1162 // parent ref
1163 traversalCache: {},
1164 // cache of output of traversal functions
1165 backgrounding: false,
1166 // whether background images are loading
1167 bbCache: null,
1168 // cache of the current bounding box
1169 bbCacheShift: {
1170 x: 0,
1171 y: 0
1172 },
1173 // shift applied to cached bb to be applied on next get
1174 bodyBounds: null,
1175 // bounds cache of element body, w/o overlay
1176 overlayBounds: null,
1177 // bounds cache of element body, including overlay
1178 labelBounds: {
1179 // bounds cache of labels
1180 all: null,
1181 source: null,
1182 target: null,
1183 main: null
1184 },
1185 arrowBounds: {
1186 // bounds cache of edge arrows
1187 source: null,
1188 target: null,
1189 'mid-source': null,
1190 'mid-target': null
1191 }
1192 };
1193
1194 if (_p.position.x == null) {
1195 _p.position.x = 0;
1196 }
1197
1198 if (_p.position.y == null) {
1199 _p.position.y = 0;
1200 } // renderedPosition overrides if specified
1201
1202
1203 if (params.renderedPosition) {
1204 var rpos = params.renderedPosition;
1205 var pan = cy.pan();
1206 var zoom = cy.zoom();
1207 _p.position = {
1208 x: (rpos.x - pan.x) / zoom,
1209 y: (rpos.y - pan.y) / zoom
1210 };
1211 }
1212
1213 var classes = [];
1214
1215 if (array(params.classes)) {
1216 classes = params.classes;
1217 } else if (string(params.classes)) {
1218 classes = params.classes.split(/\s+/);
1219 }
1220
1221 for (var i = 0, l = classes.length; i < l; i++) {
1222 var cls = classes[i];
1223
1224 if (!cls || cls === '') {
1225 continue;
1226 }
1227
1228 _p.classes.add(cls);
1229 }
1230
1231 this.createEmitter();
1232 var bypass = params.style || params.css;
1233
1234 if (bypass) {
1235 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1236 this.style(bypass);
1237 }
1238
1239 if (restore === undefined || restore) {
1240 this.restore();
1241 }
1242};
1243
1244var defineSearch = function defineSearch(params) {
1245 params = {
1246 bfs: params.bfs || !params.dfs,
1247 dfs: params.dfs || !params.bfs
1248 }; // from pseudocode on wikipedia
1249
1250 return function searchFn(roots, fn$1, directed) {
1251 var options;
1252
1253 if (plainObject(roots) && !elementOrCollection(roots)) {
1254 options = roots;
1255 roots = options.roots || options.root;
1256 fn$1 = options.visit;
1257 directed = options.directed;
1258 }
1259
1260 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1261 fn$1 = fn(fn$1) ? fn$1 : function () {};
1262 var cy = this._private.cy;
1263 var v = roots = string(roots) ? this.filter(roots) : roots;
1264 var Q = [];
1265 var connectedNodes = [];
1266 var connectedBy = {};
1267 var id2depth = {};
1268 var V = {};
1269 var j = 0;
1270 var found;
1271
1272 var _this$byGroup = this.byGroup(),
1273 nodes = _this$byGroup.nodes,
1274 edges = _this$byGroup.edges; // enqueue v
1275
1276
1277 for (var i = 0; i < v.length; i++) {
1278 var vi = v[i];
1279 var viId = vi.id();
1280
1281 if (vi.isNode()) {
1282 Q.unshift(vi);
1283
1284 if (params.bfs) {
1285 V[viId] = true;
1286 connectedNodes.push(vi);
1287 }
1288
1289 id2depth[viId] = 0;
1290 }
1291 }
1292
1293 var _loop2 = function _loop2() {
1294 var v = params.bfs ? Q.shift() : Q.pop();
1295 var vId = v.id();
1296
1297 if (params.dfs) {
1298 if (V[vId]) {
1299 return "continue";
1300 }
1301
1302 V[vId] = true;
1303 connectedNodes.push(v);
1304 }
1305
1306 var depth = id2depth[vId];
1307 var prevEdge = connectedBy[vId];
1308 var src = prevEdge != null ? prevEdge.source() : null;
1309 var tgt = prevEdge != null ? prevEdge.target() : null;
1310 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1311 var ret = void 0;
1312 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1313
1314 if (ret === true) {
1315 found = v;
1316 return "break";
1317 }
1318
1319 if (ret === false) {
1320 return "break";
1321 }
1322
1323 var vwEdges = v.connectedEdges().filter(function (e) {
1324 return (!directed || e.source().same(v)) && edges.has(e);
1325 });
1326
1327 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1328 var e = vwEdges[_i2];
1329 var w = e.connectedNodes().filter(function (n) {
1330 return !n.same(v) && nodes.has(n);
1331 });
1332 var wId = w.id();
1333
1334 if (w.length !== 0 && !V[wId]) {
1335 w = w[0];
1336 Q.push(w);
1337
1338 if (params.bfs) {
1339 V[wId] = true;
1340 connectedNodes.push(w);
1341 }
1342
1343 connectedBy[wId] = e;
1344 id2depth[wId] = id2depth[vId] + 1;
1345 }
1346 }
1347 };
1348
1349 _loop: while (Q.length !== 0) {
1350 var _ret = _loop2();
1351
1352 switch (_ret) {
1353 case "continue":
1354 continue;
1355
1356 case "break":
1357 break _loop;
1358 }
1359 }
1360
1361 var connectedEles = cy.collection();
1362
1363 for (var _i = 0; _i < connectedNodes.length; _i++) {
1364 var node = connectedNodes[_i];
1365 var edge = connectedBy[node.id()];
1366
1367 if (edge != null) {
1368 connectedEles.push(edge);
1369 }
1370
1371 connectedEles.push(node);
1372 }
1373
1374 return {
1375 path: cy.collection(connectedEles),
1376 found: cy.collection(found)
1377 };
1378 };
1379}; // search, spanning trees, etc
1380
1381
1382var elesfn = {
1383 breadthFirstSearch: defineSearch({
1384 bfs: true
1385 }),
1386 depthFirstSearch: defineSearch({
1387 dfs: true
1388 })
1389}; // nice, short mathemathical alias
1390
1391elesfn.bfs = elesfn.breadthFirstSearch;
1392elesfn.dfs = elesfn.depthFirstSearch;
1393
1394var dijkstraDefaults = defaults({
1395 root: null,
1396 weight: function weight(edge) {
1397 return 1;
1398 },
1399 directed: false
1400});
1401var elesfn$1 = {
1402 dijkstra: function dijkstra(options) {
1403 if (!plainObject(options)) {
1404 var args = arguments;
1405 options = {
1406 root: args[0],
1407 weight: args[1],
1408 directed: args[2]
1409 };
1410 }
1411
1412 var _dijkstraDefaults = dijkstraDefaults(options),
1413 root = _dijkstraDefaults.root,
1414 weight = _dijkstraDefaults.weight,
1415 directed = _dijkstraDefaults.directed;
1416
1417 var eles = this;
1418 var weightFn = weight;
1419 var source = string(root) ? this.filter(root)[0] : root[0];
1420 var dist = {};
1421 var prev = {};
1422 var knownDist = {};
1423
1424 var _this$byGroup = this.byGroup(),
1425 nodes = _this$byGroup.nodes,
1426 edges = _this$byGroup.edges;
1427
1428 edges.unmergeBy(function (ele) {
1429 return ele.isLoop();
1430 });
1431
1432 var getDist = function getDist(node) {
1433 return dist[node.id()];
1434 };
1435
1436 var setDist = function setDist(node, d) {
1437 dist[node.id()] = d;
1438 Q.updateItem(node);
1439 };
1440
1441 var Q = new Heap(function (a, b) {
1442 return getDist(a) - getDist(b);
1443 });
1444
1445 for (var i = 0; i < nodes.length; i++) {
1446 var node = nodes[i];
1447 dist[node.id()] = node.same(source) ? 0 : Infinity;
1448 Q.push(node);
1449 }
1450
1451 var distBetween = function distBetween(u, v) {
1452 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1453 var smallestDistance = Infinity;
1454 var smallestEdge;
1455
1456 for (var _i = 0; _i < uvs.length; _i++) {
1457 var edge = uvs[_i];
1458
1459 var _weight = weightFn(edge);
1460
1461 if (_weight < smallestDistance || !smallestEdge) {
1462 smallestDistance = _weight;
1463 smallestEdge = edge;
1464 }
1465 }
1466
1467 return {
1468 edge: smallestEdge,
1469 dist: smallestDistance
1470 };
1471 };
1472
1473 while (Q.size() > 0) {
1474 var u = Q.pop();
1475 var smalletsDist = getDist(u);
1476 var uid = u.id();
1477 knownDist[uid] = smalletsDist;
1478
1479 if (smalletsDist === Infinity) {
1480 continue;
1481 }
1482
1483 var neighbors = u.neighborhood().intersect(nodes);
1484
1485 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1486 var v = neighbors[_i2];
1487 var vid = v.id();
1488 var vDist = distBetween(u, v);
1489 var alt = smalletsDist + vDist.dist;
1490
1491 if (alt < getDist(v)) {
1492 setDist(v, alt);
1493 prev[vid] = {
1494 node: u,
1495 edge: vDist.edge
1496 };
1497 }
1498 } // for
1499
1500 } // while
1501
1502
1503 return {
1504 distanceTo: function distanceTo(node) {
1505 var target = string(node) ? nodes.filter(node)[0] : node[0];
1506 return knownDist[target.id()];
1507 },
1508 pathTo: function pathTo(node) {
1509 var target = string(node) ? nodes.filter(node)[0] : node[0];
1510 var S = [];
1511 var u = target;
1512 var uid = u.id();
1513
1514 if (target.length > 0) {
1515 S.unshift(target);
1516
1517 while (prev[uid]) {
1518 var p = prev[uid];
1519 S.unshift(p.edge);
1520 S.unshift(p.node);
1521 u = p.node;
1522 uid = u.id();
1523 }
1524 }
1525
1526 return eles.spawn(S);
1527 }
1528 };
1529 }
1530};
1531
1532var elesfn$2 = {
1533 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1534 // implemented from pseudocode from wikipedia
1535 kruskal: function kruskal(weightFn) {
1536 weightFn = weightFn || function (edge) {
1537 return 1;
1538 };
1539
1540 var _this$byGroup = this.byGroup(),
1541 nodes = _this$byGroup.nodes,
1542 edges = _this$byGroup.edges;
1543
1544 var numNodes = nodes.length;
1545 var forest = new Array(numNodes);
1546 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1547
1548 var findSetIndex = function findSetIndex(ele) {
1549 for (var i = 0; i < forest.length; i++) {
1550 var eles = forest[i];
1551
1552 if (eles.has(ele)) {
1553 return i;
1554 }
1555 }
1556 }; // start with one forest per node
1557
1558
1559 for (var i = 0; i < numNodes; i++) {
1560 forest[i] = this.spawn(nodes[i]);
1561 }
1562
1563 var S = edges.sort(function (a, b) {
1564 return weightFn(a) - weightFn(b);
1565 });
1566
1567 for (var _i = 0; _i < S.length; _i++) {
1568 var edge = S[_i];
1569 var u = edge.source()[0];
1570 var v = edge.target()[0];
1571 var setUIndex = findSetIndex(u);
1572 var setVIndex = findSetIndex(v);
1573 var setU = forest[setUIndex];
1574 var setV = forest[setVIndex];
1575
1576 if (setUIndex !== setVIndex) {
1577 A.merge(edge); // combine forests for u and v
1578
1579 setU.merge(setV);
1580 forest.splice(setVIndex, 1);
1581 }
1582 }
1583
1584 return A;
1585 }
1586};
1587
1588var aStarDefaults = defaults({
1589 root: null,
1590 goal: null,
1591 weight: function weight(edge) {
1592 return 1;
1593 },
1594 heuristic: function heuristic(edge) {
1595 return 0;
1596 },
1597 directed: false
1598});
1599var elesfn$3 = {
1600 // Implemented from pseudocode from wikipedia
1601 aStar: function aStar(options) {
1602 var cy = this.cy();
1603
1604 var _aStarDefaults = aStarDefaults(options),
1605 root = _aStarDefaults.root,
1606 goal = _aStarDefaults.goal,
1607 heuristic = _aStarDefaults.heuristic,
1608 directed = _aStarDefaults.directed,
1609 weight = _aStarDefaults.weight;
1610
1611 root = cy.collection(root)[0];
1612 goal = cy.collection(goal)[0];
1613 var sid = root.id();
1614 var tid = goal.id();
1615 var gScore = {};
1616 var fScore = {};
1617 var closedSetIds = {};
1618 var openSet = new Heap(function (a, b) {
1619 return fScore[a.id()] - fScore[b.id()];
1620 });
1621 var openSetIds = new Set$1();
1622 var cameFrom = {};
1623 var cameFromEdge = {};
1624
1625 var addToOpenSet = function addToOpenSet(ele, id) {
1626 openSet.push(ele);
1627 openSetIds.add(id);
1628 };
1629
1630 var cMin, cMinId;
1631
1632 var popFromOpenSet = function popFromOpenSet() {
1633 cMin = openSet.pop();
1634 cMinId = cMin.id();
1635 openSetIds["delete"](cMinId);
1636 };
1637
1638 var isInOpenSet = function isInOpenSet(id) {
1639 return openSetIds.has(id);
1640 };
1641
1642 addToOpenSet(root, sid);
1643 gScore[sid] = 0;
1644 fScore[sid] = heuristic(root); // Counter
1645
1646 var steps = 0; // Main loop
1647
1648 while (openSet.size() > 0) {
1649 popFromOpenSet();
1650 steps++; // If we've found our goal, then we are done
1651
1652 if (cMinId === tid) {
1653 var path = [];
1654 var pathNode = goal;
1655 var pathNodeId = tid;
1656 var pathEdge = cameFromEdge[pathNodeId];
1657
1658 for (;;) {
1659 path.unshift(pathNode);
1660
1661 if (pathEdge != null) {
1662 path.unshift(pathEdge);
1663 }
1664
1665 pathNode = cameFrom[pathNodeId];
1666
1667 if (pathNode == null) {
1668 break;
1669 }
1670
1671 pathNodeId = pathNode.id();
1672 pathEdge = cameFromEdge[pathNodeId];
1673 }
1674
1675 return {
1676 found: true,
1677 distance: gScore[cMinId],
1678 path: this.spawn(path),
1679 steps: steps
1680 };
1681 } // Add cMin to processed nodes
1682
1683
1684 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1685 // Take into account if graph is directed or not
1686
1687 var vwEdges = cMin._private.edges;
1688
1689 for (var i = 0; i < vwEdges.length; i++) {
1690 var e = vwEdges[i]; // edge must be in set of calling eles
1691
1692 if (!this.hasElementWithId(e.id())) {
1693 continue;
1694 } // cMin must be the source of edge if directed
1695
1696
1697 if (directed && e.data('source') !== cMinId) {
1698 continue;
1699 }
1700
1701 var wSrc = e.source();
1702 var wTgt = e.target();
1703 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1704 var wid = w.id(); // node must be in set of calling eles
1705
1706 if (!this.hasElementWithId(wid)) {
1707 continue;
1708 } // if node is in closedSet, ignore it
1709
1710
1711 if (closedSetIds[wid]) {
1712 continue;
1713 } // New tentative score for node w
1714
1715
1716 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1717 // w not present in openSet
1718 // OR
1719 // tentative gScore is less than previous value
1720 // w not in openSet
1721
1722 if (!isInOpenSet(wid)) {
1723 gScore[wid] = tempScore;
1724 fScore[wid] = tempScore + heuristic(w);
1725 addToOpenSet(w, wid);
1726 cameFrom[wid] = cMin;
1727 cameFromEdge[wid] = e;
1728 continue;
1729 } // w already in openSet, but with greater gScore
1730
1731
1732 if (tempScore < gScore[wid]) {
1733 gScore[wid] = tempScore;
1734 fScore[wid] = tempScore + heuristic(w);
1735 cameFrom[wid] = cMin;
1736 cameFromEdge[wid] = e;
1737 }
1738 } // End of neighbors update
1739
1740 } // End of main loop
1741 // If we've reached here, then we've not reached our goal
1742
1743
1744 return {
1745 found: false,
1746 distance: undefined,
1747 path: undefined,
1748 steps: steps
1749 };
1750 }
1751}; // elesfn
1752
1753var floydWarshallDefaults = defaults({
1754 weight: function weight(edge) {
1755 return 1;
1756 },
1757 directed: false
1758});
1759var elesfn$4 = {
1760 // Implemented from pseudocode from wikipedia
1761 floydWarshall: function floydWarshall(options) {
1762 var cy = this.cy();
1763
1764 var _floydWarshallDefault = floydWarshallDefaults(options),
1765 weight = _floydWarshallDefault.weight,
1766 directed = _floydWarshallDefault.directed;
1767
1768 var weightFn = weight;
1769
1770 var _this$byGroup = this.byGroup(),
1771 nodes = _this$byGroup.nodes,
1772 edges = _this$byGroup.edges;
1773
1774 var N = nodes.length;
1775 var Nsq = N * N;
1776
1777 var indexOf = function indexOf(node) {
1778 return nodes.indexOf(node);
1779 };
1780
1781 var atIndex = function atIndex(i) {
1782 return nodes[i];
1783 }; // Initialize distance matrix
1784
1785
1786 var dist = new Array(Nsq);
1787
1788 for (var n = 0; n < Nsq; n++) {
1789 var j = n % N;
1790 var i = (n - j) / N;
1791
1792 if (i === j) {
1793 dist[n] = 0;
1794 } else {
1795 dist[n] = Infinity;
1796 }
1797 } // Initialize matrix used for path reconstruction
1798 // Initialize distance matrix
1799
1800
1801 var next = new Array(Nsq);
1802 var edgeNext = new Array(Nsq); // Process edges
1803
1804 for (var _i = 0; _i < edges.length; _i++) {
1805 var edge = edges[_i];
1806 var src = edge.source()[0];
1807 var tgt = edge.target()[0];
1808
1809 if (src === tgt) {
1810 continue;
1811 } // exclude loops
1812
1813
1814 var s = indexOf(src);
1815 var t = indexOf(tgt);
1816 var st = s * N + t; // source to target index
1817
1818 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1819
1820
1821 if (dist[st] > _weight) {
1822 dist[st] = _weight;
1823 next[st] = t;
1824 edgeNext[st] = edge;
1825 } // If undirected graph, process 'reversed' edge
1826
1827
1828 if (!directed) {
1829 var ts = t * N + s; // target to source index
1830
1831 if (!directed && dist[ts] > _weight) {
1832 dist[ts] = _weight;
1833 next[ts] = s;
1834 edgeNext[ts] = edge;
1835 }
1836 }
1837 } // Main loop
1838
1839
1840 for (var k = 0; k < N; k++) {
1841 for (var _i2 = 0; _i2 < N; _i2++) {
1842 var ik = _i2 * N + k;
1843
1844 for (var _j = 0; _j < N; _j++) {
1845 var ij = _i2 * N + _j;
1846 var kj = k * N + _j;
1847
1848 if (dist[ik] + dist[kj] < dist[ij]) {
1849 dist[ij] = dist[ik] + dist[kj];
1850 next[ij] = next[ik];
1851 }
1852 }
1853 }
1854 }
1855
1856 var getArgEle = function getArgEle(ele) {
1857 return (string(ele) ? cy.filter(ele) : ele)[0];
1858 };
1859
1860 var indexOfArgEle = function indexOfArgEle(ele) {
1861 return indexOf(getArgEle(ele));
1862 };
1863
1864 var res = {
1865 distance: function distance(from, to) {
1866 var i = indexOfArgEle(from);
1867 var j = indexOfArgEle(to);
1868 return dist[i * N + j];
1869 },
1870 path: function path(from, to) {
1871 var i = indexOfArgEle(from);
1872 var j = indexOfArgEle(to);
1873 var fromNode = atIndex(i);
1874
1875 if (i === j) {
1876 return fromNode.collection();
1877 }
1878
1879 if (next[i * N + j] == null) {
1880 return cy.collection();
1881 }
1882
1883 var path = cy.collection();
1884 var prev = i;
1885 var edge;
1886 path.merge(fromNode);
1887
1888 while (i !== j) {
1889 prev = i;
1890 i = next[i * N + j];
1891 edge = edgeNext[prev * N + i];
1892 path.merge(edge);
1893 path.merge(atIndex(i));
1894 }
1895
1896 return path;
1897 }
1898 };
1899 return res;
1900 } // floydWarshall
1901
1902}; // elesfn
1903
1904var bellmanFordDefaults = defaults({
1905 weight: function weight(edge) {
1906 return 1;
1907 },
1908 directed: false,
1909 root: null
1910});
1911var elesfn$5 = {
1912 // Implemented from pseudocode from wikipedia
1913 bellmanFord: function bellmanFord(options) {
1914 var _this = this;
1915
1916 var _bellmanFordDefaults = bellmanFordDefaults(options),
1917 weight = _bellmanFordDefaults.weight,
1918 directed = _bellmanFordDefaults.directed,
1919 root = _bellmanFordDefaults.root;
1920
1921 var weightFn = weight;
1922 var eles = this;
1923 var cy = this.cy();
1924
1925 var _this$byGroup = this.byGroup(),
1926 edges = _this$byGroup.edges,
1927 nodes = _this$byGroup.nodes;
1928
1929 var numNodes = nodes.length;
1930 var infoMap = new Map$1();
1931 var hasNegativeWeightCycle = false;
1932 var negativeWeightCycles = [];
1933 root = cy.collection(root)[0]; // in case selector passed
1934
1935 edges.unmergeBy(function (edge) {
1936 return edge.isLoop();
1937 });
1938 var numEdges = edges.length;
1939
1940 var getInfo = function getInfo(node) {
1941 var obj = infoMap.get(node.id());
1942
1943 if (!obj) {
1944 obj = {};
1945 infoMap.set(node.id(), obj);
1946 }
1947
1948 return obj;
1949 };
1950
1951 var getNodeFromTo = function getNodeFromTo(to) {
1952 return (string(to) ? cy.$(to) : to)[0];
1953 };
1954
1955 var distanceTo = function distanceTo(to) {
1956 return getInfo(getNodeFromTo(to)).dist;
1957 };
1958
1959 var pathTo = function pathTo(to) {
1960 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1961 var end = getNodeFromTo(to);
1962 var path = [];
1963 var node = end;
1964
1965 for (;;) {
1966 if (node == null) {
1967 return _this.spawn();
1968 }
1969
1970 var _getInfo = getInfo(node),
1971 edge = _getInfo.edge,
1972 pred = _getInfo.pred;
1973
1974 path.unshift(node[0]);
1975
1976 if (node.same(thisStart) && path.length > 0) {
1977 break;
1978 }
1979
1980 if (edge != null) {
1981 path.unshift(edge);
1982 }
1983
1984 node = pred;
1985 }
1986
1987 return eles.spawn(path);
1988 }; // Initializations { dist, pred, edge }
1989
1990
1991 for (var i = 0; i < numNodes; i++) {
1992 var node = nodes[i];
1993 var info = getInfo(node);
1994
1995 if (node.same(root)) {
1996 info.dist = 0;
1997 } else {
1998 info.dist = Infinity;
1999 }
2000
2001 info.pred = null;
2002 info.edge = null;
2003 } // Edges relaxation
2004
2005
2006 var replacedEdge = false;
2007
2008 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2009 var dist = info1.dist + weight;
2010
2011 if (dist < info2.dist && !edge.same(info1.edge)) {
2012 info2.dist = dist;
2013 info2.pred = node1;
2014 info2.edge = edge;
2015 replacedEdge = true;
2016 }
2017 };
2018
2019 for (var _i = 1; _i < numNodes; _i++) {
2020 replacedEdge = false;
2021
2022 for (var e = 0; e < numEdges; e++) {
2023 var edge = edges[e];
2024 var src = edge.source();
2025 var tgt = edge.target();
2026
2027 var _weight = weightFn(edge);
2028
2029 var srcInfo = getInfo(src);
2030 var tgtInfo = getInfo(tgt);
2031 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2032
2033 if (!directed) {
2034 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2035 }
2036 }
2037
2038 if (!replacedEdge) {
2039 break;
2040 }
2041 }
2042
2043 if (replacedEdge) {
2044 // Check for negative weight cycles
2045 for (var _e = 0; _e < numEdges; _e++) {
2046 var _edge = edges[_e];
2047
2048 var _src = _edge.source();
2049
2050 var _tgt = _edge.target();
2051
2052 var _weight2 = weightFn(_edge);
2053
2054 var srcDist = getInfo(_src).dist;
2055 var tgtDist = getInfo(_tgt).dist;
2056
2057 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2058 warn('Graph contains a negative weight cycle for Bellman-Ford');
2059 hasNegativeWeightCycle = true;
2060 break;
2061 }
2062 }
2063 }
2064
2065 return {
2066 distanceTo: distanceTo,
2067 pathTo: pathTo,
2068 hasNegativeWeightCycle: hasNegativeWeightCycle,
2069 negativeWeightCycles: negativeWeightCycles
2070 };
2071 } // bellmanFord
2072
2073}; // elesfn
2074
2075var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2076// Updates the remaining edge lists
2077// Receives as a paramater the edge which causes the collapse
2078
2079var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2080 if (remainingEdges.length === 0) {
2081 error("Karger-Stein must be run on a connected (sub)graph");
2082 }
2083
2084 var edgeInfo = remainingEdges[edgeIndex];
2085 var sourceIn = edgeInfo[1];
2086 var targetIn = edgeInfo[2];
2087 var partition1 = nodeMap[sourceIn];
2088 var partition2 = nodeMap[targetIn];
2089 var newEdges = remainingEdges; // re-use array
2090 // Delete all edges between partition1 and partition2
2091
2092 for (var i = newEdges.length - 1; i >= 0; i--) {
2093 var edge = newEdges[i];
2094 var src = edge[1];
2095 var tgt = edge[2];
2096
2097 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2098 newEdges.splice(i, 1);
2099 }
2100 } // All edges pointing to partition2 should now point to partition1
2101
2102
2103 for (var _i = 0; _i < newEdges.length; _i++) {
2104 var _edge = newEdges[_i];
2105
2106 if (_edge[1] === partition2) {
2107 // Check source
2108 newEdges[_i] = _edge.slice(); // copy
2109
2110 newEdges[_i][1] = partition1;
2111 } else if (_edge[2] === partition2) {
2112 // Check target
2113 newEdges[_i] = _edge.slice(); // copy
2114
2115 newEdges[_i][2] = partition1;
2116 }
2117 } // Move all nodes from partition2 to partition1
2118
2119
2120 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2121 if (nodeMap[_i2] === partition2) {
2122 nodeMap[_i2] = partition1;
2123 }
2124 }
2125
2126 return newEdges;
2127}; // Contracts a graph until we reach a certain number of meta nodes
2128
2129
2130var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2131 while (size > sizeLimit) {
2132 // Choose an edge randomly
2133 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2134
2135 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2136 size--;
2137 }
2138
2139 return remainingEdges;
2140};
2141
2142var elesfn$6 = {
2143 // Computes the minimum cut of an undirected graph
2144 // Returns the correct answer with high probability
2145 kargerStein: function kargerStein() {
2146 var _this = this;
2147
2148 var _this$byGroup = this.byGroup(),
2149 nodes = _this$byGroup.nodes,
2150 edges = _this$byGroup.edges;
2151
2152 edges.unmergeBy(function (edge) {
2153 return edge.isLoop();
2154 });
2155 var numNodes = nodes.length;
2156 var numEdges = edges.length;
2157 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2158 var stopSize = Math.floor(numNodes / sqrt2);
2159
2160 if (numNodes < 2) {
2161 error('At least 2 nodes are required for Karger-Stein algorithm');
2162 return undefined;
2163 } // Now store edge destination as indexes
2164 // Format for each edge (edge index, source node index, target node index)
2165
2166
2167 var edgeIndexes = [];
2168
2169 for (var i = 0; i < numEdges; i++) {
2170 var e = edges[i];
2171 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2172 } // We will store the best cut found here
2173
2174
2175 var minCutSize = Infinity;
2176 var minCutEdgeIndexes = [];
2177 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2178
2179 var metaNodeMap = new Array(numNodes);
2180 var metaNodeMap2 = new Array(numNodes);
2181
2182 var copyNodesMap = function copyNodesMap(from, to) {
2183 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2184 to[_i3] = from[_i3];
2185 }
2186 }; // Main loop
2187
2188
2189 for (var iter = 0; iter <= numIter; iter++) {
2190 // Reset meta node partition
2191 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2192 metaNodeMap[_i4] = _i4;
2193 } // Contract until stop point (stopSize nodes)
2194
2195
2196 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2197 var edgesState2 = edgesState.slice(); // copy
2198 // Create a copy of the colapsed nodes state
2199
2200 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2201
2202 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2203 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2204
2205 if (res1.length <= res2.length && res1.length < minCutSize) {
2206 minCutSize = res1.length;
2207 minCutEdgeIndexes = res1;
2208 copyNodesMap(metaNodeMap, minCutNodeMap);
2209 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2210 minCutSize = res2.length;
2211 minCutEdgeIndexes = res2;
2212 copyNodesMap(metaNodeMap2, minCutNodeMap);
2213 }
2214 } // end of main loop
2215 // Construct result
2216
2217
2218 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2219 return edges[e[0]];
2220 }));
2221 var partition1 = this.spawn();
2222 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2223
2224 var witnessNodePartition = minCutNodeMap[0];
2225
2226 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2227 var partitionId = minCutNodeMap[_i5];
2228 var node = nodes[_i5];
2229
2230 if (partitionId === witnessNodePartition) {
2231 partition1.merge(node);
2232 } else {
2233 partition2.merge(node);
2234 }
2235 } // construct components corresponding to each disjoint subset of nodes
2236
2237
2238 var constructComponent = function constructComponent(subset) {
2239 var component = _this.spawn();
2240
2241 subset.forEach(function (node) {
2242 component.merge(node);
2243 node.connectedEdges().forEach(function (edge) {
2244 // ensure edge is within calling collection and edge is not in cut
2245 if (_this.contains(edge) && !cut.contains(edge)) {
2246 component.merge(edge);
2247 }
2248 });
2249 });
2250 return component;
2251 };
2252
2253 var components = [constructComponent(partition1), constructComponent(partition2)];
2254 var ret = {
2255 cut: cut,
2256 components: components,
2257 // n.b. partitions are included to be compatible with the old api spec
2258 // (could be removed in a future major version)
2259 partition1: partition1,
2260 partition2: partition2
2261 };
2262 return ret;
2263 }
2264}; // elesfn
2265
2266var copyPosition = function copyPosition(p) {
2267 return {
2268 x: p.x,
2269 y: p.y
2270 };
2271};
2272var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2273 return {
2274 x: p.x * zoom + pan.x,
2275 y: p.y * zoom + pan.y
2276 };
2277};
2278var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2279 return {
2280 x: (p.x - pan.x) / zoom,
2281 y: (p.y - pan.y) / zoom
2282 };
2283};
2284var array2point = function array2point(arr) {
2285 return {
2286 x: arr[0],
2287 y: arr[1]
2288 };
2289};
2290var min = function min(arr) {
2291 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2292 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2293 var min = Infinity;
2294
2295 for (var i = begin; i < end; i++) {
2296 var val = arr[i];
2297
2298 if (isFinite(val)) {
2299 min = Math.min(val, min);
2300 }
2301 }
2302
2303 return min;
2304};
2305var max = function max(arr) {
2306 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2307 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2308 var max = -Infinity;
2309
2310 for (var i = begin; i < end; i++) {
2311 var val = arr[i];
2312
2313 if (isFinite(val)) {
2314 max = Math.max(val, max);
2315 }
2316 }
2317
2318 return max;
2319};
2320var mean = function mean(arr) {
2321 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2322 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2323 var total = 0;
2324 var n = 0;
2325
2326 for (var i = begin; i < end; i++) {
2327 var val = arr[i];
2328
2329 if (isFinite(val)) {
2330 total += val;
2331 n++;
2332 }
2333 }
2334
2335 return total / n;
2336};
2337var median = function median(arr) {
2338 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2339 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2340 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2341 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2342 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2343
2344 if (copy) {
2345 arr = arr.slice(begin, end);
2346 } else {
2347 if (end < arr.length) {
2348 arr.splice(end, arr.length - end);
2349 }
2350
2351 if (begin > 0) {
2352 arr.splice(0, begin);
2353 }
2354 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2355
2356
2357 var off = 0; // offset from non-finite values
2358
2359 for (var i = arr.length - 1; i >= 0; i--) {
2360 var v = arr[i];
2361
2362 if (includeHoles) {
2363 if (!isFinite(v)) {
2364 arr[i] = -Infinity;
2365 off++;
2366 }
2367 } else {
2368 // just remove it if we don't want to consider holes
2369 arr.splice(i, 1);
2370 }
2371 }
2372
2373 if (sort) {
2374 arr.sort(function (a, b) {
2375 return a - b;
2376 }); // requires copy = true if you don't want to change the orig
2377 }
2378
2379 var len = arr.length;
2380 var mid = Math.floor(len / 2);
2381
2382 if (len % 2 !== 0) {
2383 return arr[mid + 1 + off];
2384 } else {
2385 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2386 }
2387};
2388var deg2rad = function deg2rad(deg) {
2389 return Math.PI * deg / 180;
2390};
2391var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2392 return Math.atan2(dispY, dispX) - Math.PI / 2;
2393};
2394var log2 = Math.log2 || function (n) {
2395 return Math.log(n) / Math.log(2);
2396};
2397var signum = function signum(x) {
2398 if (x > 0) {
2399 return 1;
2400 } else if (x < 0) {
2401 return -1;
2402 } else {
2403 return 0;
2404 }
2405};
2406var dist = function dist(p1, p2) {
2407 return Math.sqrt(sqdist(p1, p2));
2408};
2409var sqdist = function sqdist(p1, p2) {
2410 var dx = p2.x - p1.x;
2411 var dy = p2.y - p1.y;
2412 return dx * dx + dy * dy;
2413};
2414var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2415 var length = v.length; // First, get sum of all elements
2416
2417 var total = 0;
2418
2419 for (var i = 0; i < length; i++) {
2420 total += v[i];
2421 } // Now, divide each by the sum of all elements
2422
2423
2424 for (var _i = 0; _i < length; _i++) {
2425 v[_i] = v[_i] / total;
2426 }
2427
2428 return v;
2429};
2430
2431var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2432 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2433};
2434var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2435 return {
2436 x: qbezierAt(p0.x, p1.x, p2.x, t),
2437 y: qbezierAt(p0.y, p1.y, p2.y, t)
2438 };
2439};
2440var lineAt = function lineAt(p0, p1, t, d) {
2441 var vec = {
2442 x: p1.x - p0.x,
2443 y: p1.y - p0.y
2444 };
2445 var vecDist = dist(p0, p1);
2446 var normVec = {
2447 x: vec.x / vecDist,
2448 y: vec.y / vecDist
2449 };
2450 t = t == null ? 0 : t;
2451 d = d != null ? d : t * vecDist;
2452 return {
2453 x: p0.x + normVec.x * d,
2454 y: p0.y + normVec.y * d
2455 };
2456};
2457var bound = function bound(min, val, max) {
2458 return Math.max(min, Math.min(max, val));
2459}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2460
2461var makeBoundingBox = function makeBoundingBox(bb) {
2462 if (bb == null) {
2463 return {
2464 x1: Infinity,
2465 y1: Infinity,
2466 x2: -Infinity,
2467 y2: -Infinity,
2468 w: 0,
2469 h: 0
2470 };
2471 } else if (bb.x1 != null && bb.y1 != null) {
2472 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2473 return {
2474 x1: bb.x1,
2475 y1: bb.y1,
2476 x2: bb.x2,
2477 y2: bb.y2,
2478 w: bb.x2 - bb.x1,
2479 h: bb.y2 - bb.y1
2480 };
2481 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2482 return {
2483 x1: bb.x1,
2484 y1: bb.y1,
2485 x2: bb.x1 + bb.w,
2486 y2: bb.y1 + bb.h,
2487 w: bb.w,
2488 h: bb.h
2489 };
2490 }
2491 }
2492};
2493var copyBoundingBox = function copyBoundingBox(bb) {
2494 return {
2495 x1: bb.x1,
2496 x2: bb.x2,
2497 w: bb.w,
2498 y1: bb.y1,
2499 y2: bb.y2,
2500 h: bb.h
2501 };
2502};
2503var clearBoundingBox = function clearBoundingBox(bb) {
2504 bb.x1 = Infinity;
2505 bb.y1 = Infinity;
2506 bb.x2 = -Infinity;
2507 bb.y2 = -Infinity;
2508 bb.w = 0;
2509 bb.h = 0;
2510};
2511var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2512 // update bb1 with bb2 bounds
2513 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2514 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2515 bb1.w = bb1.x2 - bb1.x1;
2516 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2517 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2518 bb1.h = bb1.y2 - bb1.y1;
2519};
2520var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2521 bb.x1 = Math.min(bb.x1, x);
2522 bb.x2 = Math.max(bb.x2, x);
2523 bb.w = bb.x2 - bb.x1;
2524 bb.y1 = Math.min(bb.y1, y);
2525 bb.y2 = Math.max(bb.y2, y);
2526 bb.h = bb.y2 - bb.y1;
2527};
2528var expandBoundingBox = function expandBoundingBox(bb) {
2529 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2530 bb.x1 -= padding;
2531 bb.x2 += padding;
2532 bb.y1 -= padding;
2533 bb.y2 += padding;
2534 bb.w = bb.x2 - bb.x1;
2535 bb.h = bb.y2 - bb.y1;
2536 return bb;
2537};
2538var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2539 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2540 var top, right, bottom, left;
2541
2542 if (padding.length === 1) {
2543 top = right = bottom = left = padding[0];
2544 } else if (padding.length === 2) {
2545 top = bottom = padding[0];
2546 left = right = padding[1];
2547 } else if (padding.length === 4) {
2548 var _padding = _slicedToArray(padding, 4);
2549
2550 top = _padding[0];
2551 right = _padding[1];
2552 bottom = _padding[2];
2553 left = _padding[3];
2554 }
2555
2556 bb.x1 -= left;
2557 bb.x2 += right;
2558 bb.y1 -= top;
2559 bb.y2 += bottom;
2560 bb.w = bb.x2 - bb.x1;
2561 bb.h = bb.y2 - bb.y1;
2562 return bb;
2563};
2564
2565var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2566 bb1.x1 = bb2.x1;
2567 bb1.y1 = bb2.y1;
2568 bb1.x2 = bb2.x2;
2569 bb1.y2 = bb2.y2;
2570 bb1.w = bb1.x2 - bb1.x1;
2571 bb1.h = bb1.y2 - bb1.y1;
2572};
2573var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2574 // case: one bb to right of other
2575 if (bb1.x1 > bb2.x2) {
2576 return false;
2577 }
2578
2579 if (bb2.x1 > bb1.x2) {
2580 return false;
2581 } // case: one bb to left of other
2582
2583
2584 if (bb1.x2 < bb2.x1) {
2585 return false;
2586 }
2587
2588 if (bb2.x2 < bb1.x1) {
2589 return false;
2590 } // case: one bb above other
2591
2592
2593 if (bb1.y2 < bb2.y1) {
2594 return false;
2595 }
2596
2597 if (bb2.y2 < bb1.y1) {
2598 return false;
2599 } // case: one bb below other
2600
2601
2602 if (bb1.y1 > bb2.y2) {
2603 return false;
2604 }
2605
2606 if (bb2.y1 > bb1.y2) {
2607 return false;
2608 } // otherwise, must have some overlap
2609
2610
2611 return true;
2612};
2613var inBoundingBox = function inBoundingBox(bb, x, y) {
2614 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2615};
2616var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2617 return inBoundingBox(bb, pt.x, pt.y);
2618};
2619var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2620 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2621};
2622var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2623 var cornerRadius = getRoundRectangleRadius(width, height);
2624 var halfWidth = width / 2;
2625 var halfHeight = height / 2; // Check intersections with straight line segments
2626
2627 var straightLineIntersections; // Top segment, left to right
2628
2629 {
2630 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2631 var topStartY = nodeY - halfHeight - padding;
2632 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2633 var topEndY = topStartY;
2634 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2635
2636 if (straightLineIntersections.length > 0) {
2637 return straightLineIntersections;
2638 }
2639 } // Right segment, top to bottom
2640
2641 {
2642 var rightStartX = nodeX + halfWidth + padding;
2643 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2644 var rightEndX = rightStartX;
2645 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2646 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2647
2648 if (straightLineIntersections.length > 0) {
2649 return straightLineIntersections;
2650 }
2651 } // Bottom segment, left to right
2652
2653 {
2654 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2655 var bottomStartY = nodeY + halfHeight + padding;
2656 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2657 var bottomEndY = bottomStartY;
2658 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2659
2660 if (straightLineIntersections.length > 0) {
2661 return straightLineIntersections;
2662 }
2663 } // Left segment, top to bottom
2664
2665 {
2666 var leftStartX = nodeX - halfWidth - padding;
2667 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2668 var leftEndX = leftStartX;
2669 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2670 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2671
2672 if (straightLineIntersections.length > 0) {
2673 return straightLineIntersections;
2674 }
2675 } // Check intersections with arc segments
2676
2677 var arcIntersections; // Top Left
2678
2679 {
2680 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2681 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2682 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2683
2684 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2685 return [arcIntersections[0], arcIntersections[1]];
2686 }
2687 } // Top Right
2688
2689 {
2690 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2691 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2692 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2693
2694 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2695 return [arcIntersections[0], arcIntersections[1]];
2696 }
2697 } // Bottom Right
2698
2699 {
2700 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2701 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2702 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2703
2704 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2705 return [arcIntersections[0], arcIntersections[1]];
2706 }
2707 } // Bottom Left
2708
2709 {
2710 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2711 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2712 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2713
2714 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2715 return [arcIntersections[0], arcIntersections[1]];
2716 }
2717 }
2718 return []; // if nothing
2719};
2720var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2721 var t = tolerance;
2722 var x1 = Math.min(lx1, lx2);
2723 var x2 = Math.max(lx1, lx2);
2724 var y1 = Math.min(ly1, ly2);
2725 var y2 = Math.max(ly1, ly2);
2726 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2727};
2728var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2729 var bb = {
2730 x1: Math.min(x1, x3, x2) - tolerance,
2731 x2: Math.max(x1, x3, x2) + tolerance,
2732 y1: Math.min(y1, y3, y2) - tolerance,
2733 y2: Math.max(y1, y3, y2) + tolerance
2734 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2735
2736 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2737 // console.log('bezier out of rough bb')
2738 return false;
2739 } else {
2740 // console.log('do more expensive check');
2741 return true;
2742 }
2743};
2744var solveQuadratic = function solveQuadratic(a, b, c, val) {
2745 c -= val;
2746 var r = b * b - 4 * a * c;
2747
2748 if (r < 0) {
2749 return [];
2750 }
2751
2752 var sqrtR = Math.sqrt(r);
2753 var denom = 2 * a;
2754 var root1 = (-b + sqrtR) / denom;
2755 var root2 = (-b - sqrtR) / denom;
2756 return [root1, root2];
2757};
2758var solveCubic = function solveCubic(a, b, c, d, result) {
2759 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2760 // r is the real component, i is the imaginary component
2761 // An implementation of the Cardano method from the year 1545
2762 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2763 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2764
2765 if (a === 0) {
2766 a = epsilon;
2767 }
2768
2769 b /= a;
2770 c /= a;
2771 d /= a;
2772 var discriminant, q, r, dum1, s, t, term1, r13;
2773 q = (3.0 * c - b * b) / 9.0;
2774 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2775 r /= 54.0;
2776 discriminant = q * q * q + r * r;
2777 result[1] = 0;
2778 term1 = b / 3.0;
2779
2780 if (discriminant > 0) {
2781 s = r + Math.sqrt(discriminant);
2782 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2783 t = r - Math.sqrt(discriminant);
2784 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2785 result[0] = -term1 + s + t;
2786 term1 += (s + t) / 2.0;
2787 result[4] = result[2] = -term1;
2788 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2789 result[3] = term1;
2790 result[5] = -term1;
2791 return;
2792 }
2793
2794 result[5] = result[3] = 0;
2795
2796 if (discriminant === 0) {
2797 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2798 result[0] = -term1 + 2.0 * r13;
2799 result[4] = result[2] = -(r13 + term1);
2800 return;
2801 }
2802
2803 q = -q;
2804 dum1 = q * q * q;
2805 dum1 = Math.acos(r / Math.sqrt(dum1));
2806 r13 = 2.0 * Math.sqrt(q);
2807 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2808 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2809 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2810 return;
2811};
2812var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2813 // Find minimum distance by using the minimum of the distance
2814 // function between the given point and the curve
2815 // This gives the coefficients of the resulting cubic equation
2816 // whose roots tell us where a possible minimum is
2817 // (Coefficients are divided by 4)
2818 var a = 1.0 * x1 * x1 - 4 * x1 * x2 + 2 * x1 * x3 + 4 * x2 * x2 - 4 * x2 * x3 + x3 * x3 + y1 * y1 - 4 * y1 * y2 + 2 * y1 * y3 + 4 * y2 * y2 - 4 * y2 * y3 + y3 * y3;
2819 var b = 1.0 * 9 * x1 * x2 - 3 * x1 * x1 - 3 * x1 * x3 - 6 * x2 * x2 + 3 * x2 * x3 + 9 * y1 * y2 - 3 * y1 * y1 - 3 * y1 * y3 - 6 * y2 * y2 + 3 * y2 * y3;
2820 var c = 1.0 * 3 * x1 * x1 - 6 * x1 * x2 + x1 * x3 - x1 * x + 2 * x2 * x2 + 2 * x2 * x - x3 * x + 3 * y1 * y1 - 6 * y1 * y2 + y1 * y3 - y1 * y + 2 * y2 * y2 + 2 * y2 * y - y3 * y;
2821 var d = 1.0 * x1 * x2 - x1 * x1 + x1 * x - x2 * x + y1 * y2 - y1 * y1 + y1 * y - y2 * y; // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a);
2822
2823 var roots = []; // Use the cubic solving algorithm
2824
2825 solveCubic(a, b, c, d, roots);
2826 var zeroThreshold = 0.0000001;
2827 var params = [];
2828
2829 for (var index = 0; index < 6; index += 2) {
2830 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2831 params.push(roots[index]);
2832 }
2833 }
2834
2835 params.push(1.0);
2836 params.push(0.0);
2837 var minDistanceSquared = -1;
2838 var curX, curY, distSquared;
2839
2840 for (var i = 0; i < params.length; i++) {
2841 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2842 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2843 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2844
2845 if (minDistanceSquared >= 0) {
2846 if (distSquared < minDistanceSquared) {
2847 minDistanceSquared = distSquared;
2848 }
2849 } else {
2850 minDistanceSquared = distSquared;
2851 }
2852 }
2853
2854 return minDistanceSquared;
2855};
2856var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2857 var offset = [x - x1, y - y1];
2858 var line = [x2 - x1, y2 - y1];
2859 var lineSq = line[0] * line[0] + line[1] * line[1];
2860 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2861 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2862 var adjSq = dotProduct * dotProduct / lineSq;
2863
2864 if (dotProduct < 0) {
2865 return hypSq;
2866 }
2867
2868 if (adjSq > lineSq) {
2869 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2870 }
2871
2872 return hypSq - adjSq;
2873};
2874var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2875 var x1, y1, x2, y2;
2876 var y3; // Intersect with vertical line through (x, y)
2877
2878 var up = 0; // let down = 0;
2879
2880 for (var i = 0; i < points.length / 2; i++) {
2881 x1 = points[i * 2];
2882 y1 = points[i * 2 + 1];
2883
2884 if (i + 1 < points.length / 2) {
2885 x2 = points[(i + 1) * 2];
2886 y2 = points[(i + 1) * 2 + 1];
2887 } else {
2888 x2 = points[(i + 1 - points.length / 2) * 2];
2889 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2890 }
2891
2892 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2893 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2894
2895 if (y3 > y) {
2896 up++;
2897 } // if( y3 < y ){
2898 // down++;
2899 // }
2900
2901 } else {
2902 continue;
2903 }
2904 }
2905
2906 if (up % 2 === 0) {
2907 return false;
2908 } else {
2909 return true;
2910 }
2911};
2912var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2913 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2914
2915 var angle;
2916
2917 if (direction[0] != null) {
2918 angle = Math.atan(direction[1] / direction[0]);
2919
2920 if (direction[0] < 0) {
2921 angle = angle + Math.PI / 2;
2922 } else {
2923 angle = -angle - Math.PI / 2;
2924 }
2925 } else {
2926 angle = direction;
2927 }
2928
2929 var cos = Math.cos(-angle);
2930 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2931
2932 for (var i = 0; i < transformedPoints.length / 2; i++) {
2933 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2934 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2935 transformedPoints[i * 2] += centerX;
2936 transformedPoints[i * 2 + 1] += centerY;
2937 }
2938
2939 var points;
2940
2941 if (padding > 0) {
2942 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2943 points = joinLines(expandedLineSet);
2944 } else {
2945 points = transformedPoints;
2946 }
2947
2948 return pointInsidePolygonPoints(x, y, points);
2949};
2950var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
2951 var cutPolygonPoints = new Array(basePoints.length);
2952 var halfW = width / 2;
2953 var halfH = height / 2;
2954 var cornerRadius = getRoundPolygonRadius(width, height);
2955 var squaredCornerRadius = cornerRadius * cornerRadius;
2956
2957 for (var i = 0; i < basePoints.length / 4; i++) {
2958 var sourceUv = void 0,
2959 destUv = void 0;
2960
2961 if (i === 0) {
2962 sourceUv = basePoints.length - 2;
2963 } else {
2964 sourceUv = i * 4 - 2;
2965 }
2966
2967 destUv = i * 4 + 2;
2968 var px = centerX + halfW * basePoints[i * 4];
2969 var py = centerY + halfH * basePoints[i * 4 + 1];
2970 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
2971 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
2972 var cp0x = px - offset * basePoints[sourceUv];
2973 var cp0y = py - offset * basePoints[sourceUv + 1];
2974 var cp1x = px + offset * basePoints[destUv];
2975 var cp1y = py + offset * basePoints[destUv + 1];
2976 cutPolygonPoints[i * 4] = cp0x;
2977 cutPolygonPoints[i * 4 + 1] = cp0y;
2978 cutPolygonPoints[i * 4 + 2] = cp1x;
2979 cutPolygonPoints[i * 4 + 3] = cp1y;
2980 var orthx = basePoints[sourceUv + 1];
2981 var orthy = -basePoints[sourceUv];
2982 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
2983
2984 if (cosAlpha < 0) {
2985 orthx *= -1;
2986 orthy *= -1;
2987 }
2988
2989 var cx = cp0x + orthx * cornerRadius;
2990 var cy = cp0y + orthy * cornerRadius;
2991 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
2992
2993 if (squaredDistance <= squaredCornerRadius) {
2994 return true;
2995 }
2996 }
2997
2998 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
2999};
3000var joinLines = function joinLines(lineSet) {
3001 var vertices = new Array(lineSet.length / 2);
3002 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3003 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3004
3005 for (var i = 0; i < lineSet.length / 4; i++) {
3006 currentLineStartX = lineSet[i * 4];
3007 currentLineStartY = lineSet[i * 4 + 1];
3008 currentLineEndX = lineSet[i * 4 + 2];
3009 currentLineEndY = lineSet[i * 4 + 3];
3010
3011 if (i < lineSet.length / 4 - 1) {
3012 nextLineStartX = lineSet[(i + 1) * 4];
3013 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3014 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3015 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3016 } else {
3017 nextLineStartX = lineSet[0];
3018 nextLineStartY = lineSet[1];
3019 nextLineEndX = lineSet[2];
3020 nextLineEndY = lineSet[3];
3021 }
3022
3023 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3024 vertices[i * 2] = intersection[0];
3025 vertices[i * 2 + 1] = intersection[1];
3026 }
3027
3028 return vertices;
3029};
3030var expandPolygon = function expandPolygon(points, pad) {
3031 var expandedLineSet = new Array(points.length * 2);
3032 var currentPointX, currentPointY, nextPointX, nextPointY;
3033
3034 for (var i = 0; i < points.length / 2; i++) {
3035 currentPointX = points[i * 2];
3036 currentPointY = points[i * 2 + 1];
3037
3038 if (i < points.length / 2 - 1) {
3039 nextPointX = points[(i + 1) * 2];
3040 nextPointY = points[(i + 1) * 2 + 1];
3041 } else {
3042 nextPointX = points[0];
3043 nextPointY = points[1];
3044 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3045 // Assume CCW polygon winding
3046
3047
3048 var offsetX = nextPointY - currentPointY;
3049 var offsetY = -(nextPointX - currentPointX); // Normalize
3050
3051 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3052 var normalizedOffsetX = offsetX / offsetLength;
3053 var normalizedOffsetY = offsetY / offsetLength;
3054 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3055 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3056 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3057 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3058 }
3059
3060 return expandedLineSet;
3061};
3062var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3063 var dispX = centerX - x;
3064 var dispY = centerY - y;
3065 dispX /= ellipseWradius;
3066 dispY /= ellipseHradius;
3067 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3068 var newLength = len - 1;
3069
3070 if (newLength < 0) {
3071 return [];
3072 }
3073
3074 var lenProportion = newLength / len;
3075 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3076};
3077var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3078 x -= centerX;
3079 y -= centerY;
3080 x /= width / 2 + padding;
3081 y /= height / 2 + padding;
3082 return x * x + y * y <= 1;
3083}; // Returns intersections of increasing distance from line's start point
3084
3085var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3086 // Calculate d, direction vector of line
3087 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3088
3089 var f = [x1 - centerX, y1 - centerY];
3090 var a = d[0] * d[0] + d[1] * d[1];
3091 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3092 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3093 var discriminant = b * b - 4 * a * c;
3094
3095 if (discriminant < 0) {
3096 return [];
3097 }
3098
3099 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3100 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3101 var tMin = Math.min(t1, t2);
3102 var tMax = Math.max(t1, t2);
3103 var inRangeParams = [];
3104
3105 if (tMin >= 0 && tMin <= 1) {
3106 inRangeParams.push(tMin);
3107 }
3108
3109 if (tMax >= 0 && tMax <= 1) {
3110 inRangeParams.push(tMax);
3111 }
3112
3113 if (inRangeParams.length === 0) {
3114 return [];
3115 }
3116
3117 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3118 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3119
3120 if (inRangeParams.length > 1) {
3121 if (inRangeParams[0] == inRangeParams[1]) {
3122 return [nearIntersectionX, nearIntersectionY];
3123 } else {
3124 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3125 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3126 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3127 }
3128 } else {
3129 return [nearIntersectionX, nearIntersectionY];
3130 }
3131};
3132var midOfThree = function midOfThree(a, b, c) {
3133 if (b <= a && a <= c || c <= a && a <= b) {
3134 return a;
3135 } else if (a <= b && b <= c || c <= b && b <= a) {
3136 return b;
3137 } else {
3138 return c;
3139 }
3140}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3141
3142var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3143 var dx13 = x1 - x3;
3144 var dx21 = x2 - x1;
3145 var dx43 = x4 - x3;
3146 var dy13 = y1 - y3;
3147 var dy21 = y2 - y1;
3148 var dy43 = y4 - y3;
3149 var ua_t = dx43 * dy13 - dy43 * dx13;
3150 var ub_t = dx21 * dy13 - dy21 * dx13;
3151 var u_b = dy43 * dx21 - dx43 * dy21;
3152
3153 if (u_b !== 0) {
3154 var ua = ua_t / u_b;
3155 var ub = ub_t / u_b;
3156 var flptThreshold = 0.001;
3157
3158 var _min = 0 - flptThreshold;
3159
3160 var _max = 1 + flptThreshold;
3161
3162 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3163 return [x1 + ua * dx21, y1 + ua * dy21];
3164 } else {
3165 if (!infiniteLines) {
3166 return [];
3167 } else {
3168 return [x1 + ua * dx21, y1 + ua * dy21];
3169 }
3170 }
3171 } else {
3172 if (ua_t === 0 || ub_t === 0) {
3173 // Parallel, coincident lines. Check if overlap
3174 // Check endpoint of second line
3175 if (midOfThree(x1, x2, x4) === x4) {
3176 return [x4, y4];
3177 } // Check start point of second line
3178
3179
3180 if (midOfThree(x1, x2, x3) === x3) {
3181 return [x3, y3];
3182 } // Endpoint of first line
3183
3184
3185 if (midOfThree(x3, x4, x2) === x2) {
3186 return [x2, y2];
3187 }
3188
3189 return [];
3190 } else {
3191 // Parallel, non-coincident
3192 return [];
3193 }
3194 }
3195}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3196// intersect a node polygon (pts transformed)
3197//
3198// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3199// intersect the points (no transform)
3200
3201var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3202 var intersections = [];
3203 var intersection;
3204 var transformedPoints = new Array(basePoints.length);
3205 var doTransform = true;
3206
3207 if (width == null) {
3208 doTransform = false;
3209 }
3210
3211 var points;
3212
3213 if (doTransform) {
3214 for (var i = 0; i < transformedPoints.length / 2; i++) {
3215 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3216 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3217 }
3218
3219 if (padding > 0) {
3220 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3221 points = joinLines(expandedLineSet);
3222 } else {
3223 points = transformedPoints;
3224 }
3225 } else {
3226 points = basePoints;
3227 }
3228
3229 var currentX, currentY, nextX, nextY;
3230
3231 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3232 currentX = points[_i2 * 2];
3233 currentY = points[_i2 * 2 + 1];
3234
3235 if (_i2 < points.length / 2 - 1) {
3236 nextX = points[(_i2 + 1) * 2];
3237 nextY = points[(_i2 + 1) * 2 + 1];
3238 } else {
3239 nextX = points[0];
3240 nextY = points[1];
3241 }
3242
3243 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3244
3245 if (intersection.length !== 0) {
3246 intersections.push(intersection[0], intersection[1]);
3247 }
3248 }
3249
3250 return intersections;
3251};
3252var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3253 var intersections = [];
3254 var intersection;
3255 var lines = new Array(basePoints.length);
3256 var halfW = width / 2;
3257 var halfH = height / 2;
3258 var cornerRadius = getRoundPolygonRadius(width, height);
3259
3260 for (var i = 0; i < basePoints.length / 4; i++) {
3261 var sourceUv = void 0,
3262 destUv = void 0;
3263
3264 if (i === 0) {
3265 sourceUv = basePoints.length - 2;
3266 } else {
3267 sourceUv = i * 4 - 2;
3268 }
3269
3270 destUv = i * 4 + 2;
3271 var px = centerX + halfW * basePoints[i * 4];
3272 var py = centerY + halfH * basePoints[i * 4 + 1];
3273 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3274 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3275 var cp0x = px - offset * basePoints[sourceUv];
3276 var cp0y = py - offset * basePoints[sourceUv + 1];
3277 var cp1x = px + offset * basePoints[destUv];
3278 var cp1y = py + offset * basePoints[destUv + 1];
3279
3280 if (i === 0) {
3281 lines[basePoints.length - 2] = cp0x;
3282 lines[basePoints.length - 1] = cp0y;
3283 } else {
3284 lines[i * 4 - 2] = cp0x;
3285 lines[i * 4 - 1] = cp0y;
3286 }
3287
3288 lines[i * 4] = cp1x;
3289 lines[i * 4 + 1] = cp1y;
3290 var orthx = basePoints[sourceUv + 1];
3291 var orthy = -basePoints[sourceUv];
3292 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3293
3294 if (cosAlpha < 0) {
3295 orthx *= -1;
3296 orthy *= -1;
3297 }
3298
3299 var cx = cp0x + orthx * cornerRadius;
3300 var cy = cp0y + orthy * cornerRadius;
3301 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3302
3303 if (intersection.length !== 0) {
3304 intersections.push(intersection[0], intersection[1]);
3305 }
3306 }
3307
3308 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3309 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3310
3311 if (intersection.length !== 0) {
3312 intersections.push(intersection[0], intersection[1]);
3313 }
3314 }
3315
3316 if (intersections.length > 2) {
3317 var lowestIntersection = [intersections[0], intersections[1]];
3318 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3319
3320 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3321 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3322
3323 if (squaredDistance <= lowestSquaredDistance) {
3324 lowestIntersection[0] = intersections[_i4 * 2];
3325 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3326 lowestSquaredDistance = squaredDistance;
3327 }
3328 }
3329
3330 return lowestIntersection;
3331 }
3332
3333 return intersections;
3334};
3335var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3336 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3337 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3338 var lenRatio = (length - amount) / length;
3339
3340 if (lenRatio < 0) {
3341 lenRatio = 0.00001;
3342 }
3343
3344 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3345};
3346var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3347 var points = generateUnitNgonPoints(sides, rotationRadians);
3348 points = fitPolygonToSquare(points);
3349 return points;
3350};
3351var fitPolygonToSquare = function fitPolygonToSquare(points) {
3352 var x, y;
3353 var sides = points.length / 2;
3354 var minX = Infinity,
3355 minY = Infinity,
3356 maxX = -Infinity,
3357 maxY = -Infinity;
3358
3359 for (var i = 0; i < sides; i++) {
3360 x = points[2 * i];
3361 y = points[2 * i + 1];
3362 minX = Math.min(minX, x);
3363 maxX = Math.max(maxX, x);
3364 minY = Math.min(minY, y);
3365 maxY = Math.max(maxY, y);
3366 } // stretch factors
3367
3368
3369 var sx = 2 / (maxX - minX);
3370 var sy = 2 / (maxY - minY);
3371
3372 for (var _i5 = 0; _i5 < sides; _i5++) {
3373 x = points[2 * _i5] = points[2 * _i5] * sx;
3374 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3375 minX = Math.min(minX, x);
3376 maxX = Math.max(maxX, x);
3377 minY = Math.min(minY, y);
3378 maxY = Math.max(maxY, y);
3379 }
3380
3381 if (minY < -1) {
3382 for (var _i6 = 0; _i6 < sides; _i6++) {
3383 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3384 }
3385 }
3386
3387 return points;
3388};
3389var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3390 var increment = 1.0 / sides * 2 * Math.PI;
3391 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3392 startAngle += rotationRadians;
3393 var points = new Array(sides * 2);
3394 var currentAngle;
3395
3396 for (var i = 0; i < sides; i++) {
3397 currentAngle = i * increment + startAngle;
3398 points[2 * i] = Math.cos(currentAngle); // x
3399
3400 points[2 * i + 1] = Math.sin(-currentAngle); // y
3401 }
3402
3403 return points;
3404}; // Set the default radius, unless half of width or height is smaller than default
3405
3406var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3407 return Math.min(width / 4, height / 4, 8);
3408}; // Set the default radius
3409
3410var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3411 return Math.min(width / 10, height / 10, 8);
3412};
3413var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3414 return 8;
3415};
3416var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3417 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3418}; // get curve width, height, and control point position offsets as a percentage of node height / width
3419
3420var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3421 return {
3422 heightOffset: Math.min(15, 0.05 * height),
3423 widthOffset: Math.min(100, 0.25 * width),
3424 ctrlPtOffsetPct: 0.05
3425 };
3426};
3427
3428var pageRankDefaults = defaults({
3429 dampingFactor: 0.8,
3430 precision: 0.000001,
3431 iterations: 200,
3432 weight: function weight(edge) {
3433 return 1;
3434 }
3435});
3436var elesfn$7 = {
3437 pageRank: function pageRank(options) {
3438 var _pageRankDefaults = pageRankDefaults(options),
3439 dampingFactor = _pageRankDefaults.dampingFactor,
3440 precision = _pageRankDefaults.precision,
3441 iterations = _pageRankDefaults.iterations,
3442 weight = _pageRankDefaults.weight;
3443
3444 var cy = this._private.cy;
3445
3446 var _this$byGroup = this.byGroup(),
3447 nodes = _this$byGroup.nodes,
3448 edges = _this$byGroup.edges;
3449
3450 var numNodes = nodes.length;
3451 var numNodesSqd = numNodes * numNodes;
3452 var numEdges = edges.length; // Construct transposed adjacency matrix
3453 // First lets have a zeroed matrix of the right size
3454 // We'll also keep track of the sum of each column
3455
3456 var matrix = new Array(numNodesSqd);
3457 var columnSum = new Array(numNodes);
3458 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3459
3460 for (var i = 0; i < numNodes; i++) {
3461 for (var j = 0; j < numNodes; j++) {
3462 var n = i * numNodes + j;
3463 matrix[n] = 0;
3464 }
3465
3466 columnSum[i] = 0;
3467 } // Now, process edges
3468
3469
3470 for (var _i = 0; _i < numEdges; _i++) {
3471 var edge = edges[_i];
3472 var srcId = edge.data('source');
3473 var tgtId = edge.data('target'); // Don't include loops in the matrix
3474
3475 if (srcId === tgtId) {
3476 continue;
3477 }
3478
3479 var s = nodes.indexOfId(srcId);
3480 var t = nodes.indexOfId(tgtId);
3481 var w = weight(edge);
3482
3483 var _n = t * numNodes + s; // Update matrix
3484
3485
3486 matrix[_n] += w; // Update column sum
3487
3488 columnSum[s] += w;
3489 } // Add additional probability based on damping factor
3490 // Also, take into account columns that have sum = 0
3491
3492
3493 var p = 1.0 / numNodes + additionalProb; // Shorthand
3494 // Traverse matrix, column by column
3495
3496 for (var _j = 0; _j < numNodes; _j++) {
3497 if (columnSum[_j] === 0) {
3498 // No 'links' out from node jth, assume equal probability for each possible node
3499 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3500 var _n2 = _i2 * numNodes + _j;
3501
3502 matrix[_n2] = p;
3503 }
3504 } else {
3505 // Node jth has outgoing link, compute normalized probabilities
3506 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3507 var _n3 = _i3 * numNodes + _j;
3508
3509 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3510 }
3511 }
3512 } // Compute dominant eigenvector using power method
3513
3514
3515 var eigenvector = new Array(numNodes);
3516 var temp = new Array(numNodes);
3517 var previous; // Start with a vector of all 1's
3518 // Also, initialize a null vector which will be used as shorthand
3519
3520 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3521 eigenvector[_i4] = 1;
3522 }
3523
3524 for (var iter = 0; iter < iterations; iter++) {
3525 // Temp array with all 0's
3526 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3527 temp[_i5] = 0;
3528 } // Multiply matrix with previous result
3529
3530
3531 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3532 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3533 var _n4 = _i6 * numNodes + _j2;
3534
3535 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3536 }
3537 }
3538
3539 inPlaceSumNormalize(temp);
3540 previous = eigenvector;
3541 eigenvector = temp;
3542 temp = previous;
3543 var diff = 0; // Compute difference (squared module) of both vectors
3544
3545 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3546 var delta = previous[_i7] - eigenvector[_i7];
3547 diff += delta * delta;
3548 } // If difference is less than the desired threshold, stop iterating
3549
3550
3551 if (diff < precision) {
3552 break;
3553 }
3554 } // Construct result
3555
3556
3557 var res = {
3558 rank: function rank(node) {
3559 node = cy.collection(node)[0];
3560 return eigenvector[nodes.indexOf(node)];
3561 }
3562 };
3563 return res;
3564 } // pageRank
3565
3566}; // elesfn
3567
3568var defaults$1 = defaults({
3569 root: null,
3570 weight: function weight(edge) {
3571 return 1;
3572 },
3573 directed: false,
3574 alpha: 0
3575});
3576var elesfn$8 = {
3577 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3578 options = defaults$1(options);
3579 var cy = this.cy();
3580 var nodes = this.nodes();
3581 var numNodes = nodes.length;
3582
3583 if (!options.directed) {
3584 var degrees = {};
3585 var maxDegree = 0;
3586
3587 for (var i = 0; i < numNodes; i++) {
3588 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3589
3590 options.root = node;
3591 var currDegree = this.degreeCentrality(options);
3592
3593 if (maxDegree < currDegree.degree) {
3594 maxDegree = currDegree.degree;
3595 }
3596
3597 degrees[node.id()] = currDegree.degree;
3598 }
3599
3600 return {
3601 degree: function degree(node) {
3602 if (maxDegree === 0) {
3603 return 0;
3604 }
3605
3606 if (string(node)) {
3607 // from is a selector string
3608 node = cy.filter(node);
3609 }
3610
3611 return degrees[node.id()] / maxDegree;
3612 }
3613 };
3614 } else {
3615 var indegrees = {};
3616 var outdegrees = {};
3617 var maxIndegree = 0;
3618 var maxOutdegree = 0;
3619
3620 for (var _i = 0; _i < numNodes; _i++) {
3621 var _node = nodes[_i];
3622
3623 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3624
3625
3626 options.root = _node;
3627
3628 var _currDegree = this.degreeCentrality(options);
3629
3630 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3631 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3632 indegrees[id] = _currDegree.indegree;
3633 outdegrees[id] = _currDegree.outdegree;
3634 }
3635
3636 return {
3637 indegree: function indegree(node) {
3638 if (maxIndegree == 0) {
3639 return 0;
3640 }
3641
3642 if (string(node)) {
3643 // from is a selector string
3644 node = cy.filter(node);
3645 }
3646
3647 return indegrees[node.id()] / maxIndegree;
3648 },
3649 outdegree: function outdegree(node) {
3650 if (maxOutdegree === 0) {
3651 return 0;
3652 }
3653
3654 if (string(node)) {
3655 // from is a selector string
3656 node = cy.filter(node);
3657 }
3658
3659 return outdegrees[node.id()] / maxOutdegree;
3660 }
3661 };
3662 }
3663 },
3664 // degreeCentralityNormalized
3665 // Implemented from the algorithm in Opsahl's paper
3666 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3667 // check the heading 2 "Degree"
3668 degreeCentrality: function degreeCentrality(options) {
3669 options = defaults$1(options);
3670 var cy = this.cy();
3671 var callingEles = this;
3672 var _options = options,
3673 root = _options.root,
3674 weight = _options.weight,
3675 directed = _options.directed,
3676 alpha = _options.alpha;
3677 root = cy.collection(root)[0];
3678
3679 if (!directed) {
3680 var connEdges = root.connectedEdges().intersection(callingEles);
3681 var k = connEdges.length;
3682 var s = 0; // Now, sum edge weights
3683
3684 for (var i = 0; i < connEdges.length; i++) {
3685 s += weight(connEdges[i]);
3686 }
3687
3688 return {
3689 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3690 };
3691 } else {
3692 var edges = root.connectedEdges();
3693 var incoming = edges.filter(function (edge) {
3694 return edge.target().same(root) && callingEles.has(edge);
3695 });
3696 var outgoing = edges.filter(function (edge) {
3697 return edge.source().same(root) && callingEles.has(edge);
3698 });
3699 var k_in = incoming.length;
3700 var k_out = outgoing.length;
3701 var s_in = 0;
3702 var s_out = 0; // Now, sum incoming edge weights
3703
3704 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3705 s_in += weight(incoming[_i2]);
3706 } // Now, sum outgoing edge weights
3707
3708
3709 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3710 s_out += weight(outgoing[_i3]);
3711 }
3712
3713 return {
3714 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3715 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3716 };
3717 }
3718 } // degreeCentrality
3719
3720}; // elesfn
3721// nice, short mathemathical alias
3722
3723elesfn$8.dc = elesfn$8.degreeCentrality;
3724elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3725
3726var defaults$2 = defaults({
3727 harmonic: true,
3728 weight: function weight() {
3729 return 1;
3730 },
3731 directed: false,
3732 root: null
3733});
3734var elesfn$9 = {
3735 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3736 var _defaults = defaults$2(options),
3737 harmonic = _defaults.harmonic,
3738 weight = _defaults.weight,
3739 directed = _defaults.directed;
3740
3741 var cy = this.cy();
3742 var closenesses = {};
3743 var maxCloseness = 0;
3744 var nodes = this.nodes();
3745 var fw = this.floydWarshall({
3746 weight: weight,
3747 directed: directed
3748 }); // Compute closeness for every node and find the maximum closeness
3749
3750 for (var i = 0; i < nodes.length; i++) {
3751 var currCloseness = 0;
3752 var node_i = nodes[i];
3753
3754 for (var j = 0; j < nodes.length; j++) {
3755 if (i !== j) {
3756 var d = fw.distance(node_i, nodes[j]);
3757
3758 if (harmonic) {
3759 currCloseness += 1 / d;
3760 } else {
3761 currCloseness += d;
3762 }
3763 }
3764 }
3765
3766 if (!harmonic) {
3767 currCloseness = 1 / currCloseness;
3768 }
3769
3770 if (maxCloseness < currCloseness) {
3771 maxCloseness = currCloseness;
3772 }
3773
3774 closenesses[node_i.id()] = currCloseness;
3775 }
3776
3777 return {
3778 closeness: function closeness(node) {
3779 if (maxCloseness == 0) {
3780 return 0;
3781 }
3782
3783 if (string(node)) {
3784 // from is a selector string
3785 node = cy.filter(node)[0].id();
3786 } else {
3787 // from is a node
3788 node = node.id();
3789 }
3790
3791 return closenesses[node] / maxCloseness;
3792 }
3793 };
3794 },
3795 // Implemented from pseudocode from wikipedia
3796 closenessCentrality: function closenessCentrality(options) {
3797 var _defaults2 = defaults$2(options),
3798 root = _defaults2.root,
3799 weight = _defaults2.weight,
3800 directed = _defaults2.directed,
3801 harmonic = _defaults2.harmonic;
3802
3803 root = this.filter(root)[0]; // we need distance from this node to every other node
3804
3805 var dijkstra = this.dijkstra({
3806 root: root,
3807 weight: weight,
3808 directed: directed
3809 });
3810 var totalDistance = 0;
3811 var nodes = this.nodes();
3812
3813 for (var i = 0; i < nodes.length; i++) {
3814 var n = nodes[i];
3815
3816 if (!n.same(root)) {
3817 var d = dijkstra.distanceTo(n);
3818
3819 if (harmonic) {
3820 totalDistance += 1 / d;
3821 } else {
3822 totalDistance += d;
3823 }
3824 }
3825 }
3826
3827 return harmonic ? totalDistance : 1 / totalDistance;
3828 } // closenessCentrality
3829
3830}; // elesfn
3831// nice, short mathemathical alias
3832
3833elesfn$9.cc = elesfn$9.closenessCentrality;
3834elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3835
3836var defaults$3 = defaults({
3837 weight: null,
3838 directed: false
3839});
3840var elesfn$a = {
3841 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3842 betweennessCentrality: function betweennessCentrality(options) {
3843 var _defaults = defaults$3(options),
3844 directed = _defaults.directed,
3845 weight = _defaults.weight;
3846
3847 var weighted = weight != null;
3848 var cy = this.cy(); // starting
3849
3850 var V = this.nodes();
3851 var A = {};
3852 var _C = {};
3853 var max = 0;
3854 var C = {
3855 set: function set(key, val) {
3856 _C[key] = val;
3857
3858 if (val > max) {
3859 max = val;
3860 }
3861 },
3862 get: function get(key) {
3863 return _C[key];
3864 }
3865 }; // A contains the neighborhoods of every node
3866
3867 for (var i = 0; i < V.length; i++) {
3868 var v = V[i];
3869 var vid = v.id();
3870
3871 if (directed) {
3872 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3873 } else {
3874 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3875 }
3876
3877 C.set(vid, 0);
3878 }
3879
3880 var _loop = function _loop(s) {
3881 var sid = V[s].id();
3882 var S = []; // stack
3883
3884 var P = {};
3885 var g = {};
3886 var d = {};
3887 var Q = new Heap(function (a, b) {
3888 return d[a] - d[b];
3889 }); // queue
3890 // init dictionaries
3891
3892 for (var _i = 0; _i < V.length; _i++) {
3893 var _vid = V[_i].id();
3894
3895 P[_vid] = [];
3896 g[_vid] = 0;
3897 d[_vid] = Infinity;
3898 }
3899
3900 g[sid] = 1; // sigma
3901
3902 d[sid] = 0; // distance to s
3903
3904 Q.push(sid);
3905
3906 while (!Q.empty()) {
3907 var _v = Q.pop();
3908
3909 S.push(_v);
3910
3911 if (weighted) {
3912 for (var j = 0; j < A[_v].length; j++) {
3913 var w = A[_v][j];
3914 var vEle = cy.getElementById(_v);
3915 var edge = void 0;
3916
3917 if (vEle.edgesTo(w).length > 0) {
3918 edge = vEle.edgesTo(w)[0];
3919 } else {
3920 edge = w.edgesTo(vEle)[0];
3921 }
3922
3923 var edgeWeight = weight(edge);
3924 w = w.id();
3925
3926 if (d[w] > d[_v] + edgeWeight) {
3927 d[w] = d[_v] + edgeWeight;
3928
3929 if (Q.nodes.indexOf(w) < 0) {
3930 //if w is not in Q
3931 Q.push(w);
3932 } else {
3933 // update position if w is in Q
3934 Q.updateItem(w);
3935 }
3936
3937 g[w] = 0;
3938 P[w] = [];
3939 }
3940
3941 if (d[w] == d[_v] + edgeWeight) {
3942 g[w] = g[w] + g[_v];
3943 P[w].push(_v);
3944 }
3945 }
3946 } else {
3947 for (var _j = 0; _j < A[_v].length; _j++) {
3948 var _w = A[_v][_j].id();
3949
3950 if (d[_w] == Infinity) {
3951 Q.push(_w);
3952 d[_w] = d[_v] + 1;
3953 }
3954
3955 if (d[_w] == d[_v] + 1) {
3956 g[_w] = g[_w] + g[_v];
3957
3958 P[_w].push(_v);
3959 }
3960 }
3961 }
3962 }
3963
3964 var e = {};
3965
3966 for (var _i2 = 0; _i2 < V.length; _i2++) {
3967 e[V[_i2].id()] = 0;
3968 }
3969
3970 while (S.length > 0) {
3971 var _w2 = S.pop();
3972
3973 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
3974 var _v2 = P[_w2][_j2];
3975 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
3976 }
3977
3978 if (_w2 != V[s].id()) {
3979 C.set(_w2, C.get(_w2) + e[_w2]);
3980 }
3981 }
3982 };
3983
3984 for (var s = 0; s < V.length; s++) {
3985 _loop(s);
3986 }
3987
3988 var ret = {
3989 betweenness: function betweenness(node) {
3990 var id = cy.collection(node).id();
3991 return C.get(id);
3992 },
3993 betweennessNormalized: function betweennessNormalized(node) {
3994 if (max == 0) {
3995 return 0;
3996 }
3997
3998 var id = cy.collection(node).id();
3999 return C.get(id) / max;
4000 }
4001 }; // alias
4002
4003 ret.betweennessNormalised = ret.betweennessNormalized;
4004 return ret;
4005 } // betweennessCentrality
4006
4007}; // elesfn
4008// nice, short mathemathical alias
4009
4010elesfn$a.bc = elesfn$a.betweennessCentrality;
4011
4012// Implemented by Zoe Xi @zoexi for GSOC 2016
4013/* eslint-disable no-unused-vars */
4014
4015var defaults$4 = defaults({
4016 expandFactor: 2,
4017 // affects time of computation and cluster granularity to some extent: M * M
4018 inflateFactor: 2,
4019 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4020 multFactor: 1,
4021 // optional self loops for each node. Use a neutral value to improve cluster computations.
4022 maxIterations: 20,
4023 // maximum number of iterations of the MCL algorithm in a single run
4024 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4025 function (edge) {
4026 return 1;
4027 }]
4028});
4029/* eslint-enable */
4030
4031var setOptions = function setOptions(options) {
4032 return defaults$4(options);
4033};
4034/* eslint-enable */
4035
4036
4037var getSimilarity = function getSimilarity(edge, attributes) {
4038 var total = 0;
4039
4040 for (var i = 0; i < attributes.length; i++) {
4041 total += attributes[i](edge);
4042 }
4043
4044 return total;
4045};
4046
4047var addLoops = function addLoops(M, n, val) {
4048 for (var i = 0; i < n; i++) {
4049 M[i * n + i] = val;
4050 }
4051};
4052
4053var normalize = function normalize(M, n) {
4054 var sum;
4055
4056 for (var col = 0; col < n; col++) {
4057 sum = 0;
4058
4059 for (var row = 0; row < n; row++) {
4060 sum += M[row * n + col];
4061 }
4062
4063 for (var _row = 0; _row < n; _row++) {
4064 M[_row * n + col] = M[_row * n + col] / sum;
4065 }
4066 }
4067}; // TODO: blocked matrix multiplication?
4068
4069
4070var mmult = function mmult(A, B, n) {
4071 var C = new Array(n * n);
4072
4073 for (var i = 0; i < n; i++) {
4074 for (var j = 0; j < n; j++) {
4075 C[i * n + j] = 0;
4076 }
4077
4078 for (var k = 0; k < n; k++) {
4079 for (var _j = 0; _j < n; _j++) {
4080 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4081 }
4082 }
4083 }
4084
4085 return C;
4086};
4087
4088var expand = function expand(M, n, expandFactor
4089/** power **/
4090) {
4091 var _M = M.slice(0);
4092
4093 for (var p = 1; p < expandFactor; p++) {
4094 M = mmult(M, _M, n);
4095 }
4096
4097 return M;
4098};
4099
4100var inflate = function inflate(M, n, inflateFactor
4101/** r **/
4102) {
4103 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4104
4105
4106 for (var i = 0; i < n * n; i++) {
4107 _M[i] = Math.pow(M[i], inflateFactor);
4108 }
4109
4110 normalize(_M, n);
4111 return _M;
4112};
4113
4114var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4115 // Check that both matrices have the same elements (i,j)
4116 for (var i = 0; i < n2; i++) {
4117 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4118
4119 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4120
4121 if (v1 !== v2) {
4122 return false;
4123 }
4124 }
4125
4126 return true;
4127};
4128
4129var assign = function assign(M, n, nodes, cy) {
4130 var clusters = [];
4131
4132 for (var i = 0; i < n; i++) {
4133 var cluster = [];
4134
4135 for (var j = 0; j < n; j++) {
4136 // Row-wise attractors and elements that they attract belong in same cluster
4137 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4138 cluster.push(nodes[j]);
4139 }
4140 }
4141
4142 if (cluster.length !== 0) {
4143 clusters.push(cy.collection(cluster));
4144 }
4145 }
4146
4147 return clusters;
4148};
4149
4150var isDuplicate = function isDuplicate(c1, c2) {
4151 for (var i = 0; i < c1.length; i++) {
4152 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4153 return false;
4154 }
4155 }
4156
4157 return true;
4158};
4159
4160var removeDuplicates = function removeDuplicates(clusters) {
4161 for (var i = 0; i < clusters.length; i++) {
4162 for (var j = 0; j < clusters.length; j++) {
4163 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4164 clusters.splice(j, 1);
4165 }
4166 }
4167 }
4168
4169 return clusters;
4170};
4171
4172var markovClustering = function markovClustering(options) {
4173 var nodes = this.nodes();
4174 var edges = this.edges();
4175 var cy = this.cy(); // Set parameters of algorithm:
4176
4177 var opts = setOptions(options); // Map each node to its position in node array
4178
4179 var id2position = {};
4180
4181 for (var i = 0; i < nodes.length; i++) {
4182 id2position[nodes[i].id()] = i;
4183 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4184
4185
4186 var n = nodes.length,
4187 n2 = n * n;
4188
4189 var M = new Array(n2),
4190 _M;
4191
4192 for (var _i = 0; _i < n2; _i++) {
4193 M[_i] = 0;
4194 }
4195
4196 for (var e = 0; e < edges.length; e++) {
4197 var edge = edges[e];
4198 var _i2 = id2position[edge.source().id()];
4199 var j = id2position[edge.target().id()];
4200 var sim = getSimilarity(edge, opts.attributes);
4201 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4202
4203 M[j * n + _i2] += sim;
4204 } // Begin Markov cluster algorithm
4205 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4206
4207
4208 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4209
4210 normalize(M, n);
4211 var isStillMoving = true;
4212 var iterations = 0;
4213
4214 while (isStillMoving && iterations < opts.maxIterations) {
4215 isStillMoving = false; // Step 3:
4216
4217 _M = expand(M, n, opts.expandFactor); // Step 4:
4218
4219 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4220
4221 if (!hasConverged(M, _M, n2, 4)) {
4222 isStillMoving = true;
4223 }
4224
4225 iterations++;
4226 } // Build clusters from matrix
4227
4228
4229 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4230
4231 clusters = removeDuplicates(clusters);
4232 return clusters;
4233};
4234
4235var markovClustering$1 = {
4236 markovClustering: markovClustering,
4237 mcl: markovClustering
4238};
4239
4240// Common distance metrics for clustering algorithms
4241
4242var identity = function identity(x) {
4243 return x;
4244};
4245
4246var absDiff = function absDiff(p, q) {
4247 return Math.abs(q - p);
4248};
4249
4250var addAbsDiff = function addAbsDiff(total, p, q) {
4251 return total + absDiff(p, q);
4252};
4253
4254var addSquaredDiff = function addSquaredDiff(total, p, q) {
4255 return total + Math.pow(q - p, 2);
4256};
4257
4258var sqrt = function sqrt(x) {
4259 return Math.sqrt(x);
4260};
4261
4262var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4263 return Math.max(currentMax, absDiff(p, q));
4264};
4265
4266var getDistance = function getDistance(length, getP, getQ, init, visit) {
4267 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4268 var ret = init;
4269 var p, q;
4270
4271 for (var dim = 0; dim < length; dim++) {
4272 p = getP(dim);
4273 q = getQ(dim);
4274 ret = visit(ret, p, q);
4275 }
4276
4277 return post(ret);
4278};
4279
4280var distances = {
4281 euclidean: function euclidean(length, getP, getQ) {
4282 if (length >= 2) {
4283 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4284 } else {
4285 // for single attr case, more efficient to avoid sqrt
4286 return getDistance(length, getP, getQ, 0, addAbsDiff);
4287 }
4288 },
4289 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4290 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4291 },
4292 manhattan: function manhattan(length, getP, getQ) {
4293 return getDistance(length, getP, getQ, 0, addAbsDiff);
4294 },
4295 max: function max(length, getP, getQ) {
4296 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4297 }
4298}; // in case the user accidentally doesn't use camel case
4299
4300distances['squared-euclidean'] = distances['squaredEuclidean'];
4301distances['squaredeuclidean'] = distances['squaredEuclidean'];
4302function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4303 var impl;
4304
4305 if (fn(method)) {
4306 impl = method;
4307 } else {
4308 impl = distances[method] || distances.euclidean;
4309 }
4310
4311 if (length === 0 && fn(method)) {
4312 return impl(nodeP, nodeQ);
4313 } else {
4314 return impl(length, getP, getQ, nodeP, nodeQ);
4315 }
4316}
4317
4318var defaults$5 = defaults({
4319 k: 2,
4320 m: 2,
4321 sensitivityThreshold: 0.0001,
4322 distance: 'euclidean',
4323 maxIterations: 10,
4324 attributes: [],
4325 testMode: false,
4326 testCentroids: null
4327});
4328
4329var setOptions$1 = function setOptions(options) {
4330 return defaults$5(options);
4331};
4332/* eslint-enable */
4333
4334
4335var getDist = function getDist(type, node, centroid, attributes, mode) {
4336 var noNodeP = mode !== 'kMedoids';
4337 var getP = noNodeP ? function (i) {
4338 return centroid[i];
4339 } : function (i) {
4340 return attributes[i](centroid);
4341 };
4342
4343 var getQ = function getQ(i) {
4344 return attributes[i](node);
4345 };
4346
4347 var nodeP = centroid;
4348 var nodeQ = node;
4349 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4350};
4351
4352var randomCentroids = function randomCentroids(nodes, k, attributes) {
4353 var ndim = attributes.length;
4354 var min = new Array(ndim);
4355 var max = new Array(ndim);
4356 var centroids = new Array(k);
4357 var centroid = null; // Find min, max values for each attribute dimension
4358
4359 for (var i = 0; i < ndim; i++) {
4360 min[i] = nodes.min(attributes[i]).value;
4361 max[i] = nodes.max(attributes[i]).value;
4362 } // Build k centroids, each represented as an n-dim feature vector
4363
4364
4365 for (var c = 0; c < k; c++) {
4366 centroid = [];
4367
4368 for (var _i = 0; _i < ndim; _i++) {
4369 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4370 }
4371
4372 centroids[c] = centroid;
4373 }
4374
4375 return centroids;
4376};
4377
4378var classify = function classify(node, centroids, distance, attributes, type) {
4379 var min = Infinity;
4380 var index = 0;
4381
4382 for (var i = 0; i < centroids.length; i++) {
4383 var dist = getDist(distance, node, centroids[i], attributes, type);
4384
4385 if (dist < min) {
4386 min = dist;
4387 index = i;
4388 }
4389 }
4390
4391 return index;
4392};
4393
4394var buildCluster = function buildCluster(centroid, nodes, assignment) {
4395 var cluster = [];
4396 var node = null;
4397
4398 for (var n = 0; n < nodes.length; n++) {
4399 node = nodes[n];
4400
4401 if (assignment[node.id()] === centroid) {
4402 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4403 cluster.push(node);
4404 }
4405 }
4406
4407 return cluster;
4408};
4409
4410var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4411 return Math.abs(v2 - v1) <= sensitivityThreshold;
4412};
4413
4414var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4415 for (var i = 0; i < v1.length; i++) {
4416 for (var j = 0; j < v1[i].length; j++) {
4417 var diff = Math.abs(v1[i][j] - v2[i][j]);
4418
4419 if (diff > sensitivityThreshold) {
4420 return false;
4421 }
4422 }
4423 }
4424
4425 return true;
4426};
4427
4428var seenBefore = function seenBefore(node, medoids, n) {
4429 for (var i = 0; i < n; i++) {
4430 if (node === medoids[i]) return true;
4431 }
4432
4433 return false;
4434};
4435
4436var randomMedoids = function randomMedoids(nodes, k) {
4437 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4438 // so we need to check to see if we've already seen or chose this node before.
4439
4440 if (nodes.length < 50) {
4441 // Randomly select k medoids from the n nodes
4442 for (var i = 0; i < k; i++) {
4443 var node = nodes[Math.floor(Math.random() * nodes.length)]; // If we've already chosen this node to be a medoid, don't choose it again (for small data sets).
4444 // Instead choose a different random node.
4445
4446 while (seenBefore(node, medoids, i)) {
4447 node = nodes[Math.floor(Math.random() * nodes.length)];
4448 }
4449
4450 medoids[i] = node;
4451 }
4452 } else {
4453 // Relatively large data set, so pretty safe to not check and just select random nodes
4454 for (var _i2 = 0; _i2 < k; _i2++) {
4455 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4456 }
4457 }
4458
4459 return medoids;
4460};
4461
4462var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4463 var cost = 0;
4464
4465 for (var n = 0; n < cluster.length; n++) {
4466 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4467 }
4468
4469 return cost;
4470};
4471
4472var kMeans = function kMeans(options) {
4473 var cy = this.cy();
4474 var nodes = this.nodes();
4475 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4476
4477 var opts = setOptions$1(options); // Begin k-means algorithm
4478
4479 var clusters = new Array(opts.k);
4480 var assignment = {};
4481 var centroids; // Step 1: Initialize centroid positions
4482
4483 if (opts.testMode) {
4484 if (typeof opts.testCentroids === 'number') {
4485 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4486 } else if (_typeof(opts.testCentroids) === 'object') {
4487 centroids = opts.testCentroids;
4488 } else {
4489 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4490 }
4491 } else {
4492 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4493 }
4494
4495 var isStillMoving = true;
4496 var iterations = 0;
4497
4498 while (isStillMoving && iterations < opts.maxIterations) {
4499 // Step 2: Assign nodes to the nearest centroid
4500 for (var n = 0; n < nodes.length; n++) {
4501 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4502
4503 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4504 } // Step 3: For each of the k clusters, update its centroid
4505
4506
4507 isStillMoving = false;
4508
4509 for (var c = 0; c < opts.k; c++) {
4510 // Get all nodes that belong to this cluster
4511 var cluster = buildCluster(c, nodes, assignment);
4512
4513 if (cluster.length === 0) {
4514 // If cluster is empty, break out early & move to next cluster
4515 continue;
4516 } // Update centroids by calculating avg of all nodes within the cluster.
4517
4518
4519 var ndim = opts.attributes.length;
4520 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4521
4522 var newCentroid = new Array(ndim);
4523 var sum = new Array(ndim);
4524
4525 for (var d = 0; d < ndim; d++) {
4526 sum[d] = 0.0;
4527
4528 for (var i = 0; i < cluster.length; i++) {
4529 node = cluster[i];
4530 sum[d] += opts.attributes[d](node);
4531 }
4532
4533 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4534
4535 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4536 isStillMoving = true;
4537 }
4538 }
4539
4540 centroids[c] = newCentroid;
4541 clusters[c] = cy.collection(cluster);
4542 }
4543
4544 iterations++;
4545 }
4546
4547 return clusters;
4548};
4549
4550var kMedoids = function kMedoids(options) {
4551 var cy = this.cy();
4552 var nodes = this.nodes();
4553 var node = null;
4554 var opts = setOptions$1(options); // Begin k-medoids algorithm
4555
4556 var clusters = new Array(opts.k);
4557 var medoids;
4558 var assignment = {};
4559 var curCost;
4560 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4561 // Step 1: Initialize k medoids
4562
4563 if (opts.testMode) {
4564 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4565 medoids = opts.testCentroids;
4566 } else {
4567 medoids = randomMedoids(nodes, opts.k);
4568 }
4569 } else {
4570 medoids = randomMedoids(nodes, opts.k);
4571 }
4572
4573 var isStillMoving = true;
4574 var iterations = 0;
4575
4576 while (isStillMoving && iterations < opts.maxIterations) {
4577 // Step 2: Assign nodes to the nearest medoid
4578 for (var n = 0; n < nodes.length; n++) {
4579 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4580
4581 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4582 }
4583
4584 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4585 // select the node with the lowest configuration cost as new medoid.
4586
4587 for (var m = 0; m < medoids.length; m++) {
4588 // Get all nodes that belong to this medoid
4589 var cluster = buildCluster(m, nodes, assignment);
4590
4591 if (cluster.length === 0) {
4592 // If cluster is empty, break out early & move to next cluster
4593 continue;
4594 }
4595
4596 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4597 // Select different medoid if its configuration has the lowest cost
4598
4599 for (var _n = 0; _n < cluster.length; _n++) {
4600 curCost = findCost(cluster[_n], cluster, opts.attributes);
4601
4602 if (curCost < minCosts[m]) {
4603 minCosts[m] = curCost;
4604 medoids[m] = cluster[_n];
4605 isStillMoving = true;
4606 }
4607 }
4608
4609 clusters[m] = cy.collection(cluster);
4610 }
4611
4612 iterations++;
4613 }
4614
4615 return clusters;
4616};
4617
4618var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4619 var numerator, denominator;
4620
4621 for (var n = 0; n < nodes.length; n++) {
4622 for (var c = 0; c < centroids.length; c++) {
4623 weight[n][c] = Math.pow(U[n][c], opts.m);
4624 }
4625 }
4626
4627 for (var _c = 0; _c < centroids.length; _c++) {
4628 for (var dim = 0; dim < opts.attributes.length; dim++) {
4629 numerator = 0;
4630 denominator = 0;
4631
4632 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4633 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4634 denominator += weight[_n2][_c];
4635 }
4636
4637 centroids[_c][dim] = numerator / denominator;
4638 }
4639 }
4640};
4641
4642var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4643 // Save previous step
4644 for (var i = 0; i < U.length; i++) {
4645 _U[i] = U[i].slice();
4646 }
4647
4648 var sum, numerator, denominator;
4649 var pow = 2 / (opts.m - 1);
4650
4651 for (var c = 0; c < centroids.length; c++) {
4652 for (var n = 0; n < nodes.length; n++) {
4653 sum = 0;
4654
4655 for (var k = 0; k < centroids.length; k++) {
4656 // against all other centroids
4657 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4658 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4659 sum += Math.pow(numerator / denominator, pow);
4660 }
4661
4662 U[n][c] = 1 / sum;
4663 }
4664 }
4665};
4666
4667var assign$1 = function assign(nodes, U, opts, cy) {
4668 var clusters = new Array(opts.k);
4669
4670 for (var c = 0; c < clusters.length; c++) {
4671 clusters[c] = [];
4672 }
4673
4674 var max;
4675 var index;
4676
4677 for (var n = 0; n < U.length; n++) {
4678 // for each node (U is N x C matrix)
4679 max = -Infinity;
4680 index = -1; // Determine which cluster the node is most likely to belong in
4681
4682 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4683 if (U[n][_c2] > max) {
4684 max = U[n][_c2];
4685 index = _c2;
4686 }
4687 }
4688
4689 clusters[index].push(nodes[n]);
4690 } // Turn every array into a collection of nodes
4691
4692
4693 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4694 clusters[_c3] = cy.collection(clusters[_c3]);
4695 }
4696
4697 return clusters;
4698};
4699
4700var fuzzyCMeans = function fuzzyCMeans(options) {
4701 var cy = this.cy();
4702 var nodes = this.nodes();
4703 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4704
4705 var clusters;
4706 var centroids;
4707 var U;
4708
4709 var _U;
4710
4711 var weight; // Step 1: Initialize letiables.
4712
4713 _U = new Array(nodes.length);
4714
4715 for (var i = 0; i < nodes.length; i++) {
4716 // N x C matrix
4717 _U[i] = new Array(opts.k);
4718 }
4719
4720 U = new Array(nodes.length);
4721
4722 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4723 // N x C matrix
4724 U[_i3] = new Array(opts.k);
4725 }
4726
4727 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4728 var total = 0;
4729
4730 for (var j = 0; j < opts.k; j++) {
4731 U[_i4][j] = Math.random();
4732 total += U[_i4][j];
4733 }
4734
4735 for (var _j = 0; _j < opts.k; _j++) {
4736 U[_i4][_j] = U[_i4][_j] / total;
4737 }
4738 }
4739
4740 centroids = new Array(opts.k);
4741
4742 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4743 centroids[_i5] = new Array(opts.attributes.length);
4744 }
4745
4746 weight = new Array(nodes.length);
4747
4748 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4749 // N x C matrix
4750 weight[_i6] = new Array(opts.k);
4751 } // end init FCM
4752
4753
4754 var isStillMoving = true;
4755 var iterations = 0;
4756
4757 while (isStillMoving && iterations < opts.maxIterations) {
4758 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4759
4760 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4761
4762 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4763
4764 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4765 isStillMoving = true;
4766 }
4767
4768 iterations++;
4769 } // Assign nodes to clusters with highest probability.
4770
4771
4772 clusters = assign$1(nodes, U, opts, cy);
4773 return {
4774 clusters: clusters,
4775 degreeOfMembership: U
4776 };
4777};
4778
4779var kClustering = {
4780 kMeans: kMeans,
4781 kMedoids: kMedoids,
4782 fuzzyCMeans: fuzzyCMeans,
4783 fcm: fuzzyCMeans
4784};
4785
4786// Implemented by Zoe Xi @zoexi for GSOC 2016
4787var defaults$6 = defaults({
4788 distance: 'euclidean',
4789 // distance metric to compare nodes
4790 linkage: 'min',
4791 // linkage criterion : how to determine the distance between clusters of nodes
4792 mode: 'threshold',
4793 // mode:'threshold' => clusters must be threshold distance apart
4794 threshold: Infinity,
4795 // the distance threshold
4796 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4797 addDendrogram: false,
4798 // whether to add the dendrogram to the graph for viz
4799 dendrogramDepth: 0,
4800 // depth at which dendrogram branches are merged into the returned clusters
4801 attributes: [] // array of attr functions
4802
4803});
4804var linkageAliases = {
4805 'single': 'min',
4806 'complete': 'max'
4807};
4808
4809var setOptions$2 = function setOptions(options) {
4810 var opts = defaults$6(options);
4811 var preferredAlias = linkageAliases[opts.linkage];
4812
4813 if (preferredAlias != null) {
4814 opts.linkage = preferredAlias;
4815 }
4816
4817 return opts;
4818};
4819
4820var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4821 // Find two closest clusters from cached mins
4822 var minKey = 0;
4823 var min = Infinity;
4824 var dist;
4825 var attrs = opts.attributes;
4826
4827 var getDist = function getDist(n1, n2) {
4828 return clusteringDistance(opts.distance, attrs.length, function (i) {
4829 return attrs[i](n1);
4830 }, function (i) {
4831 return attrs[i](n2);
4832 }, n1, n2);
4833 };
4834
4835 for (var i = 0; i < clusters.length; i++) {
4836 var key = clusters[i].key;
4837 var _dist = dists[key][mins[key]];
4838
4839 if (_dist < min) {
4840 minKey = key;
4841 min = _dist;
4842 }
4843 }
4844
4845 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4846 return false;
4847 }
4848
4849 var c1 = index[minKey];
4850 var c2 = index[mins[minKey]];
4851 var merged; // Merge two closest clusters
4852
4853 if (opts.mode === 'dendrogram') {
4854 merged = {
4855 left: c1,
4856 right: c2,
4857 key: c1.key
4858 };
4859 } else {
4860 merged = {
4861 value: c1.value.concat(c2.value),
4862 key: c1.key
4863 };
4864 }
4865
4866 clusters[c1.index] = merged;
4867 clusters.splice(c2.index, 1);
4868 index[c1.key] = merged; // Update distances with new merged cluster
4869
4870 for (var _i = 0; _i < clusters.length; _i++) {
4871 var cur = clusters[_i];
4872
4873 if (c1.key === cur.key) {
4874 dist = Infinity;
4875 } else if (opts.linkage === 'min') {
4876 dist = dists[c1.key][cur.key];
4877
4878 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4879 dist = dists[c2.key][cur.key];
4880 }
4881 } else if (opts.linkage === 'max') {
4882 dist = dists[c1.key][cur.key];
4883
4884 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4885 dist = dists[c2.key][cur.key];
4886 }
4887 } else if (opts.linkage === 'mean') {
4888 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4889 } else {
4890 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4891 }
4892
4893 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4894 } // Update cached mins
4895
4896
4897 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4898 var key1 = clusters[_i2].key;
4899
4900 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4901 var _min = key1;
4902
4903 for (var j = 0; j < clusters.length; j++) {
4904 var key2 = clusters[j].key;
4905
4906 if (dists[key1][key2] < dists[key1][_min]) {
4907 _min = key2;
4908 }
4909 }
4910
4911 mins[key1] = _min;
4912 }
4913
4914 clusters[_i2].index = _i2;
4915 } // Clean up meta data used for clustering
4916
4917
4918 c1.key = c2.key = c1.index = c2.index = null;
4919 return true;
4920};
4921
4922var getAllChildren = function getAllChildren(root, arr, cy) {
4923 if (!root) return;
4924
4925 if (root.value) {
4926 arr.push(root.value);
4927 } else {
4928 if (root.left) getAllChildren(root.left, arr);
4929 if (root.right) getAllChildren(root.right, arr);
4930 }
4931};
4932
4933var buildDendrogram = function buildDendrogram(root, cy) {
4934 if (!root) return '';
4935
4936 if (root.left && root.right) {
4937 var leftStr = buildDendrogram(root.left, cy);
4938 var rightStr = buildDendrogram(root.right, cy);
4939 var node = cy.add({
4940 group: 'nodes',
4941 data: {
4942 id: leftStr + ',' + rightStr
4943 }
4944 });
4945 cy.add({
4946 group: 'edges',
4947 data: {
4948 source: leftStr,
4949 target: node.id()
4950 }
4951 });
4952 cy.add({
4953 group: 'edges',
4954 data: {
4955 source: rightStr,
4956 target: node.id()
4957 }
4958 });
4959 return node.id();
4960 } else if (root.value) {
4961 return root.value.id();
4962 }
4963};
4964
4965var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
4966 if (!root) return [];
4967 var left = [],
4968 right = [],
4969 leaves = [];
4970
4971 if (k === 0) {
4972 // don't cut tree, simply return all nodes as 1 single cluster
4973 if (root.left) getAllChildren(root.left, left);
4974 if (root.right) getAllChildren(root.right, right);
4975 leaves = left.concat(right);
4976 return [cy.collection(leaves)];
4977 } else if (k === 1) {
4978 // cut at root
4979 if (root.value) {
4980 // leaf node
4981 return [cy.collection(root.value)];
4982 } else {
4983 if (root.left) getAllChildren(root.left, left);
4984 if (root.right) getAllChildren(root.right, right);
4985 return [cy.collection(left), cy.collection(right)];
4986 }
4987 } else {
4988 if (root.value) {
4989 return [cy.collection(root.value)];
4990 } else {
4991 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
4992 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
4993 return left.concat(right);
4994 }
4995 }
4996};
4997/* eslint-enable */
4998
4999
5000var hierarchicalClustering = function hierarchicalClustering(options) {
5001 var cy = this.cy();
5002 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5003
5004 var opts = setOptions$2(options);
5005 var attrs = opts.attributes;
5006
5007 var getDist = function getDist(n1, n2) {
5008 return clusteringDistance(opts.distance, attrs.length, function (i) {
5009 return attrs[i](n1);
5010 }, function (i) {
5011 return attrs[i](n2);
5012 }, n1, n2);
5013 }; // Begin hierarchical algorithm
5014
5015
5016 var clusters = [];
5017 var dists = []; // distances between each pair of clusters
5018
5019 var mins = []; // closest cluster for each cluster
5020
5021 var index = []; // hash of all clusters by key
5022 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5023
5024 for (var n = 0; n < nodes.length; n++) {
5025 var cluster = {
5026 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5027 key: n,
5028 index: n
5029 };
5030 clusters[n] = cluster;
5031 index[n] = cluster;
5032 dists[n] = [];
5033 mins[n] = 0;
5034 } // Calculate the distance between each pair of clusters
5035
5036
5037 for (var i = 0; i < clusters.length; i++) {
5038 for (var j = 0; j <= i; j++) {
5039 var dist = void 0;
5040
5041 if (opts.mode === 'dendrogram') {
5042 // modes store cluster values differently
5043 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5044 } else {
5045 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5046 }
5047
5048 dists[i][j] = dist;
5049 dists[j][i] = dist;
5050
5051 if (dist < dists[i][mins[i]]) {
5052 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5053 }
5054 }
5055 } // Find the closest pair of clusters and merge them into a single cluster.
5056 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5057
5058
5059 var merged = mergeClosest(clusters, index, dists, mins, opts);
5060
5061 while (merged) {
5062 merged = mergeClosest(clusters, index, dists, mins, opts);
5063 }
5064
5065 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5066 // in addition to returning the clusters.
5067
5068 if (opts.mode === 'dendrogram') {
5069 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5070 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5071 } else {
5072 // Regular mode simply returns the clusters
5073 retClusters = new Array(clusters.length);
5074 clusters.forEach(function (cluster, i) {
5075 // Clean up meta data used for clustering
5076 cluster.key = cluster.index = null;
5077 retClusters[i] = cy.collection(cluster.value);
5078 });
5079 }
5080
5081 return retClusters;
5082};
5083
5084var hierarchicalClustering$1 = {
5085 hierarchicalClustering: hierarchicalClustering,
5086 hca: hierarchicalClustering
5087};
5088
5089// Implemented by Zoe Xi @zoexi for GSOC 2016
5090var defaults$7 = defaults({
5091 distance: 'euclidean',
5092 // distance metric to compare attributes between two nodes
5093 preference: 'median',
5094 // suitability of a data point to serve as an exemplar
5095 damping: 0.8,
5096 // damping factor between [0.5, 1)
5097 maxIterations: 1000,
5098 // max number of iterations to run
5099 minIterations: 100,
5100 // min number of iterations to run in order for clustering to stop
5101 attributes: [// functions to quantify the similarity between any two points
5102 // e.g. node => node.data('weight')
5103 ]
5104});
5105
5106var setOptions$3 = function setOptions(options) {
5107 var dmp = options.damping;
5108 var pref = options.preference;
5109
5110 if (!(0.5 <= dmp && dmp < 1)) {
5111 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5112 }
5113
5114 var validPrefs = ['median', 'mean', 'min', 'max'];
5115
5116 if (!(validPrefs.some(function (v) {
5117 return v === pref;
5118 }) || number(pref))) {
5119 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5120 return "'".concat(p, "'");
5121 }).join(', '), "] or a number. Got: ").concat(pref));
5122 }
5123
5124 return defaults$7(options);
5125};
5126/* eslint-enable */
5127
5128
5129var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5130 var attr = function attr(n, i) {
5131 return attributes[i](n);
5132 }; // nb negative because similarity should have an inverse relationship to distance
5133
5134
5135 return -clusteringDistance(type, attributes.length, function (i) {
5136 return attr(n1, i);
5137 }, function (i) {
5138 return attr(n2, i);
5139 }, n1, n2);
5140};
5141
5142var getPreference = function getPreference(S, preference) {
5143 // larger preference = greater # of clusters
5144 var p = null;
5145
5146 if (preference === 'median') {
5147 p = median(S);
5148 } else if (preference === 'mean') {
5149 p = mean(S);
5150 } else if (preference === 'min') {
5151 p = min(S);
5152 } else if (preference === 'max') {
5153 p = max(S);
5154 } else {
5155 // Custom preference number, as set by user
5156 p = preference;
5157 }
5158
5159 return p;
5160};
5161
5162var findExemplars = function findExemplars(n, R, A) {
5163 var indices = [];
5164
5165 for (var i = 0; i < n; i++) {
5166 if (R[i * n + i] + A[i * n + i] > 0) {
5167 indices.push(i);
5168 }
5169 }
5170
5171 return indices;
5172};
5173
5174var assignClusters = function assignClusters(n, S, exemplars) {
5175 var clusters = [];
5176
5177 for (var i = 0; i < n; i++) {
5178 var index = -1;
5179 var max = -Infinity;
5180
5181 for (var ei = 0; ei < exemplars.length; ei++) {
5182 var e = exemplars[ei];
5183
5184 if (S[i * n + e] > max) {
5185 index = e;
5186 max = S[i * n + e];
5187 }
5188 }
5189
5190 if (index > 0) {
5191 clusters.push(index);
5192 }
5193 }
5194
5195 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5196 clusters[exemplars[_ei]] = exemplars[_ei];
5197 }
5198
5199 return clusters;
5200};
5201
5202var assign$2 = function assign(n, S, exemplars) {
5203 var clusters = assignClusters(n, S, exemplars);
5204
5205 for (var ei = 0; ei < exemplars.length; ei++) {
5206 var ii = [];
5207
5208 for (var c = 0; c < clusters.length; c++) {
5209 if (clusters[c] === exemplars[ei]) {
5210 ii.push(c);
5211 }
5212 }
5213
5214 var maxI = -1;
5215 var maxSum = -Infinity;
5216
5217 for (var i = 0; i < ii.length; i++) {
5218 var sum = 0;
5219
5220 for (var j = 0; j < ii.length; j++) {
5221 sum += S[ii[j] * n + ii[i]];
5222 }
5223
5224 if (sum > maxSum) {
5225 maxI = i;
5226 maxSum = sum;
5227 }
5228 }
5229
5230 exemplars[ei] = ii[maxI];
5231 }
5232
5233 clusters = assignClusters(n, S, exemplars);
5234 return clusters;
5235};
5236
5237var affinityPropagation = function affinityPropagation(options) {
5238 var cy = this.cy();
5239 var nodes = this.nodes();
5240 var opts = setOptions$3(options); // Map each node to its position in node array
5241
5242 var id2position = {};
5243
5244 for (var i = 0; i < nodes.length; i++) {
5245 id2position[nodes[i].id()] = i;
5246 } // Begin affinity propagation algorithm
5247
5248
5249 var n; // number of data points
5250
5251 var n2; // size of matrices
5252
5253 var S; // similarity matrix (1D array)
5254
5255 var p; // preference/suitability of a data point to serve as an exemplar
5256
5257 var R; // responsibility matrix (1D array)
5258
5259 var A; // availability matrix (1D array)
5260
5261 n = nodes.length;
5262 n2 = n * n; // Initialize and build S similarity matrix
5263
5264 S = new Array(n2);
5265
5266 for (var _i = 0; _i < n2; _i++) {
5267 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5268 }
5269
5270 for (var _i2 = 0; _i2 < n; _i2++) {
5271 for (var j = 0; j < n; j++) {
5272 if (_i2 !== j) {
5273 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5274 }
5275 }
5276 } // Place preferences on the diagonal of S
5277
5278
5279 p = getPreference(S, opts.preference);
5280
5281 for (var _i3 = 0; _i3 < n; _i3++) {
5282 S[_i3 * n + _i3] = p;
5283 } // Initialize R responsibility matrix
5284
5285
5286 R = new Array(n2);
5287
5288 for (var _i4 = 0; _i4 < n2; _i4++) {
5289 R[_i4] = 0.0;
5290 } // Initialize A availability matrix
5291
5292
5293 A = new Array(n2);
5294
5295 for (var _i5 = 0; _i5 < n2; _i5++) {
5296 A[_i5] = 0.0;
5297 }
5298
5299 var old = new Array(n);
5300 var Rp = new Array(n);
5301 var se = new Array(n);
5302
5303 for (var _i6 = 0; _i6 < n; _i6++) {
5304 old[_i6] = 0.0;
5305 Rp[_i6] = 0.0;
5306 se[_i6] = 0;
5307 }
5308
5309 var e = new Array(n * opts.minIterations);
5310
5311 for (var _i7 = 0; _i7 < e.length; _i7++) {
5312 e[_i7] = 0;
5313 }
5314
5315 var iter;
5316
5317 for (iter = 0; iter < opts.maxIterations; iter++) {
5318 // main algorithmic loop
5319 // Update R responsibility matrix
5320 for (var _i8 = 0; _i8 < n; _i8++) {
5321 var max = -Infinity,
5322 max2 = -Infinity,
5323 maxI = -1,
5324 AS = 0.0;
5325
5326 for (var _j = 0; _j < n; _j++) {
5327 old[_j] = R[_i8 * n + _j];
5328 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5329
5330 if (AS >= max) {
5331 max2 = max;
5332 max = AS;
5333 maxI = _j;
5334 } else if (AS > max2) {
5335 max2 = AS;
5336 }
5337 }
5338
5339 for (var _j2 = 0; _j2 < n; _j2++) {
5340 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5341 }
5342
5343 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5344 } // Update A availability matrix
5345
5346
5347 for (var _i9 = 0; _i9 < n; _i9++) {
5348 var sum = 0;
5349
5350 for (var _j3 = 0; _j3 < n; _j3++) {
5351 old[_j3] = A[_j3 * n + _i9];
5352 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5353 sum += Rp[_j3];
5354 }
5355
5356 sum -= Rp[_i9];
5357 Rp[_i9] = R[_i9 * n + _i9];
5358 sum += Rp[_i9];
5359
5360 for (var _j4 = 0; _j4 < n; _j4++) {
5361 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5362 }
5363
5364 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5365 } // Check for convergence
5366
5367
5368 var K = 0;
5369
5370 for (var _i10 = 0; _i10 < n; _i10++) {
5371 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5372 e[iter % opts.minIterations * n + _i10] = E;
5373 K += E;
5374 }
5375
5376 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5377 var _sum = 0;
5378
5379 for (var _i11 = 0; _i11 < n; _i11++) {
5380 se[_i11] = 0;
5381
5382 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5383 se[_i11] += e[_j5 * n + _i11];
5384 }
5385
5386 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5387 _sum++;
5388 }
5389 }
5390
5391 if (_sum === n) {
5392 // then we have convergence
5393 break;
5394 }
5395 }
5396 } // Identify exemplars (cluster centers)
5397
5398
5399 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5400
5401 var clusterIndices = assign$2(n, S, exemplarsIndices);
5402 var clusters = {};
5403
5404 for (var c = 0; c < exemplarsIndices.length; c++) {
5405 clusters[exemplarsIndices[c]] = [];
5406 }
5407
5408 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5409 var pos = id2position[nodes[_i12].id()];
5410
5411 var clusterIndex = clusterIndices[pos];
5412
5413 if (clusterIndex != null) {
5414 // the node may have not been assigned a cluster if no valid attributes were specified
5415 clusters[clusterIndex].push(nodes[_i12]);
5416 }
5417 }
5418
5419 var retClusters = new Array(exemplarsIndices.length);
5420
5421 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5422 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5423 }
5424
5425 return retClusters;
5426};
5427
5428var affinityPropagation$1 = {
5429 affinityPropagation: affinityPropagation,
5430 ap: affinityPropagation
5431};
5432
5433var hierholzerDefaults = defaults({
5434 root: undefined,
5435 directed: false
5436});
5437var elesfn$b = {
5438 hierholzer: function hierholzer(options) {
5439 if (!plainObject(options)) {
5440 var args = arguments;
5441 options = {
5442 root: args[0],
5443 directed: args[1]
5444 };
5445 }
5446
5447 var _hierholzerDefaults = hierholzerDefaults(options),
5448 root = _hierholzerDefaults.root,
5449 directed = _hierholzerDefaults.directed;
5450
5451 var eles = this;
5452 var dflag = false;
5453 var oddIn;
5454 var oddOut;
5455 var startVertex;
5456 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5457 var nodes = {};
5458 var edges = {};
5459
5460 if (directed) {
5461 eles.forEach(function (ele) {
5462 var id = ele.id();
5463
5464 if (ele.isNode()) {
5465 var ind = ele.indegree(true);
5466 var outd = ele.outdegree(true);
5467 var d1 = ind - outd;
5468 var d2 = outd - ind;
5469
5470 if (d1 == 1) {
5471 if (oddIn) dflag = true;else oddIn = id;
5472 } else if (d2 == 1) {
5473 if (oddOut) dflag = true;else oddOut = id;
5474 } else if (d2 > 1 || d1 > 1) {
5475 dflag = true;
5476 }
5477
5478 nodes[id] = [];
5479 ele.outgoers().forEach(function (e) {
5480 if (e.isEdge()) nodes[id].push(e.id());
5481 });
5482 } else {
5483 edges[id] = [undefined, ele.target().id()];
5484 }
5485 });
5486 } else {
5487 eles.forEach(function (ele) {
5488 var id = ele.id();
5489
5490 if (ele.isNode()) {
5491 var d = ele.degree(true);
5492
5493 if (d % 2) {
5494 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5495 }
5496
5497 nodes[id] = [];
5498 ele.connectedEdges().forEach(function (e) {
5499 return nodes[id].push(e.id());
5500 });
5501 } else {
5502 edges[id] = [ele.source().id(), ele.target().id()];
5503 }
5504 });
5505 }
5506
5507 var result = {
5508 found: false,
5509 trail: undefined
5510 };
5511 if (dflag) return result;else if (oddOut && oddIn) {
5512 if (directed) {
5513 if (startVertex && oddOut != startVertex) {
5514 return result;
5515 }
5516
5517 startVertex = oddOut;
5518 } else {
5519 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5520 return result;
5521 } else if (!startVertex) {
5522 startVertex = oddOut;
5523 }
5524 }
5525 } else {
5526 if (!startVertex) startVertex = eles[0].id();
5527 }
5528
5529 var walk = function walk(v) {
5530 var currentNode = v;
5531 var subtour = [v];
5532 var adj, adjTail, adjHead;
5533
5534 while (nodes[currentNode].length) {
5535 adj = nodes[currentNode].shift();
5536 adjTail = edges[adj][0];
5537 adjHead = edges[adj][1];
5538
5539 if (currentNode != adjHead) {
5540 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5541 return e != adj;
5542 });
5543 currentNode = adjHead;
5544 } else if (!directed && currentNode != adjTail) {
5545 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5546 return e != adj;
5547 });
5548 currentNode = adjTail;
5549 }
5550
5551 subtour.unshift(adj);
5552 subtour.unshift(currentNode);
5553 }
5554
5555 return subtour;
5556 };
5557
5558 var trail = [];
5559 var subtour = [];
5560 subtour = walk(startVertex);
5561
5562 while (subtour.length != 1) {
5563 if (nodes[subtour[0]].length == 0) {
5564 trail.unshift(eles.getElementById(subtour.shift()));
5565 trail.unshift(eles.getElementById(subtour.shift()));
5566 } else {
5567 subtour = walk(subtour.shift()).concat(subtour);
5568 }
5569 }
5570
5571 trail.unshift(eles.getElementById(subtour.shift())); // final node
5572
5573 for (var d in nodes) {
5574 if (nodes[d].length) {
5575 return result;
5576 }
5577 }
5578
5579 result.found = true;
5580 result.trail = this.spawn(trail, true);
5581 return result;
5582 }
5583};
5584
5585var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5586 var eles = this;
5587 var nodes = {};
5588 var id = 0;
5589 var edgeCount = 0;
5590 var components = [];
5591 var stack = [];
5592 var visitedEdges = {};
5593
5594 var buildComponent = function buildComponent(x, y) {
5595 var i = stack.length - 1;
5596 var cutset = [];
5597 var component = eles.spawn();
5598
5599 while (stack[i].x != x || stack[i].y != y) {
5600 cutset.push(stack.pop().edge);
5601 i--;
5602 }
5603
5604 cutset.push(stack.pop().edge);
5605 cutset.forEach(function (edge) {
5606 var connectedNodes = edge.connectedNodes().intersection(eles);
5607 component.merge(edge);
5608 connectedNodes.forEach(function (node) {
5609 var nodeId = node.id();
5610 var connectedEdges = node.connectedEdges().intersection(eles);
5611 component.merge(node);
5612
5613 if (!nodes[nodeId].cutVertex) {
5614 component.merge(connectedEdges);
5615 } else {
5616 component.merge(connectedEdges.filter(function (edge) {
5617 return edge.isLoop();
5618 }));
5619 }
5620 });
5621 });
5622 components.push(component);
5623 };
5624
5625 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5626 if (root === parent) edgeCount += 1;
5627 nodes[currentNode] = {
5628 id: id,
5629 low: id++,
5630 cutVertex: false
5631 };
5632 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5633
5634 if (edges.size() === 0) {
5635 components.push(eles.spawn(eles.getElementById(currentNode)));
5636 } else {
5637 var sourceId, targetId, otherNodeId, edgeId;
5638 edges.forEach(function (edge) {
5639 sourceId = edge.source().id();
5640 targetId = edge.target().id();
5641 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5642
5643 if (otherNodeId !== parent) {
5644 edgeId = edge.id();
5645
5646 if (!visitedEdges[edgeId]) {
5647 visitedEdges[edgeId] = true;
5648 stack.push({
5649 x: currentNode,
5650 y: otherNodeId,
5651 edge: edge
5652 });
5653 }
5654
5655 if (!(otherNodeId in nodes)) {
5656 biconnectedSearch(root, otherNodeId, currentNode);
5657 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5658
5659 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5660 nodes[currentNode].cutVertex = true;
5661 buildComponent(currentNode, otherNodeId);
5662 }
5663 } else {
5664 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5665 }
5666 }
5667 });
5668 }
5669 };
5670
5671 eles.forEach(function (ele) {
5672 if (ele.isNode()) {
5673 var nodeId = ele.id();
5674
5675 if (!(nodeId in nodes)) {
5676 edgeCount = 0;
5677 biconnectedSearch(nodeId, nodeId);
5678 nodes[nodeId].cutVertex = edgeCount > 1;
5679 }
5680 }
5681 });
5682 var cutVertices = Object.keys(nodes).filter(function (id) {
5683 return nodes[id].cutVertex;
5684 }).map(function (id) {
5685 return eles.getElementById(id);
5686 });
5687 return {
5688 cut: eles.spawn(cutVertices),
5689 components: components
5690 };
5691};
5692
5693var hopcroftTarjanBiconnected$1 = {
5694 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5695 htbc: hopcroftTarjanBiconnected,
5696 htb: hopcroftTarjanBiconnected,
5697 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5698};
5699
5700var tarjanStronglyConnected = function tarjanStronglyConnected() {
5701 var eles = this;
5702 var nodes = {};
5703 var index = 0;
5704 var components = [];
5705 var stack = [];
5706 var cut = eles.spawn(eles);
5707
5708 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5709 stack.push(sourceNodeId);
5710 nodes[sourceNodeId] = {
5711 index: index,
5712 low: index++,
5713 explored: false
5714 };
5715 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5716 connectedEdges.forEach(function (edge) {
5717 var targetNodeId = edge.target().id();
5718
5719 if (targetNodeId !== sourceNodeId) {
5720 if (!(targetNodeId in nodes)) {
5721 stronglyConnectedSearch(targetNodeId);
5722 }
5723
5724 if (!nodes[targetNodeId].explored) {
5725 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5726 }
5727 }
5728 });
5729
5730 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5731 var componentNodes = eles.spawn();
5732
5733 for (;;) {
5734 var nodeId = stack.pop();
5735 componentNodes.merge(eles.getElementById(nodeId));
5736 nodes[nodeId].low = nodes[sourceNodeId].index;
5737 nodes[nodeId].explored = true;
5738
5739 if (nodeId === sourceNodeId) {
5740 break;
5741 }
5742 }
5743
5744 var componentEdges = componentNodes.edgesWith(componentNodes);
5745 var component = componentNodes.merge(componentEdges);
5746 components.push(component);
5747 cut = cut.difference(component);
5748 }
5749 };
5750
5751 eles.forEach(function (ele) {
5752 if (ele.isNode()) {
5753 var nodeId = ele.id();
5754
5755 if (!(nodeId in nodes)) {
5756 stronglyConnectedSearch(nodeId);
5757 }
5758 }
5759 });
5760 return {
5761 cut: cut,
5762 components: components
5763 };
5764};
5765
5766var tarjanStronglyConnected$1 = {
5767 tarjanStronglyConnected: tarjanStronglyConnected,
5768 tsc: tarjanStronglyConnected,
5769 tscc: tarjanStronglyConnected,
5770 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5771};
5772
5773var elesfn$c = {};
5774[elesfn, elesfn$1, elesfn$2, elesfn$3, elesfn$4, elesfn$5, elesfn$6, elesfn$7, elesfn$8, elesfn$9, elesfn$a, markovClustering$1, kClustering, hierarchicalClustering$1, affinityPropagation$1, elesfn$b, hopcroftTarjanBiconnected$1, tarjanStronglyConnected$1].forEach(function (props) {
5775 extend(elesfn$c, props);
5776});
5777
5778/*!
5779Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5780Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5781Licensed under The MIT License (http://opensource.org/licenses/MIT)
5782*/
5783
5784/* promise states [Promises/A+ 2.1] */
5785var STATE_PENDING = 0;
5786/* [Promises/A+ 2.1.1] */
5787
5788var STATE_FULFILLED = 1;
5789/* [Promises/A+ 2.1.2] */
5790
5791var STATE_REJECTED = 2;
5792/* [Promises/A+ 2.1.3] */
5793
5794/* promise object constructor */
5795
5796var api = function api(executor) {
5797 /* optionally support non-constructor/plain-function call */
5798 if (!(this instanceof api)) return new api(executor);
5799 /* initialize object */
5800
5801 this.id = 'Thenable/1.0.7';
5802 this.state = STATE_PENDING;
5803 /* initial state */
5804
5805 this.fulfillValue = undefined;
5806 /* initial value */
5807
5808 /* [Promises/A+ 1.3, 2.1.2.2] */
5809
5810 this.rejectReason = undefined;
5811 /* initial reason */
5812
5813 /* [Promises/A+ 1.5, 2.1.3.2] */
5814
5815 this.onFulfilled = [];
5816 /* initial handlers */
5817
5818 this.onRejected = [];
5819 /* initial handlers */
5820
5821 /* provide optional information-hiding proxy */
5822
5823 this.proxy = {
5824 then: this.then.bind(this)
5825 };
5826 /* support optional executor function */
5827
5828 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5829};
5830/* promise API methods */
5831
5832
5833api.prototype = {
5834 /* promise resolving methods */
5835 fulfill: function fulfill(value) {
5836 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5837 },
5838 reject: function reject(value) {
5839 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5840 },
5841
5842 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5843 then: function then(onFulfilled, onRejected) {
5844 var curr = this;
5845 var next = new api();
5846 /* [Promises/A+ 2.2.7] */
5847
5848 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5849 /* [Promises/A+ 2.2.2/2.2.6] */
5850
5851 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5852 /* [Promises/A+ 2.2.3/2.2.6] */
5853
5854 execute(curr);
5855 return next.proxy;
5856 /* [Promises/A+ 2.2.7, 3.3] */
5857 }
5858};
5859/* deliver an action */
5860
5861var deliver = function deliver(curr, state, name, value) {
5862 if (curr.state === STATE_PENDING) {
5863 curr.state = state;
5864 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5865
5866 curr[name] = value;
5867 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5868
5869 execute(curr);
5870 }
5871
5872 return curr;
5873};
5874/* execute all handlers */
5875
5876
5877var execute = function execute(curr) {
5878 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5879};
5880/* execute particular set of handlers */
5881
5882
5883var execute_handlers = function execute_handlers(curr, name, value) {
5884 /* global setImmediate: true */
5885
5886 /* global setTimeout: true */
5887
5888 /* short-circuit processing */
5889 if (curr[name].length === 0) return;
5890 /* iterate over all handlers, exactly once */
5891
5892 var handlers = curr[name];
5893 curr[name] = [];
5894 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5895
5896 var func = function func() {
5897 for (var i = 0; i < handlers.length; i++) {
5898 handlers[i](value);
5899 }
5900 /* [Promises/A+ 2.2.5] */
5901
5902 };
5903 /* execute procedure asynchronously */
5904
5905 /* [Promises/A+ 2.2.4, 3.1] */
5906
5907
5908 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5909};
5910/* generate a resolver function */
5911
5912
5913var resolver = function resolver(cb, next, method) {
5914 return function (value) {
5915 if (typeof cb !== 'function')
5916 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5917 next[method].call(next, value);
5918 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5919 else {
5920 var result;
5921
5922 try {
5923 result = cb(value);
5924 }
5925 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5926 catch (e) {
5927 next.reject(e);
5928 /* [Promises/A+ 2.2.7.2] */
5929
5930 return;
5931 }
5932
5933 resolve(next, result);
5934 /* [Promises/A+ 2.2.7.1] */
5935 }
5936 };
5937};
5938/* "Promise Resolution Procedure" */
5939
5940/* [Promises/A+ 2.3] */
5941
5942
5943var resolve = function resolve(promise, x) {
5944 /* sanity check arguments */
5945
5946 /* [Promises/A+ 2.3.1] */
5947 if (promise === x || promise.proxy === x) {
5948 promise.reject(new TypeError('cannot resolve promise with itself'));
5949 return;
5950 }
5951 /* surgically check for a "then" method
5952 (mainly to just call the "getter" of "then" only once) */
5953
5954
5955 var then;
5956
5957 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
5958 try {
5959 then = x.then;
5960 }
5961 /* [Promises/A+ 2.3.3.1, 3.5] */
5962 catch (e) {
5963 promise.reject(e);
5964 /* [Promises/A+ 2.3.3.2] */
5965
5966 return;
5967 }
5968 }
5969 /* handle own Thenables [Promises/A+ 2.3.2]
5970 and similar "thenables" [Promises/A+ 2.3.3] */
5971
5972
5973 if (typeof then === 'function') {
5974 var resolved = false;
5975
5976 try {
5977 /* call retrieved "then" method */
5978
5979 /* [Promises/A+ 2.3.3.3] */
5980 then.call(x,
5981 /* resolvePromise */
5982
5983 /* [Promises/A+ 2.3.3.3.1] */
5984 function (y) {
5985 if (resolved) return;
5986 resolved = true;
5987 /* [Promises/A+ 2.3.3.3.3] */
5988
5989 if (y === x)
5990 /* [Promises/A+ 3.6] */
5991 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
5992 },
5993 /* rejectPromise */
5994
5995 /* [Promises/A+ 2.3.3.3.2] */
5996 function (r) {
5997 if (resolved) return;
5998 resolved = true;
5999 /* [Promises/A+ 2.3.3.3.3] */
6000
6001 promise.reject(r);
6002 });
6003 } catch (e) {
6004 if (!resolved)
6005 /* [Promises/A+ 2.3.3.3.3] */
6006 promise.reject(e);
6007 /* [Promises/A+ 2.3.3.3.4] */
6008 }
6009
6010 return;
6011 }
6012 /* handle other values */
6013
6014
6015 promise.fulfill(x);
6016 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6017}; // so we always have Promise.all()
6018
6019
6020api.all = function (ps) {
6021 return new api(function (resolveAll, rejectAll) {
6022 var vals = new Array(ps.length);
6023 var doneCount = 0;
6024
6025 var fulfill = function fulfill(i, val) {
6026 vals[i] = val;
6027 doneCount++;
6028
6029 if (doneCount === ps.length) {
6030 resolveAll(vals);
6031 }
6032 };
6033
6034 for (var i = 0; i < ps.length; i++) {
6035 (function (i) {
6036 var p = ps[i];
6037 var isPromise = p != null && p.then != null;
6038
6039 if (isPromise) {
6040 p.then(function (val) {
6041 fulfill(i, val);
6042 }, function (err) {
6043 rejectAll(err);
6044 });
6045 } else {
6046 var val = p;
6047 fulfill(i, val);
6048 }
6049 })(i);
6050 }
6051 });
6052};
6053
6054api.resolve = function (val) {
6055 return new api(function (resolve, reject) {
6056 resolve(val);
6057 });
6058};
6059
6060api.reject = function (val) {
6061 return new api(function (resolve, reject) {
6062 reject(val);
6063 });
6064};
6065
6066var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6067
6068var Animation = function Animation(target, opts, opts2) {
6069 var isCore = core(target);
6070 var isEle = !isCore;
6071
6072 var _p = this._private = extend({
6073 duration: 1000
6074 }, opts, opts2);
6075
6076 _p.target = target;
6077 _p.style = _p.style || _p.css;
6078 _p.started = false;
6079 _p.playing = false;
6080 _p.hooked = false;
6081 _p.applying = false;
6082 _p.progress = 0;
6083 _p.completes = [];
6084 _p.frames = [];
6085
6086 if (_p.complete && fn(_p.complete)) {
6087 _p.completes.push(_p.complete);
6088 }
6089
6090 if (isEle) {
6091 var pos = target.position();
6092 _p.startPosition = _p.startPosition || {
6093 x: pos.x,
6094 y: pos.y
6095 };
6096 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6097 }
6098
6099 if (isCore) {
6100 var pan = target.pan();
6101 _p.startPan = {
6102 x: pan.x,
6103 y: pan.y
6104 };
6105 _p.startZoom = target.zoom();
6106 } // for future timeline/animations impl
6107
6108
6109 this.length = 1;
6110 this[0] = this;
6111};
6112
6113var anifn = Animation.prototype;
6114extend(anifn, {
6115 instanceString: function instanceString() {
6116 return 'animation';
6117 },
6118 hook: function hook() {
6119 var _p = this._private;
6120
6121 if (!_p.hooked) {
6122 // add to target's animation queue
6123 var q;
6124 var tAni = _p.target._private.animation;
6125
6126 if (_p.queue) {
6127 q = tAni.queue;
6128 } else {
6129 q = tAni.current;
6130 }
6131
6132 q.push(this); // add to the animation loop pool
6133
6134 if (elementOrCollection(_p.target)) {
6135 _p.target.cy().addToAnimationPool(_p.target);
6136 }
6137
6138 _p.hooked = true;
6139 }
6140
6141 return this;
6142 },
6143 play: function play() {
6144 var _p = this._private; // autorewind
6145
6146 if (_p.progress === 1) {
6147 _p.progress = 0;
6148 }
6149
6150 _p.playing = true;
6151 _p.started = false; // needs to be started by animation loop
6152
6153 _p.stopped = false;
6154 this.hook(); // the animation loop will start the animation...
6155
6156 return this;
6157 },
6158 playing: function playing() {
6159 return this._private.playing;
6160 },
6161 apply: function apply() {
6162 var _p = this._private;
6163 _p.applying = true;
6164 _p.started = false; // needs to be started by animation loop
6165
6166 _p.stopped = false;
6167 this.hook(); // the animation loop will apply the animation at this progress
6168
6169 return this;
6170 },
6171 applying: function applying() {
6172 return this._private.applying;
6173 },
6174 pause: function pause() {
6175 var _p = this._private;
6176 _p.playing = false;
6177 _p.started = false;
6178 return this;
6179 },
6180 stop: function stop() {
6181 var _p = this._private;
6182 _p.playing = false;
6183 _p.started = false;
6184 _p.stopped = true; // to be removed from animation queues
6185
6186 return this;
6187 },
6188 rewind: function rewind() {
6189 return this.progress(0);
6190 },
6191 fastforward: function fastforward() {
6192 return this.progress(1);
6193 },
6194 time: function time(t) {
6195 var _p = this._private;
6196
6197 if (t === undefined) {
6198 return _p.progress * _p.duration;
6199 } else {
6200 return this.progress(t / _p.duration);
6201 }
6202 },
6203 progress: function progress(p) {
6204 var _p = this._private;
6205 var wasPlaying = _p.playing;
6206
6207 if (p === undefined) {
6208 return _p.progress;
6209 } else {
6210 if (wasPlaying) {
6211 this.pause();
6212 }
6213
6214 _p.progress = p;
6215 _p.started = false;
6216
6217 if (wasPlaying) {
6218 this.play();
6219 }
6220 }
6221
6222 return this;
6223 },
6224 completed: function completed() {
6225 return this._private.progress === 1;
6226 },
6227 reverse: function reverse() {
6228 var _p = this._private;
6229 var wasPlaying = _p.playing;
6230
6231 if (wasPlaying) {
6232 this.pause();
6233 }
6234
6235 _p.progress = 1 - _p.progress;
6236 _p.started = false;
6237
6238 var swap = function swap(a, b) {
6239 var _pa = _p[a];
6240
6241 if (_pa == null) {
6242 return;
6243 }
6244
6245 _p[a] = _p[b];
6246 _p[b] = _pa;
6247 };
6248
6249 swap('zoom', 'startZoom');
6250 swap('pan', 'startPan');
6251 swap('position', 'startPosition'); // swap styles
6252
6253 if (_p.style) {
6254 for (var i = 0; i < _p.style.length; i++) {
6255 var prop = _p.style[i];
6256 var name = prop.name;
6257 var startStyleProp = _p.startStyle[name];
6258 _p.startStyle[name] = prop;
6259 _p.style[i] = startStyleProp;
6260 }
6261 }
6262
6263 if (wasPlaying) {
6264 this.play();
6265 }
6266
6267 return this;
6268 },
6269 promise: function promise(type) {
6270 var _p = this._private;
6271 var arr;
6272
6273 switch (type) {
6274 case 'frame':
6275 arr = _p.frames;
6276 break;
6277
6278 default:
6279 case 'complete':
6280 case 'completed':
6281 arr = _p.completes;
6282 }
6283
6284 return new Promise$1(function (resolve, reject) {
6285 arr.push(function () {
6286 resolve();
6287 });
6288 });
6289 }
6290});
6291anifn.complete = anifn.completed;
6292anifn.run = anifn.play;
6293anifn.running = anifn.playing;
6294
6295var define = {
6296 animated: function animated() {
6297 return function animatedImpl() {
6298 var self = this;
6299 var selfIsArrayLike = self.length !== undefined;
6300 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6301
6302 var cy = this._private.cy || this;
6303
6304 if (!cy.styleEnabled()) {
6305 return false;
6306 }
6307
6308 var ele = all[0];
6309
6310 if (ele) {
6311 return ele._private.animation.current.length > 0;
6312 }
6313 };
6314 },
6315 // animated
6316 clearQueue: function clearQueue() {
6317 return function clearQueueImpl() {
6318 var self = this;
6319 var selfIsArrayLike = self.length !== undefined;
6320 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6321
6322 var cy = this._private.cy || this;
6323
6324 if (!cy.styleEnabled()) {
6325 return this;
6326 }
6327
6328 for (var i = 0; i < all.length; i++) {
6329 var ele = all[i];
6330 ele._private.animation.queue = [];
6331 }
6332
6333 return this;
6334 };
6335 },
6336 // clearQueue
6337 delay: function delay() {
6338 return function delayImpl(time, complete) {
6339 var cy = this._private.cy || this;
6340
6341 if (!cy.styleEnabled()) {
6342 return this;
6343 }
6344
6345 return this.animate({
6346 delay: time,
6347 duration: time,
6348 complete: complete
6349 });
6350 };
6351 },
6352 // delay
6353 delayAnimation: function delayAnimation() {
6354 return function delayAnimationImpl(time, complete) {
6355 var cy = this._private.cy || this;
6356
6357 if (!cy.styleEnabled()) {
6358 return this;
6359 }
6360
6361 return this.animation({
6362 delay: time,
6363 duration: time,
6364 complete: complete
6365 });
6366 };
6367 },
6368 // delay
6369 animation: function animation() {
6370 return function animationImpl(properties, params) {
6371 var self = this;
6372 var selfIsArrayLike = self.length !== undefined;
6373 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6374
6375 var cy = this._private.cy || this;
6376 var isCore = !selfIsArrayLike;
6377 var isEles = !isCore;
6378
6379 if (!cy.styleEnabled()) {
6380 return this;
6381 }
6382
6383 var style = cy.style();
6384 properties = extend({}, properties, params);
6385 var propertiesEmpty = Object.keys(properties).length === 0;
6386
6387 if (propertiesEmpty) {
6388 return new Animation(all[0], properties); // nothing to animate
6389 }
6390
6391 if (properties.duration === undefined) {
6392 properties.duration = 400;
6393 }
6394
6395 switch (properties.duration) {
6396 case 'slow':
6397 properties.duration = 600;
6398 break;
6399
6400 case 'fast':
6401 properties.duration = 200;
6402 break;
6403 }
6404
6405 if (isEles) {
6406 properties.style = style.getPropsList(properties.style || properties.css);
6407 properties.css = undefined;
6408 }
6409
6410 if (isEles && properties.renderedPosition != null) {
6411 var rpos = properties.renderedPosition;
6412 var pan = cy.pan();
6413 var zoom = cy.zoom();
6414 properties.position = renderedToModelPosition(rpos, zoom, pan);
6415 } // override pan w/ panBy if set
6416
6417
6418 if (isCore && properties.panBy != null) {
6419 var panBy = properties.panBy;
6420 var cyPan = cy.pan();
6421 properties.pan = {
6422 x: cyPan.x + panBy.x,
6423 y: cyPan.y + panBy.y
6424 };
6425 } // override pan w/ center if set
6426
6427
6428 var center = properties.center || properties.centre;
6429
6430 if (isCore && center != null) {
6431 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6432
6433 if (centerPan != null) {
6434 properties.pan = centerPan;
6435 }
6436 } // override pan & zoom w/ fit if set
6437
6438
6439 if (isCore && properties.fit != null) {
6440 var fit = properties.fit;
6441 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6442
6443 if (fitVp != null) {
6444 properties.pan = fitVp.pan;
6445 properties.zoom = fitVp.zoom;
6446 }
6447 } // override zoom (& potentially pan) w/ zoom obj if set
6448
6449
6450 if (isCore && plainObject(properties.zoom)) {
6451 var vp = cy.getZoomedViewport(properties.zoom);
6452
6453 if (vp != null) {
6454 if (vp.zoomed) {
6455 properties.zoom = vp.zoom;
6456 }
6457
6458 if (vp.panned) {
6459 properties.pan = vp.pan;
6460 }
6461 } else {
6462 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6463 }
6464 }
6465
6466 return new Animation(all[0], properties);
6467 };
6468 },
6469 // animate
6470 animate: function animate() {
6471 return function animateImpl(properties, params) {
6472 var self = this;
6473 var selfIsArrayLike = self.length !== undefined;
6474 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6475
6476 var cy = this._private.cy || this;
6477
6478 if (!cy.styleEnabled()) {
6479 return this;
6480 }
6481
6482 if (params) {
6483 properties = extend({}, properties, params);
6484 } // manually hook and run the animation
6485
6486
6487 for (var i = 0; i < all.length; i++) {
6488 var ele = all[i];
6489 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6490 var ani = ele.animation(properties, queue ? {
6491 queue: true
6492 } : undefined);
6493 ani.play();
6494 }
6495
6496 return this; // chaining
6497 };
6498 },
6499 // animate
6500 stop: function stop() {
6501 return function stopImpl(clearQueue, jumpToEnd) {
6502 var self = this;
6503 var selfIsArrayLike = self.length !== undefined;
6504 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6505
6506 var cy = this._private.cy || this;
6507
6508 if (!cy.styleEnabled()) {
6509 return this;
6510 }
6511
6512 for (var i = 0; i < all.length; i++) {
6513 var ele = all[i];
6514 var _p = ele._private;
6515 var anis = _p.animation.current;
6516
6517 for (var j = 0; j < anis.length; j++) {
6518 var ani = anis[j];
6519 var ani_p = ani._private;
6520
6521 if (jumpToEnd) {
6522 // next iteration of the animation loop, the animation
6523 // will go straight to the end and be removed
6524 ani_p.duration = 0;
6525 }
6526 } // clear the queue of future animations
6527
6528
6529 if (clearQueue) {
6530 _p.animation.queue = [];
6531 }
6532
6533 if (!jumpToEnd) {
6534 _p.animation.current = [];
6535 }
6536 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6537
6538
6539 cy.notify('draw');
6540 return this;
6541 };
6542 } // stop
6543
6544}; // define
6545
6546var define$1 = {
6547 // access data field
6548 data: function data(params) {
6549 var defaults = {
6550 field: 'data',
6551 bindingEvent: 'data',
6552 allowBinding: false,
6553 allowSetting: false,
6554 allowGetting: false,
6555 settingEvent: 'data',
6556 settingTriggersEvent: false,
6557 triggerFnName: 'trigger',
6558 immutableKeys: {},
6559 // key => true if immutable
6560 updateStyle: false,
6561 beforeGet: function beforeGet(self) {},
6562 beforeSet: function beforeSet(self, obj) {},
6563 onSet: function onSet(self) {},
6564 canSet: function canSet(self) {
6565 return true;
6566 }
6567 };
6568 params = extend({}, defaults, params);
6569 return function dataImpl(name, value) {
6570 var p = params;
6571 var self = this;
6572 var selfIsArrayLike = self.length !== undefined;
6573 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6574
6575 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6576
6577 if (string(name)) {
6578 // set or get property
6579 // .data('foo')
6580 if (p.allowGetting && value === undefined) {
6581 // get
6582 var ret;
6583
6584 if (single) {
6585 p.beforeGet(single);
6586 ret = single._private[p.field][name];
6587 }
6588
6589 return ret; // .data('foo', 'bar')
6590 } else if (p.allowSetting && value !== undefined) {
6591 // set
6592 var valid = !p.immutableKeys[name];
6593
6594 if (valid) {
6595 var change = _defineProperty({}, name, value);
6596
6597 p.beforeSet(self, change);
6598
6599 for (var i = 0, l = all.length; i < l; i++) {
6600 var ele = all[i];
6601
6602 if (p.canSet(ele)) {
6603 ele._private[p.field][name] = value;
6604 }
6605 } // update mappers if asked
6606
6607
6608 if (p.updateStyle) {
6609 self.updateStyle();
6610 } // call onSet callback
6611
6612
6613 p.onSet(self);
6614
6615 if (p.settingTriggersEvent) {
6616 self[p.triggerFnName](p.settingEvent);
6617 }
6618 }
6619 } // .data({ 'foo': 'bar' })
6620
6621 } else if (p.allowSetting && plainObject(name)) {
6622 // extend
6623 var obj = name;
6624 var k, v;
6625 var keys = Object.keys(obj);
6626 p.beforeSet(self, obj);
6627
6628 for (var _i = 0; _i < keys.length; _i++) {
6629 k = keys[_i];
6630 v = obj[k];
6631
6632 var _valid = !p.immutableKeys[k];
6633
6634 if (_valid) {
6635 for (var j = 0; j < all.length; j++) {
6636 var _ele = all[j];
6637
6638 if (p.canSet(_ele)) {
6639 _ele._private[p.field][k] = v;
6640 }
6641 }
6642 }
6643 } // update mappers if asked
6644
6645
6646 if (p.updateStyle) {
6647 self.updateStyle();
6648 } // call onSet callback
6649
6650
6651 p.onSet(self);
6652
6653 if (p.settingTriggersEvent) {
6654 self[p.triggerFnName](p.settingEvent);
6655 } // .data(function(){ ... })
6656
6657 } else if (p.allowBinding && fn(name)) {
6658 // bind to event
6659 var fn$1 = name;
6660 self.on(p.bindingEvent, fn$1); // .data()
6661 } else if (p.allowGetting && name === undefined) {
6662 // get whole object
6663 var _ret;
6664
6665 if (single) {
6666 p.beforeGet(single);
6667 _ret = single._private[p.field];
6668 }
6669
6670 return _ret;
6671 }
6672
6673 return self; // maintain chainability
6674 }; // function
6675 },
6676 // data
6677 // remove data field
6678 removeData: function removeData(params) {
6679 var defaults = {
6680 field: 'data',
6681 event: 'data',
6682 triggerFnName: 'trigger',
6683 triggerEvent: false,
6684 immutableKeys: {} // key => true if immutable
6685
6686 };
6687 params = extend({}, defaults, params);
6688 return function removeDataImpl(names) {
6689 var p = params;
6690 var self = this;
6691 var selfIsArrayLike = self.length !== undefined;
6692 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6693 // .removeData('foo bar')
6694
6695 if (string(names)) {
6696 // then get the list of keys, and delete them
6697 var keys = names.split(/\s+/);
6698 var l = keys.length;
6699
6700 for (var i = 0; i < l; i++) {
6701 // delete each non-empty key
6702 var key = keys[i];
6703
6704 if (emptyString(key)) {
6705 continue;
6706 }
6707
6708 var valid = !p.immutableKeys[key]; // not valid if immutable
6709
6710 if (valid) {
6711 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6712 all[i_a]._private[p.field][key] = undefined;
6713 }
6714 }
6715 }
6716
6717 if (p.triggerEvent) {
6718 self[p.triggerFnName](p.event);
6719 } // .removeData()
6720
6721 } else if (names === undefined) {
6722 // then delete all keys
6723 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6724 var _privateFields = all[_i_a]._private[p.field];
6725
6726 var _keys = Object.keys(_privateFields);
6727
6728 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6729 var _key = _keys[_i2];
6730 var validKeyToDelete = !p.immutableKeys[_key];
6731
6732 if (validKeyToDelete) {
6733 _privateFields[_key] = undefined;
6734 }
6735 }
6736 }
6737
6738 if (p.triggerEvent) {
6739 self[p.triggerFnName](p.event);
6740 }
6741 }
6742
6743 return self; // maintain chaining
6744 }; // function
6745 } // removeData
6746
6747}; // define
6748
6749var define$2 = {
6750 eventAliasesOn: function eventAliasesOn(proto) {
6751 var p = proto;
6752 p.addListener = p.listen = p.bind = p.on;
6753 p.unlisten = p.unbind = p.off = p.removeListener;
6754 p.trigger = p.emit; // this is just a wrapper alias of .on()
6755
6756 p.pon = p.promiseOn = function (events, selector) {
6757 var self = this;
6758 var args = Array.prototype.slice.call(arguments, 0);
6759 return new Promise$1(function (resolve, reject) {
6760 var callback = function callback(e) {
6761 self.off.apply(self, offArgs);
6762 resolve(e);
6763 };
6764
6765 var onArgs = args.concat([callback]);
6766 var offArgs = onArgs.concat([]);
6767 self.on.apply(self, onArgs);
6768 });
6769 };
6770 }
6771}; // define
6772
6773// use this module to cherry pick functions into your prototype
6774var define$3 = {};
6775[define, define$1, define$2].forEach(function (m) {
6776 extend(define$3, m);
6777});
6778
6779var elesfn$d = {
6780 animate: define$3.animate(),
6781 animation: define$3.animation(),
6782 animated: define$3.animated(),
6783 clearQueue: define$3.clearQueue(),
6784 delay: define$3.delay(),
6785 delayAnimation: define$3.delayAnimation(),
6786 stop: define$3.stop()
6787};
6788
6789var elesfn$e = {
6790 classes: function classes(_classes) {
6791 var self = this;
6792
6793 if (_classes === undefined) {
6794 var ret = [];
6795
6796 self[0]._private.classes.forEach(function (cls) {
6797 return ret.push(cls);
6798 });
6799
6800 return ret;
6801 } else if (!array(_classes)) {
6802 // extract classes from string
6803 _classes = (_classes || '').match(/\S+/g) || [];
6804 }
6805
6806 var changed = [];
6807 var classesSet = new Set$1(_classes); // check and update each ele
6808
6809 for (var j = 0; j < self.length; j++) {
6810 var ele = self[j];
6811 var _p = ele._private;
6812 var eleClasses = _p.classes;
6813 var changedEle = false; // check if ele has all of the passed classes
6814
6815 for (var i = 0; i < _classes.length; i++) {
6816 var cls = _classes[i];
6817 var eleHasClass = eleClasses.has(cls);
6818
6819 if (!eleHasClass) {
6820 changedEle = true;
6821 break;
6822 }
6823 } // check if ele has classes outside of those passed
6824
6825
6826 if (!changedEle) {
6827 changedEle = eleClasses.size !== _classes.length;
6828 }
6829
6830 if (changedEle) {
6831 _p.classes = classesSet;
6832 changed.push(ele);
6833 }
6834 } // trigger update style on those eles that had class changes
6835
6836
6837 if (changed.length > 0) {
6838 this.spawn(changed).updateStyle().emit('class');
6839 }
6840
6841 return self;
6842 },
6843 addClass: function addClass(classes) {
6844 return this.toggleClass(classes, true);
6845 },
6846 hasClass: function hasClass(className) {
6847 var ele = this[0];
6848 return ele != null && ele._private.classes.has(className);
6849 },
6850 toggleClass: function toggleClass(classes, toggle) {
6851 if (!array(classes)) {
6852 // extract classes from string
6853 classes = classes.match(/\S+/g) || [];
6854 }
6855
6856 var self = this;
6857 var toggleUndefd = toggle === undefined;
6858 var changed = []; // eles who had classes changed
6859
6860 for (var i = 0, il = self.length; i < il; i++) {
6861 var ele = self[i];
6862 var eleClasses = ele._private.classes;
6863 var changedEle = false;
6864
6865 for (var j = 0; j < classes.length; j++) {
6866 var cls = classes[j];
6867 var hasClass = eleClasses.has(cls);
6868 var changedNow = false;
6869
6870 if (toggle || toggleUndefd && !hasClass) {
6871 eleClasses.add(cls);
6872 changedNow = true;
6873 } else if (!toggle || toggleUndefd && hasClass) {
6874 eleClasses["delete"](cls);
6875 changedNow = true;
6876 }
6877
6878 if (!changedEle && changedNow) {
6879 changed.push(ele);
6880 changedEle = true;
6881 }
6882 } // for j classes
6883
6884 } // for i eles
6885 // trigger update style on those eles that had class changes
6886
6887
6888 if (changed.length > 0) {
6889 this.spawn(changed).updateStyle().emit('class');
6890 }
6891
6892 return self;
6893 },
6894 removeClass: function removeClass(classes) {
6895 return this.toggleClass(classes, false);
6896 },
6897 flashClass: function flashClass(classes, duration) {
6898 var self = this;
6899
6900 if (duration == null) {
6901 duration = 250;
6902 } else if (duration === 0) {
6903 return self; // nothing to do really
6904 }
6905
6906 self.addClass(classes);
6907 setTimeout(function () {
6908 self.removeClass(classes);
6909 }, duration);
6910 return self;
6911 }
6912};
6913elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6914
6915var tokens = {
6916 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6917 // chars we need to escape in let names, etc
6918 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6919 // binary comparison op (used in data selectors)
6920 boolOp: '\\?|\\!|\\^',
6921 // boolean (unary) operators (used in data selectors)
6922 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6923 // string literals (used in data selectors) -- doublequotes | singlequotes
6924 number: number$1,
6925 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6926 meta: 'degree|indegree|outdegree',
6927 // allowed metadata fields (i.e. allowed functions to use from Collection)
6928 separator: '\\s*,\\s*',
6929 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6930 descendant: '\\s+',
6931 child: '\\s+>\\s+',
6932 subject: '\\$',
6933 group: 'node|edge|\\*',
6934 directedEdge: '\\s+->\\s+',
6935 undirectedEdge: '\\s+<->\\s+'
6936};
6937tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
6938
6939tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
6940
6941tokens.className = tokens.variable; // a class name (follows variable conventions)
6942
6943tokens.id = tokens.variable; // an element id (follows variable conventions)
6944
6945(function () {
6946 var ops, op, i; // add @ variants to comparatorOp
6947
6948 ops = tokens.comparatorOp.split('|');
6949
6950 for (i = 0; i < ops.length; i++) {
6951 op = ops[i];
6952 tokens.comparatorOp += '|@' + op;
6953 } // add ! variants to comparatorOp
6954
6955
6956 ops = tokens.comparatorOp.split('|');
6957
6958 for (i = 0; i < ops.length; i++) {
6959 op = ops[i];
6960
6961 if (op.indexOf('!') >= 0) {
6962 continue;
6963 } // skip ops that explicitly contain !
6964
6965
6966 if (op === '=') {
6967 continue;
6968 } // skip = b/c != is explicitly defined
6969
6970
6971 tokens.comparatorOp += '|\\!' + op;
6972 }
6973})();
6974
6975/**
6976 * Make a new query object
6977 *
6978 * @prop type {Type} The type enum (int) of the query
6979 * @prop checks List of checks to make against an ele to test for a match
6980 */
6981var newQuery = function newQuery() {
6982 return {
6983 checks: []
6984 };
6985};
6986
6987/**
6988 * A check type enum-like object. Uses integer values for fast match() lookup.
6989 * The ordering does not matter as long as the ints are unique.
6990 */
6991var Type = {
6992 /** E.g. node */
6993 GROUP: 0,
6994
6995 /** A collection of elements */
6996 COLLECTION: 1,
6997
6998 /** A filter(ele) function */
6999 FILTER: 2,
7000
7001 /** E.g. [foo > 1] */
7002 DATA_COMPARE: 3,
7003
7004 /** E.g. [foo] */
7005 DATA_EXIST: 4,
7006
7007 /** E.g. [?foo] */
7008 DATA_BOOL: 5,
7009
7010 /** E.g. [[degree > 2]] */
7011 META_COMPARE: 6,
7012
7013 /** E.g. :selected */
7014 STATE: 7,
7015
7016 /** E.g. #foo */
7017 ID: 8,
7018
7019 /** E.g. .foo */
7020 CLASS: 9,
7021
7022 /** E.g. #foo <-> #bar */
7023 UNDIRECTED_EDGE: 10,
7024
7025 /** E.g. #foo -> #bar */
7026 DIRECTED_EDGE: 11,
7027
7028 /** E.g. $#foo -> #bar */
7029 NODE_SOURCE: 12,
7030
7031 /** E.g. #foo -> $#bar */
7032 NODE_TARGET: 13,
7033
7034 /** E.g. $#foo <-> #bar */
7035 NODE_NEIGHBOR: 14,
7036
7037 /** E.g. #foo > #bar */
7038 CHILD: 15,
7039
7040 /** E.g. #foo #bar */
7041 DESCENDANT: 16,
7042
7043 /** E.g. $#foo > #bar */
7044 PARENT: 17,
7045
7046 /** E.g. $#foo #bar */
7047 ANCESTOR: 18,
7048
7049 /** E.g. #foo > $bar > #baz */
7050 COMPOUND_SPLIT: 19,
7051
7052 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7053 TRUE: 20
7054};
7055
7056var stateSelectors = [{
7057 selector: ':selected',
7058 matches: function matches(ele) {
7059 return ele.selected();
7060 }
7061}, {
7062 selector: ':unselected',
7063 matches: function matches(ele) {
7064 return !ele.selected();
7065 }
7066}, {
7067 selector: ':selectable',
7068 matches: function matches(ele) {
7069 return ele.selectable();
7070 }
7071}, {
7072 selector: ':unselectable',
7073 matches: function matches(ele) {
7074 return !ele.selectable();
7075 }
7076}, {
7077 selector: ':locked',
7078 matches: function matches(ele) {
7079 return ele.locked();
7080 }
7081}, {
7082 selector: ':unlocked',
7083 matches: function matches(ele) {
7084 return !ele.locked();
7085 }
7086}, {
7087 selector: ':visible',
7088 matches: function matches(ele) {
7089 return ele.visible();
7090 }
7091}, {
7092 selector: ':hidden',
7093 matches: function matches(ele) {
7094 return !ele.visible();
7095 }
7096}, {
7097 selector: ':transparent',
7098 matches: function matches(ele) {
7099 return ele.transparent();
7100 }
7101}, {
7102 selector: ':grabbed',
7103 matches: function matches(ele) {
7104 return ele.grabbed();
7105 }
7106}, {
7107 selector: ':free',
7108 matches: function matches(ele) {
7109 return !ele.grabbed();
7110 }
7111}, {
7112 selector: ':removed',
7113 matches: function matches(ele) {
7114 return ele.removed();
7115 }
7116}, {
7117 selector: ':inside',
7118 matches: function matches(ele) {
7119 return !ele.removed();
7120 }
7121}, {
7122 selector: ':grabbable',
7123 matches: function matches(ele) {
7124 return ele.grabbable();
7125 }
7126}, {
7127 selector: ':ungrabbable',
7128 matches: function matches(ele) {
7129 return !ele.grabbable();
7130 }
7131}, {
7132 selector: ':animated',
7133 matches: function matches(ele) {
7134 return ele.animated();
7135 }
7136}, {
7137 selector: ':unanimated',
7138 matches: function matches(ele) {
7139 return !ele.animated();
7140 }
7141}, {
7142 selector: ':parent',
7143 matches: function matches(ele) {
7144 return ele.isParent();
7145 }
7146}, {
7147 selector: ':childless',
7148 matches: function matches(ele) {
7149 return ele.isChildless();
7150 }
7151}, {
7152 selector: ':child',
7153 matches: function matches(ele) {
7154 return ele.isChild();
7155 }
7156}, {
7157 selector: ':orphan',
7158 matches: function matches(ele) {
7159 return ele.isOrphan();
7160 }
7161}, {
7162 selector: ':nonorphan',
7163 matches: function matches(ele) {
7164 return ele.isChild();
7165 }
7166}, {
7167 selector: ':compound',
7168 matches: function matches(ele) {
7169 if (ele.isNode()) {
7170 return ele.isParent();
7171 } else {
7172 return ele.source().isParent() || ele.target().isParent();
7173 }
7174 }
7175}, {
7176 selector: ':loop',
7177 matches: function matches(ele) {
7178 return ele.isLoop();
7179 }
7180}, {
7181 selector: ':simple',
7182 matches: function matches(ele) {
7183 return ele.isSimple();
7184 }
7185}, {
7186 selector: ':active',
7187 matches: function matches(ele) {
7188 return ele.active();
7189 }
7190}, {
7191 selector: ':inactive',
7192 matches: function matches(ele) {
7193 return !ele.active();
7194 }
7195}, {
7196 selector: ':backgrounding',
7197 matches: function matches(ele) {
7198 return ele.backgrounding();
7199 }
7200}, {
7201 selector: ':nonbackgrounding',
7202 matches: function matches(ele) {
7203 return !ele.backgrounding();
7204 }
7205}].sort(function (a, b) {
7206 // n.b. selectors that are starting substrings of others must have the longer ones first
7207 return descending(a.selector, b.selector);
7208});
7209
7210var lookup = function () {
7211 var selToFn = {};
7212 var s;
7213
7214 for (var i = 0; i < stateSelectors.length; i++) {
7215 s = stateSelectors[i];
7216 selToFn[s.selector] = s.matches;
7217 }
7218
7219 return selToFn;
7220}();
7221
7222var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7223 return lookup[sel](ele);
7224};
7225var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7226 return s.selector;
7227}).join('|') + ')';
7228
7229// so that values get compared properly in Selector.filter()
7230
7231var cleanMetaChars = function cleanMetaChars(str) {
7232 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7233 return $1;
7234 });
7235};
7236
7237var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7238 selector[selector.length - 1] = replacementQuery;
7239}; // NOTE: add new expression syntax here to have it recognised by the parser;
7240// - a query contains all adjacent (i.e. no separator in between) expressions;
7241// - the current query is stored in selector[i]
7242// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7243
7244
7245var exprs = [{
7246 name: 'group',
7247 // just used for identifying when debugging
7248 query: true,
7249 regex: '(' + tokens.group + ')',
7250 populate: function populate(selector, query, _ref) {
7251 var _ref2 = _slicedToArray(_ref, 1),
7252 group = _ref2[0];
7253
7254 query.checks.push({
7255 type: Type.GROUP,
7256 value: group === '*' ? group : group + 's'
7257 });
7258 }
7259}, {
7260 name: 'state',
7261 query: true,
7262 regex: stateSelectorRegex,
7263 populate: function populate(selector, query, _ref3) {
7264 var _ref4 = _slicedToArray(_ref3, 1),
7265 state = _ref4[0];
7266
7267 query.checks.push({
7268 type: Type.STATE,
7269 value: state
7270 });
7271 }
7272}, {
7273 name: 'id',
7274 query: true,
7275 regex: '\\#(' + tokens.id + ')',
7276 populate: function populate(selector, query, _ref5) {
7277 var _ref6 = _slicedToArray(_ref5, 1),
7278 id = _ref6[0];
7279
7280 query.checks.push({
7281 type: Type.ID,
7282 value: cleanMetaChars(id)
7283 });
7284 }
7285}, {
7286 name: 'className',
7287 query: true,
7288 regex: '\\.(' + tokens.className + ')',
7289 populate: function populate(selector, query, _ref7) {
7290 var _ref8 = _slicedToArray(_ref7, 1),
7291 className = _ref8[0];
7292
7293 query.checks.push({
7294 type: Type.CLASS,
7295 value: cleanMetaChars(className)
7296 });
7297 }
7298}, {
7299 name: 'dataExists',
7300 query: true,
7301 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7302 populate: function populate(selector, query, _ref9) {
7303 var _ref10 = _slicedToArray(_ref9, 1),
7304 variable = _ref10[0];
7305
7306 query.checks.push({
7307 type: Type.DATA_EXIST,
7308 field: cleanMetaChars(variable)
7309 });
7310 }
7311}, {
7312 name: 'dataCompare',
7313 query: true,
7314 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7315 populate: function populate(selector, query, _ref11) {
7316 var _ref12 = _slicedToArray(_ref11, 3),
7317 variable = _ref12[0],
7318 comparatorOp = _ref12[1],
7319 value = _ref12[2];
7320
7321 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7322
7323 if (valueIsString) {
7324 value = value.substring(1, value.length - 1);
7325 } else {
7326 value = parseFloat(value);
7327 }
7328
7329 query.checks.push({
7330 type: Type.DATA_COMPARE,
7331 field: cleanMetaChars(variable),
7332 operator: comparatorOp,
7333 value: value
7334 });
7335 }
7336}, {
7337 name: 'dataBool',
7338 query: true,
7339 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7340 populate: function populate(selector, query, _ref13) {
7341 var _ref14 = _slicedToArray(_ref13, 2),
7342 boolOp = _ref14[0],
7343 variable = _ref14[1];
7344
7345 query.checks.push({
7346 type: Type.DATA_BOOL,
7347 field: cleanMetaChars(variable),
7348 operator: boolOp
7349 });
7350 }
7351}, {
7352 name: 'metaCompare',
7353 query: true,
7354 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7355 populate: function populate(selector, query, _ref15) {
7356 var _ref16 = _slicedToArray(_ref15, 3),
7357 meta = _ref16[0],
7358 comparatorOp = _ref16[1],
7359 number = _ref16[2];
7360
7361 query.checks.push({
7362 type: Type.META_COMPARE,
7363 field: cleanMetaChars(meta),
7364 operator: comparatorOp,
7365 value: parseFloat(number)
7366 });
7367 }
7368}, {
7369 name: 'nextQuery',
7370 separator: true,
7371 regex: tokens.separator,
7372 populate: function populate(selector, query) {
7373 var currentSubject = selector.currentSubject;
7374 var edgeCount = selector.edgeCount;
7375 var compoundCount = selector.compoundCount;
7376 var lastQ = selector[selector.length - 1];
7377
7378 if (currentSubject != null) {
7379 lastQ.subject = currentSubject;
7380 selector.currentSubject = null;
7381 }
7382
7383 lastQ.edgeCount = edgeCount;
7384 lastQ.compoundCount = compoundCount;
7385 selector.edgeCount = 0;
7386 selector.compoundCount = 0; // go on to next query
7387
7388 var nextQuery = selector[selector.length++] = newQuery();
7389 return nextQuery; // this is the new query to be filled by the following exprs
7390 }
7391}, {
7392 name: 'directedEdge',
7393 separator: true,
7394 regex: tokens.directedEdge,
7395 populate: function populate(selector, query) {
7396 if (selector.currentSubject == null) {
7397 // undirected edge
7398 var edgeQuery = newQuery();
7399 var source = query;
7400 var target = newQuery();
7401 edgeQuery.checks.push({
7402 type: Type.DIRECTED_EDGE,
7403 source: source,
7404 target: target
7405 }); // the query in the selector should be the edge rather than the source
7406
7407 replaceLastQuery(selector, query, edgeQuery);
7408 selector.edgeCount++; // we're now populating the target query with expressions that follow
7409
7410 return target;
7411 } else {
7412 // source/target
7413 var srcTgtQ = newQuery();
7414 var _source = query;
7415
7416 var _target = newQuery();
7417
7418 srcTgtQ.checks.push({
7419 type: Type.NODE_SOURCE,
7420 source: _source,
7421 target: _target
7422 }); // the query in the selector should be the neighbourhood rather than the node
7423
7424 replaceLastQuery(selector, query, srcTgtQ);
7425 selector.edgeCount++;
7426 return _target; // now populating the target with the following expressions
7427 }
7428 }
7429}, {
7430 name: 'undirectedEdge',
7431 separator: true,
7432 regex: tokens.undirectedEdge,
7433 populate: function populate(selector, query) {
7434 if (selector.currentSubject == null) {
7435 // undirected edge
7436 var edgeQuery = newQuery();
7437 var source = query;
7438 var target = newQuery();
7439 edgeQuery.checks.push({
7440 type: Type.UNDIRECTED_EDGE,
7441 nodes: [source, target]
7442 }); // the query in the selector should be the edge rather than the source
7443
7444 replaceLastQuery(selector, query, edgeQuery);
7445 selector.edgeCount++; // we're now populating the target query with expressions that follow
7446
7447 return target;
7448 } else {
7449 // neighbourhood
7450 var nhoodQ = newQuery();
7451 var node = query;
7452 var neighbor = newQuery();
7453 nhoodQ.checks.push({
7454 type: Type.NODE_NEIGHBOR,
7455 node: node,
7456 neighbor: neighbor
7457 }); // the query in the selector should be the neighbourhood rather than the node
7458
7459 replaceLastQuery(selector, query, nhoodQ);
7460 return neighbor; // now populating the neighbor with following expressions
7461 }
7462 }
7463}, {
7464 name: 'child',
7465 separator: true,
7466 regex: tokens.child,
7467 populate: function populate(selector, query) {
7468 if (selector.currentSubject == null) {
7469 // default: child query
7470 var parentChildQuery = newQuery();
7471 var child = newQuery();
7472 var parent = selector[selector.length - 1];
7473 parentChildQuery.checks.push({
7474 type: Type.CHILD,
7475 parent: parent,
7476 child: child
7477 }); // the query in the selector should be the '>' itself
7478
7479 replaceLastQuery(selector, query, parentChildQuery);
7480 selector.compoundCount++; // we're now populating the child query with expressions that follow
7481
7482 return child;
7483 } else if (selector.currentSubject === query) {
7484 // compound split query
7485 var compound = newQuery();
7486 var left = selector[selector.length - 1];
7487 var right = newQuery();
7488 var subject = newQuery();
7489
7490 var _child = newQuery();
7491
7492 var _parent = newQuery(); // set up the root compound q
7493
7494
7495 compound.checks.push({
7496 type: Type.COMPOUND_SPLIT,
7497 left: left,
7498 right: right,
7499 subject: subject
7500 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7501
7502 subject.checks = query.checks; // take the checks from the left
7503
7504 query.checks = [{
7505 type: Type.TRUE
7506 }]; // checks under left refs the subject implicitly
7507 // set up the right q
7508
7509 _parent.checks.push({
7510 type: Type.TRUE
7511 }); // parent implicitly refs the subject
7512
7513
7514 right.checks.push({
7515 type: Type.PARENT,
7516 // type is swapped on right side queries
7517 parent: _parent,
7518 child: _child // empty for now
7519
7520 });
7521 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7522
7523 selector.currentSubject = subject;
7524 selector.compoundCount++;
7525 return _child; // now populating the right side's child
7526 } else {
7527 // parent query
7528 // info for parent query
7529 var _parent2 = newQuery();
7530
7531 var _child2 = newQuery();
7532
7533 var pcQChecks = [{
7534 type: Type.PARENT,
7535 parent: _parent2,
7536 child: _child2
7537 }]; // the parent-child query takes the place of the query previously being populated
7538
7539 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7540
7541 query.checks = pcQChecks; // pc query takes over
7542
7543 selector.compoundCount++;
7544 return _child2; // we're now populating the child
7545 }
7546 }
7547}, {
7548 name: 'descendant',
7549 separator: true,
7550 regex: tokens.descendant,
7551 populate: function populate(selector, query) {
7552 if (selector.currentSubject == null) {
7553 // default: descendant query
7554 var ancChQuery = newQuery();
7555 var descendant = newQuery();
7556 var ancestor = selector[selector.length - 1];
7557 ancChQuery.checks.push({
7558 type: Type.DESCENDANT,
7559 ancestor: ancestor,
7560 descendant: descendant
7561 }); // the query in the selector should be the '>' itself
7562
7563 replaceLastQuery(selector, query, ancChQuery);
7564 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7565
7566 return descendant;
7567 } else if (selector.currentSubject === query) {
7568 // compound split query
7569 var compound = newQuery();
7570 var left = selector[selector.length - 1];
7571 var right = newQuery();
7572 var subject = newQuery();
7573
7574 var _descendant = newQuery();
7575
7576 var _ancestor = newQuery(); // set up the root compound q
7577
7578
7579 compound.checks.push({
7580 type: Type.COMPOUND_SPLIT,
7581 left: left,
7582 right: right,
7583 subject: subject
7584 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7585
7586 subject.checks = query.checks; // take the checks from the left
7587
7588 query.checks = [{
7589 type: Type.TRUE
7590 }]; // checks under left refs the subject implicitly
7591 // set up the right q
7592
7593 _ancestor.checks.push({
7594 type: Type.TRUE
7595 }); // ancestor implicitly refs the subject
7596
7597
7598 right.checks.push({
7599 type: Type.ANCESTOR,
7600 // type is swapped on right side queries
7601 ancestor: _ancestor,
7602 descendant: _descendant // empty for now
7603
7604 });
7605 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7606
7607 selector.currentSubject = subject;
7608 selector.compoundCount++;
7609 return _descendant; // now populating the right side's descendant
7610 } else {
7611 // ancestor query
7612 // info for parent query
7613 var _ancestor2 = newQuery();
7614
7615 var _descendant2 = newQuery();
7616
7617 var adQChecks = [{
7618 type: Type.ANCESTOR,
7619 ancestor: _ancestor2,
7620 descendant: _descendant2
7621 }]; // the parent-child query takes the place of the query previously being populated
7622
7623 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7624
7625 query.checks = adQChecks; // pc query takes over
7626
7627 selector.compoundCount++;
7628 return _descendant2; // we're now populating the child
7629 }
7630 }
7631}, {
7632 name: 'subject',
7633 modifier: true,
7634 regex: tokens.subject,
7635 populate: function populate(selector, query) {
7636 if (selector.currentSubject != null && selector.currentSubject !== query) {
7637 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7638 return false;
7639 }
7640
7641 selector.currentSubject = query;
7642 var topQ = selector[selector.length - 1];
7643 var topChk = topQ.checks[0];
7644 var topType = topChk == null ? null : topChk.type;
7645
7646 if (topType === Type.DIRECTED_EDGE) {
7647 // directed edge with subject on the target
7648 // change to target node check
7649 topChk.type = Type.NODE_TARGET;
7650 } else if (topType === Type.UNDIRECTED_EDGE) {
7651 // undirected edge with subject on the second node
7652 // change to neighbor check
7653 topChk.type = Type.NODE_NEIGHBOR;
7654 topChk.node = topChk.nodes[1]; // second node is subject
7655
7656 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7657
7658 topChk.nodes = null;
7659 }
7660 }
7661}];
7662exprs.forEach(function (e) {
7663 return e.regexObj = new RegExp('^' + e.regex);
7664});
7665
7666/**
7667 * Of all the expressions, find the first match in the remaining text.
7668 * @param {string} remaining The remaining text to parse
7669 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7670 */
7671
7672var consumeExpr = function consumeExpr(remaining) {
7673 var expr;
7674 var match;
7675 var name;
7676
7677 for (var j = 0; j < exprs.length; j++) {
7678 var e = exprs[j];
7679 var n = e.name;
7680 var m = remaining.match(e.regexObj);
7681
7682 if (m != null) {
7683 match = m;
7684 expr = e;
7685 name = n;
7686 var consumed = m[0];
7687 remaining = remaining.substring(consumed.length);
7688 break; // we've consumed one expr, so we can return now
7689 }
7690 }
7691
7692 return {
7693 expr: expr,
7694 match: match,
7695 name: name,
7696 remaining: remaining
7697 };
7698};
7699/**
7700 * Consume all the leading whitespace
7701 * @param {string} remaining The text to consume
7702 * @returns The text with the leading whitespace removed
7703 */
7704
7705
7706var consumeWhitespace = function consumeWhitespace(remaining) {
7707 var match = remaining.match(/^\s+/);
7708
7709 if (match) {
7710 var consumed = match[0];
7711 remaining = remaining.substring(consumed.length);
7712 }
7713
7714 return remaining;
7715};
7716/**
7717 * Parse the string and store the parsed representation in the Selector.
7718 * @param {string} selector The selector string
7719 * @returns `true` if the selector was successfully parsed, `false` otherwise
7720 */
7721
7722
7723var parse = function parse(selector) {
7724 var self = this;
7725 var remaining = self.inputText = selector;
7726 var currentQuery = self[0] = newQuery();
7727 self.length = 1;
7728 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7729
7730 for (;;) {
7731 var exprInfo = consumeExpr(remaining);
7732
7733 if (exprInfo.expr == null) {
7734 warn('The selector `' + selector + '`is invalid');
7735 return false;
7736 } else {
7737 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7738
7739 var ret = exprInfo.expr.populate(self, currentQuery, args);
7740
7741 if (ret === false) {
7742 return false; // exit if population failed
7743 } else if (ret != null) {
7744 currentQuery = ret; // change the current query to be filled if the expr specifies
7745 }
7746 }
7747
7748 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7749
7750 if (remaining.match(/^\s*$/)) {
7751 break;
7752 }
7753 }
7754
7755 var lastQ = self[self.length - 1];
7756
7757 if (self.currentSubject != null) {
7758 lastQ.subject = self.currentSubject;
7759 }
7760
7761 lastQ.edgeCount = self.edgeCount;
7762 lastQ.compoundCount = self.compoundCount;
7763
7764 for (var i = 0; i < self.length; i++) {
7765 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7766
7767 if (q.compoundCount > 0 && q.edgeCount > 0) {
7768 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7769 return false;
7770 }
7771
7772 if (q.edgeCount > 1) {
7773 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7774 return false;
7775 } else if (q.edgeCount === 1) {
7776 warn('The selector `' + selector + '` is deprecated. Edge selectors do not take effect on changes to source and target nodes after an edge is added, for performance reasons. Use a class or data selector on edges instead, updating the class or data of an edge when your app detects a change in source or target nodes.');
7777 }
7778 }
7779
7780 return true; // success
7781};
7782/**
7783 * Get the selector represented as a string. This value uses default formatting,
7784 * so things like spacing may differ from the input text passed to the constructor.
7785 * @returns {string} The selector string
7786 */
7787
7788
7789var toString = function toString() {
7790 if (this.toStringCache != null) {
7791 return this.toStringCache;
7792 }
7793
7794 var clean = function clean(obj) {
7795 if (obj == null) {
7796 return '';
7797 } else {
7798 return obj;
7799 }
7800 };
7801
7802 var cleanVal = function cleanVal(val) {
7803 if (string(val)) {
7804 return '"' + val + '"';
7805 } else {
7806 return clean(val);
7807 }
7808 };
7809
7810 var space = function space(val) {
7811 return ' ' + val + ' ';
7812 };
7813
7814 var checkToString = function checkToString(check, subject) {
7815 var type = check.type,
7816 value = check.value;
7817
7818 switch (type) {
7819 case Type.GROUP:
7820 {
7821 var group = clean(value);
7822 return group.substring(0, group.length - 1);
7823 }
7824
7825 case Type.DATA_COMPARE:
7826 {
7827 var field = check.field,
7828 operator = check.operator;
7829 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7830 }
7831
7832 case Type.DATA_BOOL:
7833 {
7834 var _operator = check.operator,
7835 _field = check.field;
7836 return '[' + clean(_operator) + _field + ']';
7837 }
7838
7839 case Type.DATA_EXIST:
7840 {
7841 var _field2 = check.field;
7842 return '[' + _field2 + ']';
7843 }
7844
7845 case Type.META_COMPARE:
7846 {
7847 var _operator2 = check.operator,
7848 _field3 = check.field;
7849 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7850 }
7851
7852 case Type.STATE:
7853 {
7854 return value;
7855 }
7856
7857 case Type.ID:
7858 {
7859 return '#' + value;
7860 }
7861
7862 case Type.CLASS:
7863 {
7864 return '.' + value;
7865 }
7866
7867 case Type.PARENT:
7868 case Type.CHILD:
7869 {
7870 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7871 }
7872
7873 case Type.ANCESTOR:
7874 case Type.DESCENDANT:
7875 {
7876 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7877 }
7878
7879 case Type.COMPOUND_SPLIT:
7880 {
7881 var lhs = queryToString(check.left, subject);
7882 var sub = queryToString(check.subject, subject);
7883 var rhs = queryToString(check.right, subject);
7884 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7885 }
7886
7887 case Type.TRUE:
7888 {
7889 return '';
7890 }
7891 }
7892 };
7893
7894 var queryToString = function queryToString(query, subject) {
7895 return query.checks.reduce(function (str, chk, i) {
7896 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7897 }, '');
7898 };
7899
7900 var str = '';
7901
7902 for (var i = 0; i < this.length; i++) {
7903 var query = this[i];
7904 str += queryToString(query, query.subject);
7905
7906 if (this.length > 1 && i < this.length - 1) {
7907 str += ', ';
7908 }
7909 }
7910
7911 this.toStringCache = str;
7912 return str;
7913};
7914var parse$1 = {
7915 parse: parse,
7916 toString: toString
7917};
7918
7919var valCmp = function valCmp(fieldVal, operator, value) {
7920 var matches;
7921 var isFieldStr = string(fieldVal);
7922 var isFieldNum = number(fieldVal);
7923 var isValStr = string(value);
7924 var fieldStr, valStr;
7925 var caseInsensitive = false;
7926 var notExpr = false;
7927 var isIneqCmp = false;
7928
7929 if (operator.indexOf('!') >= 0) {
7930 operator = operator.replace('!', '');
7931 notExpr = true;
7932 }
7933
7934 if (operator.indexOf('@') >= 0) {
7935 operator = operator.replace('@', '');
7936 caseInsensitive = true;
7937 }
7938
7939 if (isFieldStr || isValStr || caseInsensitive) {
7940 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
7941 valStr = '' + value;
7942 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
7943 // even if we're comparing numbers
7944
7945
7946 if (caseInsensitive) {
7947 fieldVal = fieldStr = fieldStr.toLowerCase();
7948 value = valStr = valStr.toLowerCase();
7949 }
7950
7951 switch (operator) {
7952 case '*=':
7953 matches = fieldStr.indexOf(valStr) >= 0;
7954 break;
7955
7956 case '$=':
7957 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
7958 break;
7959
7960 case '^=':
7961 matches = fieldStr.indexOf(valStr) === 0;
7962 break;
7963
7964 case '=':
7965 matches = fieldVal === value;
7966 break;
7967
7968 case '>':
7969 isIneqCmp = true;
7970 matches = fieldVal > value;
7971 break;
7972
7973 case '>=':
7974 isIneqCmp = true;
7975 matches = fieldVal >= value;
7976 break;
7977
7978 case '<':
7979 isIneqCmp = true;
7980 matches = fieldVal < value;
7981 break;
7982
7983 case '<=':
7984 isIneqCmp = true;
7985 matches = fieldVal <= value;
7986 break;
7987
7988 default:
7989 matches = false;
7990 break;
7991 } // apply the not op, but null vals for inequalities should always stay non-matching
7992
7993
7994 if (notExpr && (fieldVal != null || !isIneqCmp)) {
7995 matches = !matches;
7996 }
7997
7998 return matches;
7999};
8000var boolCmp = function boolCmp(fieldVal, operator) {
8001 switch (operator) {
8002 case '?':
8003 return fieldVal ? true : false;
8004
8005 case '!':
8006 return fieldVal ? false : true;
8007
8008 case '^':
8009 return fieldVal === undefined;
8010 }
8011};
8012var existCmp = function existCmp(fieldVal) {
8013 return fieldVal !== undefined;
8014};
8015var data = function data(ele, field) {
8016 return ele.data(field);
8017};
8018var meta = function meta(ele, field) {
8019 return ele[field]();
8020};
8021
8022/** A lookup of `match(check, ele)` functions by `Type` int */
8023
8024var match = [];
8025/**
8026 * Returns whether the query matches for the element
8027 * @param query The `{ type, value, ... }` query object
8028 * @param ele The element to compare against
8029*/
8030
8031var matches = function matches(query, ele) {
8032 return query.checks.every(function (chk) {
8033 return match[chk.type](chk, ele);
8034 });
8035};
8036
8037match[Type.GROUP] = function (check, ele) {
8038 var group = check.value;
8039 return group === '*' || group === ele.group();
8040};
8041
8042match[Type.STATE] = function (check, ele) {
8043 var stateSelector = check.value;
8044 return stateSelectorMatches(stateSelector, ele);
8045};
8046
8047match[Type.ID] = function (check, ele) {
8048 var id = check.value;
8049 return ele.id() === id;
8050};
8051
8052match[Type.CLASS] = function (check, ele) {
8053 var cls = check.value;
8054 return ele.hasClass(cls);
8055};
8056
8057match[Type.META_COMPARE] = function (check, ele) {
8058 var field = check.field,
8059 operator = check.operator,
8060 value = check.value;
8061 return valCmp(meta(ele, field), operator, value);
8062};
8063
8064match[Type.DATA_COMPARE] = function (check, ele) {
8065 var field = check.field,
8066 operator = check.operator,
8067 value = check.value;
8068 return valCmp(data(ele, field), operator, value);
8069};
8070
8071match[Type.DATA_BOOL] = function (check, ele) {
8072 var field = check.field,
8073 operator = check.operator;
8074 return boolCmp(data(ele, field), operator);
8075};
8076
8077match[Type.DATA_EXIST] = function (check, ele) {
8078 var field = check.field,
8079 operator = check.operator;
8080 return existCmp(data(ele, field));
8081};
8082
8083match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8084 var qA = check.nodes[0];
8085 var qB = check.nodes[1];
8086 var src = ele.source();
8087 var tgt = ele.target();
8088 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8089};
8090
8091match[Type.NODE_NEIGHBOR] = function (check, ele) {
8092 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8093 return n.isNode() && matches(check.neighbor, n);
8094 });
8095};
8096
8097match[Type.DIRECTED_EDGE] = function (check, ele) {
8098 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8099};
8100
8101match[Type.NODE_SOURCE] = function (check, ele) {
8102 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8103 return n.isNode() && matches(check.target, n);
8104 });
8105};
8106
8107match[Type.NODE_TARGET] = function (check, ele) {
8108 return matches(check.target, ele) && ele.incomers().some(function (n) {
8109 return n.isNode() && matches(check.source, n);
8110 });
8111};
8112
8113match[Type.CHILD] = function (check, ele) {
8114 return matches(check.child, ele) && matches(check.parent, ele.parent());
8115};
8116
8117match[Type.PARENT] = function (check, ele) {
8118 return matches(check.parent, ele) && ele.children().some(function (c) {
8119 return matches(check.child, c);
8120 });
8121};
8122
8123match[Type.DESCENDANT] = function (check, ele) {
8124 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8125 return matches(check.ancestor, a);
8126 });
8127};
8128
8129match[Type.ANCESTOR] = function (check, ele) {
8130 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8131 return matches(check.descendant, d);
8132 });
8133};
8134
8135match[Type.COMPOUND_SPLIT] = function (check, ele) {
8136 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8137};
8138
8139match[Type.TRUE] = function () {
8140 return true;
8141};
8142
8143match[Type.COLLECTION] = function (check, ele) {
8144 var collection = check.value;
8145 return collection.has(ele);
8146};
8147
8148match[Type.FILTER] = function (check, ele) {
8149 var filter = check.value;
8150 return filter(ele);
8151};
8152
8153var filter = function filter(collection) {
8154 var self = this; // for 1 id #foo queries, just get the element
8155
8156 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8157 return collection.getElementById(self[0].checks[0].value).collection();
8158 }
8159
8160 var selectorFunction = function selectorFunction(element) {
8161 for (var j = 0; j < self.length; j++) {
8162 var query = self[j];
8163
8164 if (matches(query, element)) {
8165 return true;
8166 }
8167 }
8168
8169 return false;
8170 };
8171
8172 if (self.text() == null) {
8173 selectorFunction = function selectorFunction() {
8174 return true;
8175 };
8176 }
8177
8178 return collection.filter(selectorFunction);
8179}; // filter
8180// does selector match a single element?
8181
8182
8183var matches$1 = function matches$1(ele) {
8184 var self = this;
8185
8186 for (var j = 0; j < self.length; j++) {
8187 var query = self[j];
8188
8189 if (matches(query, ele)) {
8190 return true;
8191 }
8192 }
8193
8194 return false;
8195}; // matches
8196
8197
8198var matching = {
8199 matches: matches$1,
8200 filter: filter
8201};
8202
8203var Selector = function Selector(selector) {
8204 this.inputText = selector;
8205 this.currentSubject = null;
8206 this.compoundCount = 0;
8207 this.edgeCount = 0;
8208 this.length = 0;
8209
8210 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8211 this.addQuery({
8212 checks: [{
8213 type: Type.COLLECTION,
8214 value: selector.collection()
8215 }]
8216 });
8217 } else if (fn(selector)) {
8218 this.addQuery({
8219 checks: [{
8220 type: Type.FILTER,
8221 value: selector
8222 }]
8223 });
8224 } else if (string(selector)) {
8225 if (!this.parse(selector)) {
8226 this.invalid = true;
8227 }
8228 } else {
8229 error('A selector must be created from a string; found ');
8230 }
8231};
8232
8233var selfn = Selector.prototype;
8234[parse$1, matching].forEach(function (p) {
8235 return extend(selfn, p);
8236});
8237
8238selfn.text = function () {
8239 return this.inputText;
8240};
8241
8242selfn.size = function () {
8243 return this.length;
8244};
8245
8246selfn.eq = function (i) {
8247 return this[i];
8248};
8249
8250selfn.sameText = function (otherSel) {
8251 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8252};
8253
8254selfn.addQuery = function (q) {
8255 this[this.length++] = q;
8256};
8257
8258selfn.selector = selfn.toString;
8259
8260var elesfn$f = {
8261 allAre: function allAre(selector) {
8262 var selObj = new Selector(selector);
8263 return this.every(function (ele) {
8264 return selObj.matches(ele);
8265 });
8266 },
8267 is: function is(selector) {
8268 var selObj = new Selector(selector);
8269 return this.some(function (ele) {
8270 return selObj.matches(ele);
8271 });
8272 },
8273 some: function some(fn, thisArg) {
8274 for (var i = 0; i < this.length; i++) {
8275 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8276
8277 if (ret) {
8278 return true;
8279 }
8280 }
8281
8282 return false;
8283 },
8284 every: function every(fn, thisArg) {
8285 for (var i = 0; i < this.length; i++) {
8286 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8287
8288 if (!ret) {
8289 return false;
8290 }
8291 }
8292
8293 return true;
8294 },
8295 same: function same(collection) {
8296 // cheap collection ref check
8297 if (this === collection) {
8298 return true;
8299 }
8300
8301 collection = this.cy().collection(collection);
8302 var thisLength = this.length;
8303 var collectionLength = collection.length; // cheap length check
8304
8305 if (thisLength !== collectionLength) {
8306 return false;
8307 } // cheap element ref check
8308
8309
8310 if (thisLength === 1) {
8311 return this[0] === collection[0];
8312 }
8313
8314 return this.every(function (ele) {
8315 return collection.hasElementWithId(ele.id());
8316 });
8317 },
8318 anySame: function anySame(collection) {
8319 collection = this.cy().collection(collection);
8320 return this.some(function (ele) {
8321 return collection.hasElementWithId(ele.id());
8322 });
8323 },
8324 allAreNeighbors: function allAreNeighbors(collection) {
8325 collection = this.cy().collection(collection);
8326 var nhood = this.neighborhood();
8327 return collection.every(function (ele) {
8328 return nhood.hasElementWithId(ele.id());
8329 });
8330 },
8331 contains: function contains(collection) {
8332 collection = this.cy().collection(collection);
8333 var self = this;
8334 return collection.every(function (ele) {
8335 return self.hasElementWithId(ele.id());
8336 });
8337 }
8338};
8339elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8340elesfn$f.has = elesfn$f.contains;
8341elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8342
8343var cache = function cache(fn, name) {
8344 return function traversalCache(arg1, arg2, arg3, arg4) {
8345 var selectorOrEles = arg1;
8346 var eles = this;
8347 var key;
8348
8349 if (selectorOrEles == null) {
8350 key = '';
8351 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8352 key = selectorOrEles.id();
8353 }
8354
8355 if (eles.length === 1 && key) {
8356 var _p = eles[0]._private;
8357 var tch = _p.traversalCache = _p.traversalCache || {};
8358 var ch = tch[name] = tch[name] || [];
8359 var hash = hashString(key);
8360 var cacheHit = ch[hash];
8361
8362 if (cacheHit) {
8363 return cacheHit;
8364 } else {
8365 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8366 }
8367 } else {
8368 return fn.call(eles, arg1, arg2, arg3, arg4);
8369 }
8370 };
8371};
8372
8373var elesfn$g = {
8374 parent: function parent(selector) {
8375 var parents = []; // optimisation for single ele call
8376
8377 if (this.length === 1) {
8378 var parent = this[0]._private.parent;
8379
8380 if (parent) {
8381 return parent;
8382 }
8383 }
8384
8385 for (var i = 0; i < this.length; i++) {
8386 var ele = this[i];
8387 var _parent = ele._private.parent;
8388
8389 if (_parent) {
8390 parents.push(_parent);
8391 }
8392 }
8393
8394 return this.spawn(parents, true).filter(selector);
8395 },
8396 parents: function parents(selector) {
8397 var parents = [];
8398 var eles = this.parent();
8399
8400 while (eles.nonempty()) {
8401 for (var i = 0; i < eles.length; i++) {
8402 var ele = eles[i];
8403 parents.push(ele);
8404 }
8405
8406 eles = eles.parent();
8407 }
8408
8409 return this.spawn(parents, true).filter(selector);
8410 },
8411 commonAncestors: function commonAncestors(selector) {
8412 var ancestors;
8413
8414 for (var i = 0; i < this.length; i++) {
8415 var ele = this[i];
8416 var parents = ele.parents();
8417 ancestors = ancestors || parents;
8418 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8419 }
8420
8421 return ancestors.filter(selector);
8422 },
8423 orphans: function orphans(selector) {
8424 return this.stdFilter(function (ele) {
8425 return ele.isOrphan();
8426 }).filter(selector);
8427 },
8428 nonorphans: function nonorphans(selector) {
8429 return this.stdFilter(function (ele) {
8430 return ele.isChild();
8431 }).filter(selector);
8432 },
8433 children: cache(function (selector) {
8434 var children = [];
8435
8436 for (var i = 0; i < this.length; i++) {
8437 var ele = this[i];
8438 var eleChildren = ele._private.children;
8439
8440 for (var j = 0; j < eleChildren.length; j++) {
8441 children.push(eleChildren[j]);
8442 }
8443 }
8444
8445 return this.spawn(children, true).filter(selector);
8446 }, 'children'),
8447 siblings: function siblings(selector) {
8448 return this.parent().children().not(this).filter(selector);
8449 },
8450 isParent: function isParent() {
8451 var ele = this[0];
8452
8453 if (ele) {
8454 return ele.isNode() && ele._private.children.length !== 0;
8455 }
8456 },
8457 isChildless: function isChildless() {
8458 var ele = this[0];
8459
8460 if (ele) {
8461 return ele.isNode() && ele._private.children.length === 0;
8462 }
8463 },
8464 isChild: function isChild() {
8465 var ele = this[0];
8466
8467 if (ele) {
8468 return ele.isNode() && ele._private.parent != null;
8469 }
8470 },
8471 isOrphan: function isOrphan() {
8472 var ele = this[0];
8473
8474 if (ele) {
8475 return ele.isNode() && ele._private.parent == null;
8476 }
8477 },
8478 descendants: function descendants(selector) {
8479 var elements = [];
8480
8481 function add(eles) {
8482 for (var i = 0; i < eles.length; i++) {
8483 var ele = eles[i];
8484 elements.push(ele);
8485
8486 if (ele.children().nonempty()) {
8487 add(ele.children());
8488 }
8489 }
8490 }
8491
8492 add(this.children());
8493 return this.spawn(elements, true).filter(selector);
8494 }
8495};
8496
8497function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8498 var q = [];
8499 var did = new Set$1();
8500 var cy = eles.cy();
8501 var hasCompounds = cy.hasCompoundNodes();
8502
8503 for (var i = 0; i < eles.length; i++) {
8504 var ele = eles[i];
8505
8506 if (includeSelf) {
8507 q.push(ele);
8508 } else if (hasCompounds) {
8509 recursiveStep(q, did, ele);
8510 }
8511 }
8512
8513 while (q.length > 0) {
8514 var _ele = q.shift();
8515
8516 fn(_ele);
8517 did.add(_ele.id());
8518
8519 if (hasCompounds) {
8520 recursiveStep(q, did, _ele);
8521 }
8522 }
8523
8524 return eles;
8525}
8526
8527function addChildren(q, did, ele) {
8528 if (ele.isParent()) {
8529 var children = ele._private.children;
8530
8531 for (var i = 0; i < children.length; i++) {
8532 var child = children[i];
8533
8534 if (!did.has(child.id())) {
8535 q.push(child);
8536 }
8537 }
8538 }
8539} // very efficient version of eles.add( eles.descendants() ).forEach()
8540// for internal use
8541
8542
8543elesfn$g.forEachDown = function (fn) {
8544 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8545 return forEachCompound(this, fn, includeSelf, addChildren);
8546};
8547
8548function addParent(q, did, ele) {
8549 if (ele.isChild()) {
8550 var parent = ele._private.parent;
8551
8552 if (!did.has(parent.id())) {
8553 q.push(parent);
8554 }
8555 }
8556}
8557
8558elesfn$g.forEachUp = function (fn) {
8559 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8560 return forEachCompound(this, fn, includeSelf, addParent);
8561};
8562
8563function addParentAndChildren(q, did, ele) {
8564 addParent(q, did, ele);
8565 addChildren(q, did, ele);
8566}
8567
8568elesfn$g.forEachUpAndDown = function (fn) {
8569 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8570 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8571}; // aliases
8572
8573
8574elesfn$g.ancestors = elesfn$g.parents;
8575
8576var fn$1, elesfn$h;
8577fn$1 = elesfn$h = {
8578 data: define$3.data({
8579 field: 'data',
8580 bindingEvent: 'data',
8581 allowBinding: true,
8582 allowSetting: true,
8583 settingEvent: 'data',
8584 settingTriggersEvent: true,
8585 triggerFnName: 'trigger',
8586 allowGetting: true,
8587 immutableKeys: {
8588 'id': true,
8589 'source': true,
8590 'target': true,
8591 'parent': true
8592 },
8593 updateStyle: true
8594 }),
8595 removeData: define$3.removeData({
8596 field: 'data',
8597 event: 'data',
8598 triggerFnName: 'trigger',
8599 triggerEvent: true,
8600 immutableKeys: {
8601 'id': true,
8602 'source': true,
8603 'target': true,
8604 'parent': true
8605 },
8606 updateStyle: true
8607 }),
8608 scratch: define$3.data({
8609 field: 'scratch',
8610 bindingEvent: 'scratch',
8611 allowBinding: true,
8612 allowSetting: true,
8613 settingEvent: 'scratch',
8614 settingTriggersEvent: true,
8615 triggerFnName: 'trigger',
8616 allowGetting: true,
8617 updateStyle: true
8618 }),
8619 removeScratch: define$3.removeData({
8620 field: 'scratch',
8621 event: 'scratch',
8622 triggerFnName: 'trigger',
8623 triggerEvent: true,
8624 updateStyle: true
8625 }),
8626 rscratch: define$3.data({
8627 field: 'rscratch',
8628 allowBinding: false,
8629 allowSetting: true,
8630 settingTriggersEvent: false,
8631 allowGetting: true
8632 }),
8633 removeRscratch: define$3.removeData({
8634 field: 'rscratch',
8635 triggerEvent: false
8636 }),
8637 id: function id() {
8638 var ele = this[0];
8639
8640 if (ele) {
8641 return ele._private.data.id;
8642 }
8643 }
8644}; // aliases
8645
8646fn$1.attr = fn$1.data;
8647fn$1.removeAttr = fn$1.removeData;
8648var data$1 = elesfn$h;
8649
8650var elesfn$i = {};
8651
8652function defineDegreeFunction(callback) {
8653 return function (includeLoops) {
8654 var self = this;
8655
8656 if (includeLoops === undefined) {
8657 includeLoops = true;
8658 }
8659
8660 if (self.length === 0) {
8661 return;
8662 }
8663
8664 if (self.isNode() && !self.removed()) {
8665 var degree = 0;
8666 var node = self[0];
8667 var connectedEdges = node._private.edges;
8668
8669 for (var i = 0; i < connectedEdges.length; i++) {
8670 var edge = connectedEdges[i];
8671
8672 if (!includeLoops && edge.isLoop()) {
8673 continue;
8674 }
8675
8676 degree += callback(node, edge);
8677 }
8678
8679 return degree;
8680 } else {
8681 return;
8682 }
8683 };
8684}
8685
8686extend(elesfn$i, {
8687 degree: defineDegreeFunction(function (node, edge) {
8688 if (edge.source().same(edge.target())) {
8689 return 2;
8690 } else {
8691 return 1;
8692 }
8693 }),
8694 indegree: defineDegreeFunction(function (node, edge) {
8695 if (edge.target().same(node)) {
8696 return 1;
8697 } else {
8698 return 0;
8699 }
8700 }),
8701 outdegree: defineDegreeFunction(function (node, edge) {
8702 if (edge.source().same(node)) {
8703 return 1;
8704 } else {
8705 return 0;
8706 }
8707 })
8708});
8709
8710function defineDegreeBoundsFunction(degreeFn, callback) {
8711 return function (includeLoops) {
8712 var ret;
8713 var nodes = this.nodes();
8714
8715 for (var i = 0; i < nodes.length; i++) {
8716 var ele = nodes[i];
8717 var degree = ele[degreeFn](includeLoops);
8718
8719 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8720 ret = degree;
8721 }
8722 }
8723
8724 return ret;
8725 };
8726}
8727
8728extend(elesfn$i, {
8729 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8730 return degree < min;
8731 }),
8732 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8733 return degree > max;
8734 }),
8735 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8736 return degree < min;
8737 }),
8738 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8739 return degree > max;
8740 }),
8741 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8742 return degree < min;
8743 }),
8744 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8745 return degree > max;
8746 })
8747});
8748extend(elesfn$i, {
8749 totalDegree: function totalDegree(includeLoops) {
8750 var total = 0;
8751 var nodes = this.nodes();
8752
8753 for (var i = 0; i < nodes.length; i++) {
8754 total += nodes[i].degree(includeLoops);
8755 }
8756
8757 return total;
8758 }
8759});
8760
8761var fn$2, elesfn$j;
8762
8763var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8764 for (var i = 0; i < eles.length; i++) {
8765 var ele = eles[i];
8766
8767 if (!ele.locked()) {
8768 var oldPos = ele._private.position;
8769 var delta = {
8770 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8771 y: newPos.y != null ? newPos.y - oldPos.y : 0
8772 };
8773
8774 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8775 ele.children().shift(delta, silent);
8776 }
8777
8778 ele.dirtyBoundingBoxCache();
8779 }
8780 }
8781};
8782
8783var positionDef = {
8784 field: 'position',
8785 bindingEvent: 'position',
8786 allowBinding: true,
8787 allowSetting: true,
8788 settingEvent: 'position',
8789 settingTriggersEvent: true,
8790 triggerFnName: 'emitAndNotify',
8791 allowGetting: true,
8792 validKeys: ['x', 'y'],
8793 beforeGet: function beforeGet(ele) {
8794 ele.updateCompoundBounds();
8795 },
8796 beforeSet: function beforeSet(eles, newPos) {
8797 beforePositionSet(eles, newPos, false);
8798 },
8799 onSet: function onSet(eles) {
8800 eles.dirtyCompoundBoundsCache();
8801 },
8802 canSet: function canSet(ele) {
8803 return !ele.locked();
8804 }
8805};
8806fn$2 = elesfn$j = {
8807 position: define$3.data(positionDef),
8808 // position but no notification to renderer
8809 silentPosition: define$3.data(extend({}, positionDef, {
8810 allowBinding: false,
8811 allowSetting: true,
8812 settingTriggersEvent: false,
8813 allowGetting: false,
8814 beforeSet: function beforeSet(eles, newPos) {
8815 beforePositionSet(eles, newPos, true);
8816 },
8817 onSet: function onSet(eles) {
8818 eles.dirtyCompoundBoundsCache();
8819 }
8820 })),
8821 positions: function positions(pos, silent) {
8822 if (plainObject(pos)) {
8823 if (silent) {
8824 this.silentPosition(pos);
8825 } else {
8826 this.position(pos);
8827 }
8828 } else if (fn(pos)) {
8829 var _fn = pos;
8830 var cy = this.cy();
8831 cy.startBatch();
8832
8833 for (var i = 0; i < this.length; i++) {
8834 var ele = this[i];
8835
8836 var _pos = void 0;
8837
8838 if (_pos = _fn(ele, i)) {
8839 if (silent) {
8840 ele.silentPosition(_pos);
8841 } else {
8842 ele.position(_pos);
8843 }
8844 }
8845 }
8846
8847 cy.endBatch();
8848 }
8849
8850 return this; // chaining
8851 },
8852 silentPositions: function silentPositions(pos) {
8853 return this.positions(pos, true);
8854 },
8855 shift: function shift(dim, val, silent) {
8856 var delta;
8857
8858 if (plainObject(dim)) {
8859 delta = {
8860 x: number(dim.x) ? dim.x : 0,
8861 y: number(dim.y) ? dim.y : 0
8862 };
8863 silent = val;
8864 } else if (string(dim) && number(val)) {
8865 delta = {
8866 x: 0,
8867 y: 0
8868 };
8869 delta[dim] = val;
8870 }
8871
8872 if (delta != null) {
8873 var cy = this.cy();
8874 cy.startBatch();
8875
8876 for (var i = 0; i < this.length; i++) {
8877 var ele = this[i];
8878 var pos = ele.position();
8879 var newPos = {
8880 x: pos.x + delta.x,
8881 y: pos.y + delta.y
8882 };
8883
8884 if (silent) {
8885 ele.silentPosition(newPos);
8886 } else {
8887 ele.position(newPos);
8888 }
8889 }
8890
8891 cy.endBatch();
8892 }
8893
8894 return this;
8895 },
8896 silentShift: function silentShift(dim, val) {
8897 if (plainObject(dim)) {
8898 this.shift(dim, true);
8899 } else if (string(dim) && number(val)) {
8900 this.shift(dim, val, true);
8901 }
8902
8903 return this;
8904 },
8905 // get/set the rendered (i.e. on screen) positon of the element
8906 renderedPosition: function renderedPosition(dim, val) {
8907 var ele = this[0];
8908 var cy = this.cy();
8909 var zoom = cy.zoom();
8910 var pan = cy.pan();
8911 var rpos = plainObject(dim) ? dim : undefined;
8912 var setting = rpos !== undefined || val !== undefined && string(dim);
8913
8914 if (ele && ele.isNode()) {
8915 // must have an element and must be a node to return position
8916 if (setting) {
8917 for (var i = 0; i < this.length; i++) {
8918 var _ele = this[i];
8919
8920 if (val !== undefined) {
8921 // set one dimension
8922 _ele.position(dim, (val - pan[dim]) / zoom);
8923 } else if (rpos !== undefined) {
8924 // set whole position
8925 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8926 }
8927 }
8928 } else {
8929 // getting
8930 var pos = ele.position();
8931 rpos = modelToRenderedPosition(pos, zoom, pan);
8932
8933 if (dim === undefined) {
8934 // then return the whole rendered position
8935 return rpos;
8936 } else {
8937 // then return the specified dimension
8938 return rpos[dim];
8939 }
8940 }
8941 } else if (!setting) {
8942 return undefined; // for empty collection case
8943 }
8944
8945 return this; // chaining
8946 },
8947 // get/set the position relative to the parent
8948 relativePosition: function relativePosition(dim, val) {
8949 var ele = this[0];
8950 var cy = this.cy();
8951 var ppos = plainObject(dim) ? dim : undefined;
8952 var setting = ppos !== undefined || val !== undefined && string(dim);
8953 var hasCompoundNodes = cy.hasCompoundNodes();
8954
8955 if (ele && ele.isNode()) {
8956 // must have an element and must be a node to return position
8957 if (setting) {
8958 for (var i = 0; i < this.length; i++) {
8959 var _ele2 = this[i];
8960 var parent = hasCompoundNodes ? _ele2.parent() : null;
8961 var hasParent = parent && parent.length > 0;
8962 var relativeToParent = hasParent;
8963
8964 if (hasParent) {
8965 parent = parent[0];
8966 }
8967
8968 var origin = relativeToParent ? parent.position() : {
8969 x: 0,
8970 y: 0
8971 };
8972
8973 if (val !== undefined) {
8974 // set one dimension
8975 _ele2.position(dim, val + origin[dim]);
8976 } else if (ppos !== undefined) {
8977 // set whole position
8978 _ele2.position({
8979 x: ppos.x + origin.x,
8980 y: ppos.y + origin.y
8981 });
8982 }
8983 }
8984 } else {
8985 // getting
8986 var pos = ele.position();
8987
8988 var _parent = hasCompoundNodes ? ele.parent() : null;
8989
8990 var _hasParent = _parent && _parent.length > 0;
8991
8992 var _relativeToParent = _hasParent;
8993
8994 if (_hasParent) {
8995 _parent = _parent[0];
8996 }
8997
8998 var _origin = _relativeToParent ? _parent.position() : {
8999 x: 0,
9000 y: 0
9001 };
9002
9003 ppos = {
9004 x: pos.x - _origin.x,
9005 y: pos.y - _origin.y
9006 };
9007
9008 if (dim === undefined) {
9009 // then return the whole rendered position
9010 return ppos;
9011 } else {
9012 // then return the specified dimension
9013 return ppos[dim];
9014 }
9015 }
9016 } else if (!setting) {
9017 return undefined; // for empty collection case
9018 }
9019
9020 return this; // chaining
9021 }
9022}; // aliases
9023
9024fn$2.modelPosition = fn$2.point = fn$2.position;
9025fn$2.modelPositions = fn$2.points = fn$2.positions;
9026fn$2.renderedPoint = fn$2.renderedPosition;
9027fn$2.relativePoint = fn$2.relativePosition;
9028var position = elesfn$j;
9029
9030var fn$3, elesfn$k;
9031fn$3 = elesfn$k = {};
9032
9033elesfn$k.renderedBoundingBox = function (options) {
9034 var bb = this.boundingBox(options);
9035 var cy = this.cy();
9036 var zoom = cy.zoom();
9037 var pan = cy.pan();
9038 var x1 = bb.x1 * zoom + pan.x;
9039 var x2 = bb.x2 * zoom + pan.x;
9040 var y1 = bb.y1 * zoom + pan.y;
9041 var y2 = bb.y2 * zoom + pan.y;
9042 return {
9043 x1: x1,
9044 x2: x2,
9045 y1: y1,
9046 y2: y2,
9047 w: x2 - x1,
9048 h: y2 - y1
9049 };
9050};
9051
9052elesfn$k.dirtyCompoundBoundsCache = function () {
9053 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9054 var cy = this.cy();
9055
9056 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9057 return this;
9058 }
9059
9060 this.forEachUp(function (ele) {
9061 if (ele.isParent()) {
9062 var _p = ele._private;
9063 _p.compoundBoundsClean = false;
9064 _p.bbCache = null;
9065
9066 if (!silent) {
9067 ele.emitAndNotify('bounds');
9068 }
9069 }
9070 });
9071 return this;
9072};
9073
9074elesfn$k.updateCompoundBounds = function () {
9075 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9076 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9077
9078 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9079 return this;
9080 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9081
9082
9083 if (!force && cy.batching()) {
9084 return this;
9085 }
9086
9087 function update(parent) {
9088 if (!parent.isParent()) {
9089 return;
9090 }
9091
9092 var _p = parent._private;
9093 var children = parent.children();
9094 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9095 var min = {
9096 width: {
9097 val: parent.pstyle('min-width').pfValue,
9098 left: parent.pstyle('min-width-bias-left'),
9099 right: parent.pstyle('min-width-bias-right')
9100 },
9101 height: {
9102 val: parent.pstyle('min-height').pfValue,
9103 top: parent.pstyle('min-height-bias-top'),
9104 bottom: parent.pstyle('min-height-bias-bottom')
9105 }
9106 };
9107 var bb = children.boundingBox({
9108 includeLabels: includeLabels,
9109 includeOverlays: false,
9110 // updating the compound bounds happens outside of the regular
9111 // cache cycle (i.e. before fired events)
9112 useCache: false
9113 });
9114 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9115
9116 if (bb.w === 0 || bb.h === 0) {
9117 bb = {
9118 w: parent.pstyle('width').pfValue,
9119 h: parent.pstyle('height').pfValue
9120 };
9121 bb.x1 = pos.x - bb.w / 2;
9122 bb.x2 = pos.x + bb.w / 2;
9123 bb.y1 = pos.y - bb.h / 2;
9124 bb.y2 = pos.y + bb.h / 2;
9125 }
9126
9127 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9128 var biasDiff = 0;
9129 var biasComplementDiff = 0;
9130 var biasTotal = propBias + propBiasComplement;
9131
9132 if (propDiff > 0 && biasTotal > 0) {
9133 biasDiff = propBias / biasTotal * propDiff;
9134 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9135 }
9136
9137 return {
9138 biasDiff: biasDiff,
9139 biasComplementDiff: biasComplementDiff
9140 };
9141 }
9142
9143 function computePaddingValues(width, height, paddingObject, relativeTo) {
9144 // Assuming percentage is number from 0 to 1
9145 if (paddingObject.units === '%') {
9146 switch (relativeTo) {
9147 case 'width':
9148 return width > 0 ? paddingObject.pfValue * width : 0;
9149
9150 case 'height':
9151 return height > 0 ? paddingObject.pfValue * height : 0;
9152
9153 case 'average':
9154 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9155
9156 case 'min':
9157 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9158
9159 case 'max':
9160 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9161
9162 default:
9163 return 0;
9164 }
9165 } else if (paddingObject.units === 'px') {
9166 return paddingObject.pfValue;
9167 } else {
9168 return 0;
9169 }
9170 }
9171
9172 var leftVal = min.width.left.value;
9173
9174 if (min.width.left.units === 'px' && min.width.val > 0) {
9175 leftVal = leftVal * 100 / min.width.val;
9176 }
9177
9178 var rightVal = min.width.right.value;
9179
9180 if (min.width.right.units === 'px' && min.width.val > 0) {
9181 rightVal = rightVal * 100 / min.width.val;
9182 }
9183
9184 var topVal = min.height.top.value;
9185
9186 if (min.height.top.units === 'px' && min.height.val > 0) {
9187 topVal = topVal * 100 / min.height.val;
9188 }
9189
9190 var bottomVal = min.height.bottom.value;
9191
9192 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9193 bottomVal = bottomVal * 100 / min.height.val;
9194 }
9195
9196 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9197 var diffLeft = widthBiasDiffs.biasDiff;
9198 var diffRight = widthBiasDiffs.biasComplementDiff;
9199 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9200 var diffTop = heightBiasDiffs.biasDiff;
9201 var diffBottom = heightBiasDiffs.biasComplementDiff;
9202 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9203 _p.autoWidth = Math.max(bb.w, min.width.val);
9204 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9205 _p.autoHeight = Math.max(bb.h, min.height.val);
9206 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9207 }
9208
9209 for (var i = 0; i < this.length; i++) {
9210 var ele = this[i];
9211 var _p = ele._private;
9212
9213 if (!_p.compoundBoundsClean || force) {
9214 update(ele);
9215
9216 if (!cy.batching()) {
9217 _p.compoundBoundsClean = true;
9218 }
9219 }
9220 }
9221
9222 return this;
9223};
9224
9225var noninf = function noninf(x) {
9226 if (x === Infinity || x === -Infinity) {
9227 return 0;
9228 }
9229
9230 return x;
9231};
9232
9233var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9234 // don't update with zero area boxes
9235 if (x2 - x1 === 0 || y2 - y1 === 0) {
9236 return;
9237 } // don't update with null dim
9238
9239
9240 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9241 return;
9242 }
9243
9244 b.x1 = x1 < b.x1 ? x1 : b.x1;
9245 b.x2 = x2 > b.x2 ? x2 : b.x2;
9246 b.y1 = y1 < b.y1 ? y1 : b.y1;
9247 b.y2 = y2 > b.y2 ? y2 : b.y2;
9248 b.w = b.x2 - b.x1;
9249 b.h = b.y2 - b.y1;
9250};
9251
9252var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9253 if (b2 == null) {
9254 return b;
9255 }
9256
9257 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9258};
9259
9260var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9261 return getPrefixedProperty(obj, field, prefix);
9262};
9263
9264var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9265 if (ele.cy().headless()) {
9266 return;
9267 }
9268
9269 var _p = ele._private;
9270 var rstyle = _p.rstyle;
9271 var halfArW = rstyle.arrowWidth / 2;
9272 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9273 var x;
9274 var y;
9275
9276 if (arrowType !== 'none') {
9277 if (prefix === 'source') {
9278 x = rstyle.srcX;
9279 y = rstyle.srcY;
9280 } else if (prefix === 'target') {
9281 x = rstyle.tgtX;
9282 y = rstyle.tgtY;
9283 } else {
9284 x = rstyle.midX;
9285 y = rstyle.midY;
9286 } // always store the individual arrow bounds
9287
9288
9289 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9290 var bb = bbs[prefix] = bbs[prefix] || {};
9291 bb.x1 = x - halfArW;
9292 bb.y1 = y - halfArW;
9293 bb.x2 = x + halfArW;
9294 bb.y2 = y + halfArW;
9295 bb.w = bb.x2 - bb.x1;
9296 bb.h = bb.y2 - bb.y1;
9297 expandBoundingBox(bb, 1);
9298 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9299 }
9300};
9301
9302var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9303 if (ele.cy().headless()) {
9304 return;
9305 }
9306
9307 var prefixDash;
9308
9309 if (prefix) {
9310 prefixDash = prefix + '-';
9311 } else {
9312 prefixDash = '';
9313 }
9314
9315 var _p = ele._private;
9316 var rstyle = _p.rstyle;
9317 var label = ele.pstyle(prefixDash + 'label').strValue;
9318
9319 if (label) {
9320 var halign = ele.pstyle('text-halign');
9321 var valign = ele.pstyle('text-valign');
9322 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9323 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9324 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9325 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9326 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9327 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9328 var isEdge = ele.isEdge();
9329 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9330 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9331 var borderWidth = ele.pstyle('text-border-width').pfValue;
9332 var halfBorderWidth = borderWidth / 2;
9333 var padding = ele.pstyle('text-background-padding').pfValue;
9334 var marginOfError = 2; // expand to work around browser dimension inaccuracies
9335
9336 var lh = labelHeight;
9337 var lw = labelWidth;
9338 var lw_2 = lw / 2;
9339 var lh_2 = lh / 2;
9340 var lx1, lx2, ly1, ly2;
9341
9342 if (isEdge) {
9343 lx1 = labelX - lw_2;
9344 lx2 = labelX + lw_2;
9345 ly1 = labelY - lh_2;
9346 ly2 = labelY + lh_2;
9347 } else {
9348 switch (halign.value) {
9349 case 'left':
9350 lx1 = labelX - lw;
9351 lx2 = labelX;
9352 break;
9353
9354 case 'center':
9355 lx1 = labelX - lw_2;
9356 lx2 = labelX + lw_2;
9357 break;
9358
9359 case 'right':
9360 lx1 = labelX;
9361 lx2 = labelX + lw;
9362 break;
9363 }
9364
9365 switch (valign.value) {
9366 case 'top':
9367 ly1 = labelY - lh;
9368 ly2 = labelY;
9369 break;
9370
9371 case 'center':
9372 ly1 = labelY - lh_2;
9373 ly2 = labelY + lh_2;
9374 break;
9375
9376 case 'bottom':
9377 ly1 = labelY;
9378 ly2 = labelY + lh;
9379 break;
9380 }
9381 } // shift by margin and expand by outline and border
9382
9383
9384 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9385 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
9386 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9387 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
9388
9389 var bbPrefix = prefix || 'main';
9390 var bbs = _p.labelBounds;
9391 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9392 bb.x1 = lx1;
9393 bb.y1 = ly1;
9394 bb.x2 = lx2;
9395 bb.y2 = ly2;
9396 bb.w = lx2 - lx1;
9397 bb.h = ly2 - ly1;
9398 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9399 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9400
9401 if (isAutorotate || isPfValue) {
9402 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9403 var cos = Math.cos(theta);
9404 var sin = Math.sin(theta); // rotation point (default value for center-center)
9405
9406 var xo = (lx1 + lx2) / 2;
9407 var yo = (ly1 + ly2) / 2;
9408
9409 if (!isEdge) {
9410 switch (halign.value) {
9411 case 'left':
9412 xo = lx2;
9413 break;
9414
9415 case 'right':
9416 xo = lx1;
9417 break;
9418 }
9419
9420 switch (valign.value) {
9421 case 'top':
9422 yo = ly2;
9423 break;
9424
9425 case 'bottom':
9426 yo = ly1;
9427 break;
9428 }
9429 }
9430
9431 var rotate = function rotate(x, y) {
9432 x = x - xo;
9433 y = y - yo;
9434 return {
9435 x: x * cos - y * sin + xo,
9436 y: x * sin + y * cos + yo
9437 };
9438 };
9439
9440 var px1y1 = rotate(lx1, ly1);
9441 var px1y2 = rotate(lx1, ly2);
9442 var px2y1 = rotate(lx2, ly1);
9443 var px2y2 = rotate(lx2, ly2);
9444 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9445 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9446 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9447 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9448 }
9449
9450 var bbPrefixRot = bbPrefix + 'Rot';
9451 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9452 bbRot.x1 = lx1;
9453 bbRot.y1 = ly1;
9454 bbRot.x2 = lx2;
9455 bbRot.y2 = ly2;
9456 bbRot.w = lx2 - lx1;
9457 bbRot.h = ly2 - ly1;
9458 updateBounds(bounds, lx1, ly1, lx2, ly2);
9459 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9460 }
9461
9462 return bounds;
9463}; // get the bounding box of the elements (in raw model position)
9464
9465
9466var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9467 var cy = ele._private.cy;
9468 var styleEnabled = cy.styleEnabled();
9469 var headless = cy.headless();
9470 var bounds = makeBoundingBox();
9471 var _p = ele._private;
9472 var isNode = ele.isNode();
9473 var isEdge = ele.isEdge();
9474 var ex1, ex2, ey1, ey2; // extrema of body / lines
9475
9476 var x, y; // node pos
9477
9478 var rstyle = _p.rstyle;
9479 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9480 // (other factors like width values will be considered later in this function anyway)
9481
9482 var isDisplayed = function isDisplayed(ele) {
9483 return ele.pstyle('display').value !== 'none';
9484 };
9485
9486 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9487 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9488
9489 if (displayed) {
9490 // displayed suffices, since we will find zero area eles anyway
9491 var overlayOpacity = 0;
9492 var overlayPadding = 0;
9493
9494 if (styleEnabled && options.includeOverlays) {
9495 overlayOpacity = ele.pstyle('overlay-opacity').value;
9496
9497 if (overlayOpacity !== 0) {
9498 overlayPadding = ele.pstyle('overlay-padding').value;
9499 }
9500 }
9501
9502 var w = 0;
9503 var wHalf = 0;
9504
9505 if (styleEnabled) {
9506 w = ele.pstyle('width').pfValue;
9507 wHalf = w / 2;
9508 }
9509
9510 if (isNode && options.includeNodes) {
9511 var pos = ele.position();
9512 x = pos.x;
9513 y = pos.y;
9514
9515 var _w = ele.outerWidth();
9516
9517 var halfW = _w / 2;
9518 var h = ele.outerHeight();
9519 var halfH = h / 2; // handle node dimensions
9520 /////////////////////////
9521
9522 ex1 = x - halfW;
9523 ex2 = x + halfW;
9524 ey1 = y - halfH;
9525 ey2 = y + halfH;
9526 updateBounds(bounds, ex1, ey1, ex2, ey2);
9527 } else if (isEdge && options.includeEdges) {
9528 if (styleEnabled && !headless) {
9529 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9530 //////////////////////////////////////////////
9531
9532 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9533 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9534 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9535 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9536
9537 ex1 -= wHalf;
9538 ex2 += wHalf;
9539 ey1 -= wHalf;
9540 ey2 += wHalf;
9541 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9542 ////////////////
9543
9544 if (curveStyle === 'haystack') {
9545 var hpts = rstyle.haystackPts;
9546
9547 if (hpts && hpts.length === 2) {
9548 ex1 = hpts[0].x;
9549 ey1 = hpts[0].y;
9550 ex2 = hpts[1].x;
9551 ey2 = hpts[1].y;
9552
9553 if (ex1 > ex2) {
9554 var temp = ex1;
9555 ex1 = ex2;
9556 ex2 = temp;
9557 }
9558
9559 if (ey1 > ey2) {
9560 var _temp = ey1;
9561 ey1 = ey2;
9562 ey2 = _temp;
9563 }
9564
9565 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9566 }
9567 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9568 var pts;
9569
9570 switch (curveStyle) {
9571 case 'bezier':
9572 case 'unbundled-bezier':
9573 pts = rstyle.bezierPts;
9574 break;
9575
9576 case 'segments':
9577 case 'taxi':
9578 pts = rstyle.linePts;
9579 break;
9580 }
9581
9582 if (pts != null) {
9583 for (var j = 0; j < pts.length; j++) {
9584 var pt = pts[j];
9585 ex1 = pt.x - wHalf;
9586 ex2 = pt.x + wHalf;
9587 ey1 = pt.y - wHalf;
9588 ey2 = pt.y + wHalf;
9589 updateBounds(bounds, ex1, ey1, ex2, ey2);
9590 }
9591 }
9592 } // bezier-like or segment-like edge
9593
9594 } else {
9595 // headless or style disabled
9596 // fallback on source and target positions
9597 //////////////////////////////////////////
9598 var n1 = ele.source();
9599 var n1pos = n1.position();
9600 var n2 = ele.target();
9601 var n2pos = n2.position();
9602 ex1 = n1pos.x;
9603 ex2 = n2pos.x;
9604 ey1 = n1pos.y;
9605 ey2 = n2pos.y;
9606
9607 if (ex1 > ex2) {
9608 var _temp2 = ex1;
9609 ex1 = ex2;
9610 ex2 = _temp2;
9611 }
9612
9613 if (ey1 > ey2) {
9614 var _temp3 = ey1;
9615 ey1 = ey2;
9616 ey2 = _temp3;
9617 } // take into account edge width
9618
9619
9620 ex1 -= wHalf;
9621 ex2 += wHalf;
9622 ey1 -= wHalf;
9623 ey2 += wHalf;
9624 updateBounds(bounds, ex1, ey1, ex2, ey2);
9625 } // headless or style disabled
9626
9627 } // edges
9628 // handle edge arrow size
9629 /////////////////////////
9630
9631
9632 if (styleEnabled && options.includeEdges && isEdge) {
9633 updateBoundsFromArrow(bounds, ele, 'mid-source');
9634 updateBoundsFromArrow(bounds, ele, 'mid-target');
9635 updateBoundsFromArrow(bounds, ele, 'source');
9636 updateBoundsFromArrow(bounds, ele, 'target');
9637 } // ghost
9638 ////////
9639
9640
9641 if (styleEnabled) {
9642 var ghost = ele.pstyle('ghost').value === 'yes';
9643
9644 if (ghost) {
9645 var gx = ele.pstyle('ghost-offset-x').pfValue;
9646 var gy = ele.pstyle('ghost-offset-y').pfValue;
9647 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9648 }
9649 } // always store the body bounds separately from the labels
9650
9651
9652 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9653 assignBoundingBox(bbBody, bounds);
9654 expandBoundingBoxSides(bbBody, manualExpansion);
9655 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9656 // overlay
9657 //////////
9658
9659 if (styleEnabled) {
9660 ex1 = bounds.x1;
9661 ex2 = bounds.x2;
9662 ey1 = bounds.y1;
9663 ey2 = bounds.y2;
9664 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
9665 } // always store the body bounds separately from the labels
9666
9667
9668 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9669 assignBoundingBox(bbOverlay, bounds);
9670 expandBoundingBoxSides(bbOverlay, manualExpansion);
9671 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9672 // handle label dimensions
9673 //////////////////////////
9674
9675 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9676
9677 if (bbLabels.all != null) {
9678 clearBoundingBox(bbLabels.all);
9679 } else {
9680 bbLabels.all = makeBoundingBox();
9681 }
9682
9683 if (styleEnabled && options.includeLabels) {
9684 if (options.includeMainLabels) {
9685 updateBoundsFromLabel(bounds, ele, null);
9686 }
9687
9688 if (isEdge) {
9689 if (options.includeSourceLabels) {
9690 updateBoundsFromLabel(bounds, ele, 'source');
9691 }
9692
9693 if (options.includeTargetLabels) {
9694 updateBoundsFromLabel(bounds, ele, 'target');
9695 }
9696 }
9697 } // style enabled for labels
9698
9699 } // if displayed
9700
9701
9702 bounds.x1 = noninf(bounds.x1);
9703 bounds.y1 = noninf(bounds.y1);
9704 bounds.x2 = noninf(bounds.x2);
9705 bounds.y2 = noninf(bounds.y2);
9706 bounds.w = noninf(bounds.x2 - bounds.x1);
9707 bounds.h = noninf(bounds.y2 - bounds.y1);
9708
9709 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9710 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9711
9712 expandBoundingBox(bounds, 1);
9713 }
9714
9715 return bounds;
9716};
9717
9718var getKey = function getKey(opts) {
9719 var i = 0;
9720
9721 var tf = function tf(val) {
9722 return (val ? 1 : 0) << i++;
9723 };
9724
9725 var key = 0;
9726 key += tf(opts.incudeNodes);
9727 key += tf(opts.includeEdges);
9728 key += tf(opts.includeLabels);
9729 key += tf(opts.includeMainLabels);
9730 key += tf(opts.includeSourceLabels);
9731 key += tf(opts.includeTargetLabels);
9732 key += tf(opts.includeOverlays);
9733 return key;
9734};
9735
9736var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9737 if (ele.isEdge()) {
9738 var p1 = ele.source().position();
9739 var p2 = ele.target().position();
9740
9741 var r = function r(x) {
9742 return Math.round(x);
9743 };
9744
9745 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9746 } else {
9747 return 0;
9748 }
9749};
9750
9751var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9752 var _p = ele._private;
9753 var bb;
9754 var isEdge = ele.isEdge();
9755 var key = opts == null ? defBbOptsKey : getKey(opts);
9756 var usingDefOpts = key === defBbOptsKey;
9757 var currPosKey = getBoundingBoxPosKey(ele);
9758 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9759 var useCache = opts.useCache && isPosKeySame;
9760
9761 var isDirty = function isDirty(ele) {
9762 return ele._private.bbCache == null || ele._private.styleDirty;
9763 };
9764
9765 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9766
9767 if (needRecalc) {
9768 if (!isPosKeySame) {
9769 ele.recalculateRenderedStyle(useCache);
9770 }
9771
9772 bb = boundingBoxImpl(ele, defBbOpts);
9773 _p.bbCache = bb;
9774 _p.bbCachePosKey = currPosKey;
9775 } else {
9776 bb = _p.bbCache;
9777 } // not using def opts => need to build up bb from combination of sub bbs
9778
9779
9780 if (!usingDefOpts) {
9781 var isNode = ele.isNode();
9782 bb = makeBoundingBox();
9783
9784 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9785 if (opts.includeOverlays) {
9786 updateBoundsFromBox(bb, _p.overlayBounds);
9787 } else {
9788 updateBoundsFromBox(bb, _p.bodyBounds);
9789 }
9790 }
9791
9792 if (opts.includeLabels) {
9793 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9794 updateBoundsFromBox(bb, _p.labelBounds.all);
9795 } else {
9796 if (opts.includeMainLabels) {
9797 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9798 }
9799
9800 if (opts.includeSourceLabels) {
9801 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9802 }
9803
9804 if (opts.includeTargetLabels) {
9805 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9806 }
9807 }
9808 }
9809
9810 bb.w = bb.x2 - bb.x1;
9811 bb.h = bb.y2 - bb.y1;
9812 }
9813
9814 return bb;
9815};
9816
9817var defBbOpts = {
9818 includeNodes: true,
9819 includeEdges: true,
9820 includeLabels: true,
9821 includeMainLabels: true,
9822 includeSourceLabels: true,
9823 includeTargetLabels: true,
9824 includeOverlays: true,
9825 useCache: true
9826};
9827var defBbOptsKey = getKey(defBbOpts);
9828var filledBbOpts = defaults(defBbOpts);
9829
9830elesfn$k.boundingBox = function (options) {
9831 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9832 // specified s.t. the cache is used, so check for this case to make it faster by
9833 // avoiding the overhead of the rest of the function
9834
9835 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9836 if (options === undefined) {
9837 options = defBbOpts;
9838 } else {
9839 options = filledBbOpts(options);
9840 }
9841
9842 bounds = cachedBoundingBoxImpl(this[0], options);
9843 } else {
9844 bounds = makeBoundingBox();
9845 options = options || defBbOpts;
9846 var opts = filledBbOpts(options);
9847 var eles = this;
9848 var cy = eles.cy();
9849 var styleEnabled = cy.styleEnabled();
9850
9851 if (styleEnabled) {
9852 for (var i = 0; i < eles.length; i++) {
9853 var ele = eles[i];
9854 var _p = ele._private;
9855 var currPosKey = getBoundingBoxPosKey(ele);
9856 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9857 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
9858 ele.recalculateRenderedStyle(useCache);
9859 }
9860 }
9861
9862 this.updateCompoundBounds(!options.useCache);
9863
9864 for (var _i = 0; _i < eles.length; _i++) {
9865 var _ele = eles[_i];
9866 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9867 }
9868 }
9869
9870 bounds.x1 = noninf(bounds.x1);
9871 bounds.y1 = noninf(bounds.y1);
9872 bounds.x2 = noninf(bounds.x2);
9873 bounds.y2 = noninf(bounds.y2);
9874 bounds.w = noninf(bounds.x2 - bounds.x1);
9875 bounds.h = noninf(bounds.y2 - bounds.y1);
9876 return bounds;
9877};
9878
9879elesfn$k.dirtyBoundingBoxCache = function () {
9880 for (var i = 0; i < this.length; i++) {
9881 var _p = this[i]._private;
9882 _p.bbCache = null;
9883 _p.bbCachePosKey = null;
9884 _p.bodyBounds = null;
9885 _p.overlayBounds = null;
9886 _p.labelBounds.all = null;
9887 _p.labelBounds.source = null;
9888 _p.labelBounds.target = null;
9889 _p.labelBounds.main = null;
9890 _p.labelBounds.sourceRot = null;
9891 _p.labelBounds.targetRot = null;
9892 _p.labelBounds.mainRot = null;
9893 _p.arrowBounds.source = null;
9894 _p.arrowBounds.target = null;
9895 _p.arrowBounds['mid-source'] = null;
9896 _p.arrowBounds['mid-target'] = null;
9897 }
9898
9899 this.emitAndNotify('bounds');
9900 return this;
9901}; // private helper to get bounding box for custom node positions
9902// - good for perf in certain cases but currently requires dirtying the rendered style
9903// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9904// - try to use for only things like discrete layouts where the node position would change anyway
9905
9906
9907elesfn$k.boundingBoxAt = function (fn) {
9908 var nodes = this.nodes();
9909 var cy = this.cy();
9910 var hasCompoundNodes = cy.hasCompoundNodes();
9911 var parents = cy.collection();
9912
9913 if (hasCompoundNodes) {
9914 parents = nodes.filter(function (node) {
9915 return node.isParent();
9916 });
9917 nodes = nodes.not(parents);
9918 }
9919
9920 if (plainObject(fn)) {
9921 var obj = fn;
9922
9923 fn = function fn() {
9924 return obj;
9925 };
9926 }
9927
9928 var storeOldPos = function storeOldPos(node, i) {
9929 return node._private.bbAtOldPos = fn(node, i);
9930 };
9931
9932 var getOldPos = function getOldPos(node) {
9933 return node._private.bbAtOldPos;
9934 };
9935
9936 cy.startBatch();
9937 nodes.forEach(storeOldPos).silentPositions(fn);
9938
9939 if (hasCompoundNodes) {
9940 parents.dirtyCompoundBoundsCache();
9941 parents.dirtyBoundingBoxCache();
9942 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9943 }
9944
9945 var bb = copyBoundingBox(this.boundingBox({
9946 useCache: false
9947 }));
9948 nodes.silentPositions(getOldPos);
9949
9950 if (hasCompoundNodes) {
9951 parents.dirtyCompoundBoundsCache();
9952 parents.dirtyBoundingBoxCache();
9953 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9954 }
9955
9956 cy.endBatch();
9957 return bb;
9958};
9959
9960fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
9961fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
9962var bounds = elesfn$k;
9963
9964var fn$4, elesfn$l;
9965fn$4 = elesfn$l = {};
9966
9967var defineDimFns = function defineDimFns(opts) {
9968 opts.uppercaseName = capitalize(opts.name);
9969 opts.autoName = 'auto' + opts.uppercaseName;
9970 opts.labelName = 'label' + opts.uppercaseName;
9971 opts.outerName = 'outer' + opts.uppercaseName;
9972 opts.uppercaseOuterName = capitalize(opts.outerName);
9973
9974 fn$4[opts.name] = function dimImpl() {
9975 var ele = this[0];
9976 var _p = ele._private;
9977 var cy = _p.cy;
9978 var styleEnabled = cy._private.styleEnabled;
9979
9980 if (ele) {
9981 if (styleEnabled) {
9982 if (ele.isParent()) {
9983 ele.updateCompoundBounds();
9984 return _p[opts.autoName] || 0;
9985 }
9986
9987 var d = ele.pstyle(opts.name);
9988
9989 switch (d.strValue) {
9990 case 'label':
9991 ele.recalculateRenderedStyle();
9992 return _p.rstyle[opts.labelName] || 0;
9993
9994 default:
9995 return d.pfValue;
9996 }
9997 } else {
9998 return 1;
9999 }
10000 }
10001 };
10002
10003 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10004 var ele = this[0];
10005 var _p = ele._private;
10006 var cy = _p.cy;
10007 var styleEnabled = cy._private.styleEnabled;
10008
10009 if (ele) {
10010 if (styleEnabled) {
10011 var dim = ele[opts.name]();
10012 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10013
10014 var padding = 2 * ele.padding();
10015 return dim + border + padding;
10016 } else {
10017 return 1;
10018 }
10019 }
10020 };
10021
10022 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10023 var ele = this[0];
10024
10025 if (ele) {
10026 var d = ele[opts.name]();
10027 return d * this.cy().zoom();
10028 }
10029 };
10030
10031 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10032 var ele = this[0];
10033
10034 if (ele) {
10035 var od = ele[opts.outerName]();
10036 return od * this.cy().zoom();
10037 }
10038 };
10039};
10040
10041defineDimFns({
10042 name: 'width'
10043});
10044defineDimFns({
10045 name: 'height'
10046});
10047
10048elesfn$l.padding = function () {
10049 var ele = this[0];
10050 var _p = ele._private;
10051
10052 if (ele.isParent()) {
10053 ele.updateCompoundBounds();
10054
10055 if (_p.autoPadding !== undefined) {
10056 return _p.autoPadding;
10057 } else {
10058 return ele.pstyle('padding').pfValue;
10059 }
10060 } else {
10061 return ele.pstyle('padding').pfValue;
10062 }
10063};
10064
10065elesfn$l.paddedHeight = function () {
10066 var ele = this[0];
10067 return ele.height() + 2 * ele.padding();
10068};
10069
10070elesfn$l.paddedWidth = function () {
10071 var ele = this[0];
10072 return ele.width() + 2 * ele.padding();
10073};
10074
10075var widthHeight = elesfn$l;
10076
10077var ifEdge = function ifEdge(ele, getValue) {
10078 if (ele.isEdge()) {
10079 return getValue(ele);
10080 }
10081};
10082
10083var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10084 if (ele.isEdge()) {
10085 var cy = ele.cy();
10086 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10087 }
10088};
10089
10090var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10091 if (ele.isEdge()) {
10092 var cy = ele.cy();
10093 var pan = cy.pan();
10094 var zoom = cy.zoom();
10095 return getPoints(ele).map(function (p) {
10096 return modelToRenderedPosition(p, zoom, pan);
10097 });
10098 }
10099};
10100
10101var controlPoints = function controlPoints(ele) {
10102 return ele.renderer().getControlPoints(ele);
10103};
10104
10105var segmentPoints = function segmentPoints(ele) {
10106 return ele.renderer().getSegmentPoints(ele);
10107};
10108
10109var sourceEndpoint = function sourceEndpoint(ele) {
10110 return ele.renderer().getSourceEndpoint(ele);
10111};
10112
10113var targetEndpoint = function targetEndpoint(ele) {
10114 return ele.renderer().getTargetEndpoint(ele);
10115};
10116
10117var midpoint = function midpoint(ele) {
10118 return ele.renderer().getEdgeMidpoint(ele);
10119};
10120
10121var pts = {
10122 controlPoints: {
10123 get: controlPoints,
10124 mult: true
10125 },
10126 segmentPoints: {
10127 get: segmentPoints,
10128 mult: true
10129 },
10130 sourceEndpoint: {
10131 get: sourceEndpoint
10132 },
10133 targetEndpoint: {
10134 get: targetEndpoint
10135 },
10136 midpoint: {
10137 get: midpoint
10138 }
10139};
10140
10141var renderedName = function renderedName(name) {
10142 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10143};
10144
10145var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10146 var spec = pts[name];
10147 var rName = renderedName(name);
10148
10149 obj[name] = function () {
10150 return ifEdge(this, spec.get);
10151 };
10152
10153 if (spec.mult) {
10154 obj[rName] = function () {
10155 return ifEdgeRenderedPositions(this, spec.get);
10156 };
10157 } else {
10158 obj[rName] = function () {
10159 return ifEdgeRenderedPosition(this, spec.get);
10160 };
10161 }
10162
10163 return obj;
10164}, {});
10165
10166var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10167
10168/*!
10169Event object based on jQuery events, MIT license
10170
10171https://jquery.org/license/
10172https://tldrlegal.com/license/mit-license
10173https://github.com/jquery/jquery/blob/master/src/event.js
10174*/
10175var Event = function Event(src, props) {
10176 this.recycle(src, props);
10177};
10178
10179function returnFalse() {
10180 return false;
10181}
10182
10183function returnTrue() {
10184 return true;
10185} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10186
10187
10188Event.prototype = {
10189 instanceString: function instanceString() {
10190 return 'event';
10191 },
10192 recycle: function recycle(src, props) {
10193 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10194
10195 if (src != null && src.preventDefault) {
10196 // Browser Event object
10197 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10198 // by a handler lower down the tree; reflect the correct value.
10199
10200 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10201 } else if (src != null && src.type) {
10202 // Plain object containing all event details
10203 props = src;
10204 } else {
10205 // Event string
10206 this.type = src;
10207 } // Put explicitly provided properties onto the event object
10208
10209
10210 if (props != null) {
10211 // more efficient to manually copy fields we use
10212 this.originalEvent = props.originalEvent;
10213 this.type = props.type != null ? props.type : this.type;
10214 this.cy = props.cy;
10215 this.target = props.target;
10216 this.position = props.position;
10217 this.renderedPosition = props.renderedPosition;
10218 this.namespace = props.namespace;
10219 this.layout = props.layout;
10220 }
10221
10222 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10223 // create a rendered position based on the passed position
10224 var pos = this.position;
10225 var zoom = this.cy.zoom();
10226 var pan = this.cy.pan();
10227 this.renderedPosition = {
10228 x: pos.x * zoom + pan.x,
10229 y: pos.y * zoom + pan.y
10230 };
10231 } // Create a timestamp if incoming event doesn't have one
10232
10233
10234 this.timeStamp = src && src.timeStamp || Date.now();
10235 },
10236 preventDefault: function preventDefault() {
10237 this.isDefaultPrevented = returnTrue;
10238 var e = this.originalEvent;
10239
10240 if (!e) {
10241 return;
10242 } // if preventDefault exists run it on the original event
10243
10244
10245 if (e.preventDefault) {
10246 e.preventDefault();
10247 }
10248 },
10249 stopPropagation: function stopPropagation() {
10250 this.isPropagationStopped = returnTrue;
10251 var e = this.originalEvent;
10252
10253 if (!e) {
10254 return;
10255 } // if stopPropagation exists run it on the original event
10256
10257
10258 if (e.stopPropagation) {
10259 e.stopPropagation();
10260 }
10261 },
10262 stopImmediatePropagation: function stopImmediatePropagation() {
10263 this.isImmediatePropagationStopped = returnTrue;
10264 this.stopPropagation();
10265 },
10266 isDefaultPrevented: returnFalse,
10267 isPropagationStopped: returnFalse,
10268 isImmediatePropagationStopped: returnFalse
10269};
10270
10271var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10272
10273var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10274
10275var defaults$8 = {
10276 qualifierCompare: function qualifierCompare(q1, q2) {
10277 return q1 === q2;
10278 },
10279 eventMatches: function eventMatches()
10280 /*context, listener, eventObj*/
10281 {
10282 return true;
10283 },
10284 addEventFields: function addEventFields()
10285 /*context, evt*/
10286 {},
10287 callbackContext: function callbackContext(context
10288 /*, listener, eventObj*/
10289 ) {
10290 return context;
10291 },
10292 beforeEmit: function beforeEmit()
10293 /* context, listener, eventObj */
10294 {},
10295 afterEmit: function afterEmit()
10296 /* context, listener, eventObj */
10297 {},
10298 bubble: function bubble()
10299 /*context*/
10300 {
10301 return false;
10302 },
10303 parent: function parent()
10304 /*context*/
10305 {
10306 return null;
10307 },
10308 context: null
10309};
10310var defaultsKeys = Object.keys(defaults$8);
10311var emptyOpts = {};
10312
10313function Emitter() {
10314 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10315 var context = arguments.length > 1 ? arguments[1] : undefined;
10316
10317 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10318 for (var i = 0; i < defaultsKeys.length; i++) {
10319 var key = defaultsKeys[i];
10320 this[key] = opts[key] || defaults$8[key];
10321 }
10322
10323 this.context = context || this.context;
10324 this.listeners = [];
10325 this.emitting = 0;
10326}
10327
10328var p = Emitter.prototype;
10329
10330var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10331 if (fn(qualifier)) {
10332 callback = qualifier;
10333 qualifier = null;
10334 }
10335
10336 if (confOverrides) {
10337 if (conf == null) {
10338 conf = confOverrides;
10339 } else {
10340 conf = extend({}, conf, confOverrides);
10341 }
10342 }
10343
10344 var eventList = array(events) ? events : events.split(/\s+/);
10345
10346 for (var i = 0; i < eventList.length; i++) {
10347 var evt = eventList[i];
10348
10349 if (emptyString(evt)) {
10350 continue;
10351 }
10352
10353 var match = evt.match(eventRegex); // type[.namespace]
10354
10355 if (match) {
10356 var type = match[1];
10357 var namespace = match[2] ? match[2] : null;
10358 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10359
10360 if (ret === false) {
10361 break;
10362 } // allow exiting early
10363
10364 }
10365 }
10366};
10367
10368var makeEventObj = function makeEventObj(self, obj) {
10369 self.addEventFields(self.context, obj);
10370 return new Event(obj.type, obj);
10371};
10372
10373var forEachEventObj = function forEachEventObj(self, handler, events) {
10374 if (event(events)) {
10375 handler(self, events);
10376 return;
10377 } else if (plainObject(events)) {
10378 handler(self, makeEventObj(self, events));
10379 return;
10380 }
10381
10382 var eventList = array(events) ? events : events.split(/\s+/);
10383
10384 for (var i = 0; i < eventList.length; i++) {
10385 var evt = eventList[i];
10386
10387 if (emptyString(evt)) {
10388 continue;
10389 }
10390
10391 var match = evt.match(eventRegex); // type[.namespace]
10392
10393 if (match) {
10394 var type = match[1];
10395 var namespace = match[2] ? match[2] : null;
10396 var eventObj = makeEventObj(self, {
10397 type: type,
10398 namespace: namespace,
10399 target: self.context
10400 });
10401 handler(self, eventObj);
10402 }
10403 }
10404};
10405
10406p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10407 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10408 if (fn(callback)) {
10409 self.listeners.push({
10410 event: event,
10411 // full event string
10412 callback: callback,
10413 // callback to run
10414 type: type,
10415 // the event type (e.g. 'click')
10416 namespace: namespace,
10417 // the event namespace (e.g. ".foo")
10418 qualifier: qualifier,
10419 // a restriction on whether to match this emitter
10420 conf: conf // additional configuration
10421
10422 });
10423 }
10424 }, events, qualifier, callback, conf, confOverrides);
10425 return this;
10426};
10427
10428p.one = function (events, qualifier, callback, conf) {
10429 return this.on(events, qualifier, callback, conf, {
10430 one: true
10431 });
10432};
10433
10434p.removeListener = p.off = function (events, qualifier, callback, conf) {
10435 var _this = this;
10436
10437 if (this.emitting !== 0) {
10438 this.listeners = copyArray(this.listeners);
10439 }
10440
10441 var listeners = this.listeners;
10442
10443 var _loop = function _loop(i) {
10444 var listener = listeners[i];
10445 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10446 /*, conf*/
10447 ) {
10448 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10449 listeners.splice(i, 1);
10450 return false;
10451 }
10452 }, events, qualifier, callback, conf);
10453 };
10454
10455 for (var i = listeners.length - 1; i >= 0; i--) {
10456 _loop(i);
10457 }
10458
10459 return this;
10460};
10461
10462p.removeAllListeners = function () {
10463 return this.removeListener('*');
10464};
10465
10466p.emit = p.trigger = function (events, extraParams, manualCallback) {
10467 var listeners = this.listeners;
10468 var numListenersBeforeEmit = listeners.length;
10469 this.emitting++;
10470
10471 if (!array(extraParams)) {
10472 extraParams = [extraParams];
10473 }
10474
10475 forEachEventObj(this, function (self, eventObj) {
10476 if (manualCallback != null) {
10477 listeners = [{
10478 event: eventObj.event,
10479 type: eventObj.type,
10480 namespace: eventObj.namespace,
10481 callback: manualCallback
10482 }];
10483 numListenersBeforeEmit = listeners.length;
10484 }
10485
10486 var _loop2 = function _loop2(i) {
10487 var listener = listeners[i];
10488
10489 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10490 var args = [eventObj];
10491
10492 if (extraParams != null) {
10493 push(args, extraParams);
10494 }
10495
10496 self.beforeEmit(self.context, listener, eventObj);
10497
10498 if (listener.conf && listener.conf.one) {
10499 self.listeners = self.listeners.filter(function (l) {
10500 return l !== listener;
10501 });
10502 }
10503
10504 var context = self.callbackContext(self.context, listener, eventObj);
10505 var ret = listener.callback.apply(context, args);
10506 self.afterEmit(self.context, listener, eventObj);
10507
10508 if (ret === false) {
10509 eventObj.stopPropagation();
10510 eventObj.preventDefault();
10511 }
10512 } // if listener matches
10513
10514 };
10515
10516 for (var i = 0; i < numListenersBeforeEmit; i++) {
10517 _loop2(i);
10518 } // for listener
10519
10520
10521 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10522 self.parent(self.context).emit(eventObj, extraParams);
10523 }
10524 }, events);
10525 this.emitting--;
10526 return this;
10527};
10528
10529var emitterOptions = {
10530 qualifierCompare: function qualifierCompare(selector1, selector2) {
10531 if (selector1 == null || selector2 == null) {
10532 return selector1 == null && selector2 == null;
10533 } else {
10534 return selector1.sameText(selector2);
10535 }
10536 },
10537 eventMatches: function eventMatches(ele, listener, eventObj) {
10538 var selector = listener.qualifier;
10539
10540 if (selector != null) {
10541 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10542 }
10543
10544 return true;
10545 },
10546 addEventFields: function addEventFields(ele, evt) {
10547 evt.cy = ele.cy();
10548 evt.target = ele;
10549 },
10550 callbackContext: function callbackContext(ele, listener, eventObj) {
10551 return listener.qualifier != null ? eventObj.target : ele;
10552 },
10553 beforeEmit: function beforeEmit(context, listener
10554 /*, eventObj*/
10555 ) {
10556 if (listener.conf && listener.conf.once) {
10557 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10558 }
10559 },
10560 bubble: function bubble() {
10561 return true;
10562 },
10563 parent: function parent(ele) {
10564 return ele.isChild() ? ele.parent() : ele.cy();
10565 }
10566};
10567
10568var argSelector = function argSelector(arg) {
10569 if (string(arg)) {
10570 return new Selector(arg);
10571 } else {
10572 return arg;
10573 }
10574};
10575
10576var elesfn$m = {
10577 createEmitter: function createEmitter() {
10578 for (var i = 0; i < this.length; i++) {
10579 var ele = this[i];
10580 var _p = ele._private;
10581
10582 if (!_p.emitter) {
10583 _p.emitter = new Emitter(emitterOptions, ele);
10584 }
10585 }
10586
10587 return this;
10588 },
10589 emitter: function emitter() {
10590 return this._private.emitter;
10591 },
10592 on: function on(events, selector, callback) {
10593 var argSel = argSelector(selector);
10594
10595 for (var i = 0; i < this.length; i++) {
10596 var ele = this[i];
10597 ele.emitter().on(events, argSel, callback);
10598 }
10599
10600 return this;
10601 },
10602 removeListener: function removeListener(events, selector, callback) {
10603 var argSel = argSelector(selector);
10604
10605 for (var i = 0; i < this.length; i++) {
10606 var ele = this[i];
10607 ele.emitter().removeListener(events, argSel, callback);
10608 }
10609
10610 return this;
10611 },
10612 removeAllListeners: function removeAllListeners() {
10613 for (var i = 0; i < this.length; i++) {
10614 var ele = this[i];
10615 ele.emitter().removeAllListeners();
10616 }
10617
10618 return this;
10619 },
10620 one: function one(events, selector, callback) {
10621 var argSel = argSelector(selector);
10622
10623 for (var i = 0; i < this.length; i++) {
10624 var ele = this[i];
10625 ele.emitter().one(events, argSel, callback);
10626 }
10627
10628 return this;
10629 },
10630 once: function once(events, selector, callback) {
10631 var argSel = argSelector(selector);
10632
10633 for (var i = 0; i < this.length; i++) {
10634 var ele = this[i];
10635 ele.emitter().on(events, argSel, callback, {
10636 once: true,
10637 onceCollection: this
10638 });
10639 }
10640 },
10641 emit: function emit(events, extraParams) {
10642 for (var i = 0; i < this.length; i++) {
10643 var ele = this[i];
10644 ele.emitter().emit(events, extraParams);
10645 }
10646
10647 return this;
10648 },
10649 emitAndNotify: function emitAndNotify(event, extraParams) {
10650 // for internal use only
10651 if (this.length === 0) {
10652 return;
10653 } // empty collections don't need to notify anything
10654 // notify renderer
10655
10656
10657 this.cy().notify(event, this);
10658 this.emit(event, extraParams);
10659 return this;
10660 }
10661};
10662define$3.eventAliasesOn(elesfn$m);
10663
10664var elesfn$n = {
10665 nodes: function nodes(selector) {
10666 return this.filter(function (ele) {
10667 return ele.isNode();
10668 }).filter(selector);
10669 },
10670 edges: function edges(selector) {
10671 return this.filter(function (ele) {
10672 return ele.isEdge();
10673 }).filter(selector);
10674 },
10675 // internal helper to get nodes and edges as separate collections with single iteration over elements
10676 byGroup: function byGroup() {
10677 var nodes = this.spawn();
10678 var edges = this.spawn();
10679
10680 for (var i = 0; i < this.length; i++) {
10681 var ele = this[i];
10682
10683 if (ele.isNode()) {
10684 nodes.push(ele);
10685 } else {
10686 edges.push(ele);
10687 }
10688 }
10689
10690 return {
10691 nodes: nodes,
10692 edges: edges
10693 };
10694 },
10695 filter: function filter(_filter, thisArg) {
10696 if (_filter === undefined) {
10697 // check this first b/c it's the most common/performant case
10698 return this;
10699 } else if (string(_filter) || elementOrCollection(_filter)) {
10700 return new Selector(_filter).filter(this);
10701 } else if (fn(_filter)) {
10702 var filterEles = this.spawn();
10703 var eles = this;
10704
10705 for (var i = 0; i < eles.length; i++) {
10706 var ele = eles[i];
10707 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10708
10709 if (include) {
10710 filterEles.push(ele);
10711 }
10712 }
10713
10714 return filterEles;
10715 }
10716
10717 return this.spawn(); // if not handled by above, give 'em an empty collection
10718 },
10719 not: function not(toRemove) {
10720 if (!toRemove) {
10721 return this;
10722 } else {
10723 if (string(toRemove)) {
10724 toRemove = this.filter(toRemove);
10725 }
10726
10727 var elements = this.spawn();
10728
10729 for (var i = 0; i < this.length; i++) {
10730 var element = this[i];
10731 var remove = toRemove.has(element);
10732
10733 if (!remove) {
10734 elements.push(element);
10735 }
10736 }
10737
10738 return elements;
10739 }
10740 },
10741 absoluteComplement: function absoluteComplement() {
10742 var cy = this.cy();
10743 return cy.mutableElements().not(this);
10744 },
10745 intersect: function intersect(other) {
10746 // if a selector is specified, then filter by it instead
10747 if (string(other)) {
10748 var selector = other;
10749 return this.filter(selector);
10750 }
10751
10752 var elements = this.spawn();
10753 var col1 = this;
10754 var col2 = other;
10755 var col1Smaller = this.length < other.length;
10756 var colS = col1Smaller ? col1 : col2;
10757 var colL = col1Smaller ? col2 : col1;
10758
10759 for (var i = 0; i < colS.length; i++) {
10760 var ele = colS[i];
10761
10762 if (colL.has(ele)) {
10763 elements.push(ele);
10764 }
10765 }
10766
10767 return elements;
10768 },
10769 xor: function xor(other) {
10770 var cy = this._private.cy;
10771
10772 if (string(other)) {
10773 other = cy.$(other);
10774 }
10775
10776 var elements = this.spawn();
10777 var col1 = this;
10778 var col2 = other;
10779
10780 var add = function add(col, other) {
10781 for (var i = 0; i < col.length; i++) {
10782 var ele = col[i];
10783 var id = ele._private.data.id;
10784 var inOther = other.hasElementWithId(id);
10785
10786 if (!inOther) {
10787 elements.push(ele);
10788 }
10789 }
10790 };
10791
10792 add(col1, col2);
10793 add(col2, col1);
10794 return elements;
10795 },
10796 diff: function diff(other) {
10797 var cy = this._private.cy;
10798
10799 if (string(other)) {
10800 other = cy.$(other);
10801 }
10802
10803 var left = this.spawn();
10804 var right = this.spawn();
10805 var both = this.spawn();
10806 var col1 = this;
10807 var col2 = other;
10808
10809 var add = function add(col, other, retEles) {
10810 for (var i = 0; i < col.length; i++) {
10811 var ele = col[i];
10812 var id = ele._private.data.id;
10813 var inOther = other.hasElementWithId(id);
10814
10815 if (inOther) {
10816 both.merge(ele);
10817 } else {
10818 retEles.push(ele);
10819 }
10820 }
10821 };
10822
10823 add(col1, col2, left);
10824 add(col2, col1, right);
10825 return {
10826 left: left,
10827 right: right,
10828 both: both
10829 };
10830 },
10831 add: function add(toAdd) {
10832 var cy = this._private.cy;
10833
10834 if (!toAdd) {
10835 return this;
10836 }
10837
10838 if (string(toAdd)) {
10839 var selector = toAdd;
10840 toAdd = cy.mutableElements().filter(selector);
10841 }
10842
10843 var elements = this.spawnSelf();
10844
10845 for (var i = 0; i < toAdd.length; i++) {
10846 var ele = toAdd[i];
10847 var add = !this.has(ele);
10848
10849 if (add) {
10850 elements.push(ele);
10851 }
10852 }
10853
10854 return elements;
10855 },
10856 // in place merge on calling collection
10857 merge: function merge(toAdd) {
10858 var _p = this._private;
10859 var cy = _p.cy;
10860
10861 if (!toAdd) {
10862 return this;
10863 }
10864
10865 if (toAdd && string(toAdd)) {
10866 var selector = toAdd;
10867 toAdd = cy.mutableElements().filter(selector);
10868 }
10869
10870 var map = _p.map;
10871
10872 for (var i = 0; i < toAdd.length; i++) {
10873 var toAddEle = toAdd[i];
10874 var id = toAddEle._private.data.id;
10875 var add = !map.has(id);
10876
10877 if (add) {
10878 var index = this.length++;
10879 this[index] = toAddEle;
10880 map.set(id, {
10881 ele: toAddEle,
10882 index: index
10883 });
10884 }
10885 }
10886
10887 return this; // chaining
10888 },
10889 unmergeAt: function unmergeAt(i) {
10890 var ele = this[i];
10891 var id = ele.id();
10892 var _p = this._private;
10893 var map = _p.map; // remove ele
10894
10895 this[i] = undefined;
10896 map["delete"](id);
10897 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10898
10899 if (this.length > 1 && !unmergedLastEle) {
10900 var lastEleI = this.length - 1;
10901 var lastEle = this[lastEleI];
10902 var lastEleId = lastEle._private.data.id;
10903 this[lastEleI] = undefined;
10904 this[i] = lastEle;
10905 map.set(lastEleId, {
10906 ele: lastEle,
10907 index: i
10908 });
10909 } // the collection is now 1 ele smaller
10910
10911
10912 this.length--;
10913 return this;
10914 },
10915 // remove single ele in place in calling collection
10916 unmergeOne: function unmergeOne(ele) {
10917 ele = ele[0];
10918 var _p = this._private;
10919 var id = ele._private.data.id;
10920 var map = _p.map;
10921 var entry = map.get(id);
10922
10923 if (!entry) {
10924 return this; // no need to remove
10925 }
10926
10927 var i = entry.index;
10928 this.unmergeAt(i);
10929 return this;
10930 },
10931 // remove eles in place on calling collection
10932 unmerge: function unmerge(toRemove) {
10933 var cy = this._private.cy;
10934
10935 if (!toRemove) {
10936 return this;
10937 }
10938
10939 if (toRemove && string(toRemove)) {
10940 var selector = toRemove;
10941 toRemove = cy.mutableElements().filter(selector);
10942 }
10943
10944 for (var i = 0; i < toRemove.length; i++) {
10945 this.unmergeOne(toRemove[i]);
10946 }
10947
10948 return this; // chaining
10949 },
10950 unmergeBy: function unmergeBy(toRmFn) {
10951 for (var i = this.length - 1; i >= 0; i--) {
10952 var ele = this[i];
10953
10954 if (toRmFn(ele)) {
10955 this.unmergeAt(i);
10956 }
10957 }
10958
10959 return this;
10960 },
10961 map: function map(mapFn, thisArg) {
10962 var arr = [];
10963 var eles = this;
10964
10965 for (var i = 0; i < eles.length; i++) {
10966 var ele = eles[i];
10967 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
10968 arr.push(ret);
10969 }
10970
10971 return arr;
10972 },
10973 reduce: function reduce(fn, initialValue) {
10974 var val = initialValue;
10975 var eles = this;
10976
10977 for (var i = 0; i < eles.length; i++) {
10978 val = fn(val, eles[i], i, eles);
10979 }
10980
10981 return val;
10982 },
10983 max: function max(valFn, thisArg) {
10984 var max = -Infinity;
10985 var maxEle;
10986 var eles = this;
10987
10988 for (var i = 0; i < eles.length; i++) {
10989 var ele = eles[i];
10990 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
10991
10992 if (val > max) {
10993 max = val;
10994 maxEle = ele;
10995 }
10996 }
10997
10998 return {
10999 value: max,
11000 ele: maxEle
11001 };
11002 },
11003 min: function min(valFn, thisArg) {
11004 var min = Infinity;
11005 var minEle;
11006 var eles = this;
11007
11008 for (var i = 0; i < eles.length; i++) {
11009 var ele = eles[i];
11010 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11011
11012 if (val < min) {
11013 min = val;
11014 minEle = ele;
11015 }
11016 }
11017
11018 return {
11019 value: min,
11020 ele: minEle
11021 };
11022 }
11023}; // aliases
11024
11025var fn$5 = elesfn$n;
11026fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11027fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11028fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11029fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11030fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11031fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11032
11033var elesfn$o = {
11034 isNode: function isNode() {
11035 return this.group() === 'nodes';
11036 },
11037 isEdge: function isEdge() {
11038 return this.group() === 'edges';
11039 },
11040 isLoop: function isLoop() {
11041 return this.isEdge() && this.source()[0] === this.target()[0];
11042 },
11043 isSimple: function isSimple() {
11044 return this.isEdge() && this.source()[0] !== this.target()[0];
11045 },
11046 group: function group() {
11047 var ele = this[0];
11048
11049 if (ele) {
11050 return ele._private.group;
11051 }
11052 }
11053};
11054
11055/**
11056 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11057 * and z-index (low to high). These styles affect how this applies:
11058 *
11059 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11060 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11061 * root to leaves of the compound graph. The last drawn is `top`.
11062 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11063 * `manual` ignores this convention and draws based on the `z-index` value setting.
11064 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11065 * `z-index` will be drawn on top of an element with a lower `z-index`.
11066 */
11067
11068var zIndexSort = function zIndexSort(a, b) {
11069 var cy = a.cy();
11070 var hasCompoundNodes = cy.hasCompoundNodes();
11071
11072 function getDepth(ele) {
11073 var style = ele.pstyle('z-compound-depth');
11074
11075 if (style.value === 'auto') {
11076 return hasCompoundNodes ? ele.zDepth() : 0;
11077 } else if (style.value === 'bottom') {
11078 return -1;
11079 } else if (style.value === 'top') {
11080 return MAX_INT;
11081 } // 'orphan'
11082
11083
11084 return 0;
11085 }
11086
11087 var depthDiff = getDepth(a) - getDepth(b);
11088
11089 if (depthDiff !== 0) {
11090 return depthDiff;
11091 }
11092
11093 function getEleDepth(ele) {
11094 var style = ele.pstyle('z-index-compare');
11095
11096 if (style.value === 'auto') {
11097 return ele.isNode() ? 1 : 0;
11098 } // 'manual'
11099
11100
11101 return 0;
11102 }
11103
11104 var eleDiff = getEleDepth(a) - getEleDepth(b);
11105
11106 if (eleDiff !== 0) {
11107 return eleDiff;
11108 }
11109
11110 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11111
11112 if (zDiff !== 0) {
11113 return zDiff;
11114 } // compare indices in the core (order added to graph w/ last on top)
11115
11116
11117 return a.poolIndex() - b.poolIndex();
11118};
11119
11120var elesfn$p = {
11121 forEach: function forEach(fn$1, thisArg) {
11122 if (fn(fn$1)) {
11123 var N = this.length;
11124
11125 for (var i = 0; i < N; i++) {
11126 var ele = this[i];
11127 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11128
11129 if (ret === false) {
11130 break;
11131 } // exit each early on return false
11132
11133 }
11134 }
11135
11136 return this;
11137 },
11138 toArray: function toArray() {
11139 var array = [];
11140
11141 for (var i = 0; i < this.length; i++) {
11142 array.push(this[i]);
11143 }
11144
11145 return array;
11146 },
11147 slice: function slice(start, end) {
11148 var array = [];
11149 var thisSize = this.length;
11150
11151 if (end == null) {
11152 end = thisSize;
11153 }
11154
11155 if (start == null) {
11156 start = 0;
11157 }
11158
11159 if (start < 0) {
11160 start = thisSize + start;
11161 }
11162
11163 if (end < 0) {
11164 end = thisSize + end;
11165 }
11166
11167 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11168 array.push(this[i]);
11169 }
11170
11171 return this.spawn(array);
11172 },
11173 size: function size() {
11174 return this.length;
11175 },
11176 eq: function eq(i) {
11177 return this[i] || this.spawn();
11178 },
11179 first: function first() {
11180 return this[0] || this.spawn();
11181 },
11182 last: function last() {
11183 return this[this.length - 1] || this.spawn();
11184 },
11185 empty: function empty() {
11186 return this.length === 0;
11187 },
11188 nonempty: function nonempty() {
11189 return !this.empty();
11190 },
11191 sort: function sort(sortFn) {
11192 if (!fn(sortFn)) {
11193 return this;
11194 }
11195
11196 var sorted = this.toArray().sort(sortFn);
11197 return this.spawn(sorted);
11198 },
11199 sortByZIndex: function sortByZIndex() {
11200 return this.sort(zIndexSort);
11201 },
11202 zDepth: function zDepth() {
11203 var ele = this[0];
11204
11205 if (!ele) {
11206 return undefined;
11207 } // let cy = ele.cy();
11208
11209
11210 var _p = ele._private;
11211 var group = _p.group;
11212
11213 if (group === 'nodes') {
11214 var depth = _p.data.parent ? ele.parents().size() : 0;
11215
11216 if (!ele.isParent()) {
11217 return MAX_INT - 1; // childless nodes always on top
11218 }
11219
11220 return depth;
11221 } else {
11222 var src = _p.source;
11223 var tgt = _p.target;
11224 var srcDepth = src.zDepth();
11225 var tgtDepth = tgt.zDepth();
11226 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11227 }
11228 }
11229};
11230elesfn$p.each = elesfn$p.forEach;
11231
11232var defineSymbolIterator = function defineSymbolIterator() {
11233 var typeofUndef = "undefined" ;
11234 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11235
11236 if (isIteratorSupported) {
11237 elesfn$p[Symbol.iterator] = function () {
11238 var _this = this;
11239
11240 // eslint-disable-line no-undef
11241 var entry = {
11242 value: undefined,
11243 done: false
11244 };
11245 var i = 0;
11246 var length = this.length;
11247 return _defineProperty({
11248 next: function next() {
11249 if (i < length) {
11250 entry.value = _this[i++];
11251 } else {
11252 entry.value = undefined;
11253 entry.done = true;
11254 }
11255
11256 return entry;
11257 }
11258 }, Symbol.iterator, function () {
11259 // eslint-disable-line no-undef
11260 return this;
11261 });
11262 };
11263 }
11264};
11265
11266defineSymbolIterator();
11267
11268var getLayoutDimensionOptions = defaults({
11269 nodeDimensionsIncludeLabels: false
11270});
11271var elesfn$q = {
11272 // Calculates and returns node dimensions { x, y } based on options given
11273 layoutDimensions: function layoutDimensions(options) {
11274 options = getLayoutDimensionOptions(options);
11275 var dims;
11276
11277 if (!this.takesUpSpace()) {
11278 dims = {
11279 w: 0,
11280 h: 0
11281 };
11282 } else if (options.nodeDimensionsIncludeLabels) {
11283 var bbDim = this.boundingBox();
11284 dims = {
11285 w: bbDim.w,
11286 h: bbDim.h
11287 };
11288 } else {
11289 dims = {
11290 w: this.outerWidth(),
11291 h: this.outerHeight()
11292 };
11293 } // sanitise the dimensions for external layouts (avoid division by zero)
11294
11295
11296 if (dims.w === 0 || dims.h === 0) {
11297 dims.w = dims.h = 1;
11298 }
11299
11300 return dims;
11301 },
11302 // using standard layout options, apply position function (w/ or w/o animation)
11303 layoutPositions: function layoutPositions(layout, options, fn) {
11304 var nodes = this.nodes().filter(function (n) {
11305 return !n.isParent();
11306 });
11307 var cy = this.cy();
11308 var layoutEles = options.eles; // nodes & edges
11309
11310 var getMemoizeKey = function getMemoizeKey(node) {
11311 return node.id();
11312 };
11313
11314 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11315
11316 layout.emit({
11317 type: 'layoutstart',
11318 layout: layout
11319 });
11320 layout.animations = [];
11321
11322 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11323 var center = {
11324 x: nodesBb.x1 + nodesBb.w / 2,
11325 y: nodesBb.y1 + nodesBb.h / 2
11326 };
11327 var spacingVector = {
11328 // scale from center of bounding box (not necessarily 0,0)
11329 x: (pos.x - center.x) * spacing,
11330 y: (pos.y - center.y) * spacing
11331 };
11332 return {
11333 x: center.x + spacingVector.x,
11334 y: center.y + spacingVector.y
11335 };
11336 };
11337
11338 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11339
11340 var spacingBb = function spacingBb() {
11341 if (!useSpacingFactor) {
11342 return null;
11343 }
11344
11345 var bb = makeBoundingBox();
11346
11347 for (var i = 0; i < nodes.length; i++) {
11348 var node = nodes[i];
11349 var pos = fnMem(node, i);
11350 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11351 }
11352
11353 return bb;
11354 };
11355
11356 var bb = spacingBb();
11357 var getFinalPos = memoize(function (node, i) {
11358 var newPos = fnMem(node, i);
11359
11360 if (useSpacingFactor) {
11361 var spacing = Math.abs(options.spacingFactor);
11362 newPos = calculateSpacing(spacing, bb, newPos);
11363 }
11364
11365 if (options.transform != null) {
11366 newPos = options.transform(node, newPos);
11367 }
11368
11369 return newPos;
11370 }, getMemoizeKey);
11371
11372 if (options.animate) {
11373 for (var i = 0; i < nodes.length; i++) {
11374 var node = nodes[i];
11375 var newPos = getFinalPos(node, i);
11376 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11377
11378 if (animateNode) {
11379 var ani = node.animation({
11380 position: newPos,
11381 duration: options.animationDuration,
11382 easing: options.animationEasing
11383 });
11384 layout.animations.push(ani);
11385 } else {
11386 node.position(newPos);
11387 }
11388 }
11389
11390 if (options.fit) {
11391 var fitAni = cy.animation({
11392 fit: {
11393 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11394 padding: options.padding
11395 },
11396 duration: options.animationDuration,
11397 easing: options.animationEasing
11398 });
11399 layout.animations.push(fitAni);
11400 } else if (options.zoom !== undefined && options.pan !== undefined) {
11401 var zoomPanAni = cy.animation({
11402 zoom: options.zoom,
11403 pan: options.pan,
11404 duration: options.animationDuration,
11405 easing: options.animationEasing
11406 });
11407 layout.animations.push(zoomPanAni);
11408 }
11409
11410 layout.animations.forEach(function (ani) {
11411 return ani.play();
11412 });
11413 layout.one('layoutready', options.ready);
11414 layout.emit({
11415 type: 'layoutready',
11416 layout: layout
11417 });
11418 Promise$1.all(layout.animations.map(function (ani) {
11419 return ani.promise();
11420 })).then(function () {
11421 layout.one('layoutstop', options.stop);
11422 layout.emit({
11423 type: 'layoutstop',
11424 layout: layout
11425 });
11426 });
11427 } else {
11428 nodes.positions(getFinalPos);
11429
11430 if (options.fit) {
11431 cy.fit(options.eles, options.padding);
11432 }
11433
11434 if (options.zoom != null) {
11435 cy.zoom(options.zoom);
11436 }
11437
11438 if (options.pan) {
11439 cy.pan(options.pan);
11440 }
11441
11442 layout.one('layoutready', options.ready);
11443 layout.emit({
11444 type: 'layoutready',
11445 layout: layout
11446 });
11447 layout.one('layoutstop', options.stop);
11448 layout.emit({
11449 type: 'layoutstop',
11450 layout: layout
11451 });
11452 }
11453
11454 return this; // chaining
11455 },
11456 layout: function layout(options) {
11457 var cy = this.cy();
11458 return cy.makeLayout(extend({}, options, {
11459 eles: this
11460 }));
11461 }
11462}; // aliases:
11463
11464elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11465
11466function styleCache(key, fn, ele) {
11467 var _p = ele._private;
11468 var cache = _p.styleCache = _p.styleCache || [];
11469 var val;
11470
11471 if ((val = cache[key]) != null) {
11472 return val;
11473 } else {
11474 val = cache[key] = fn(ele);
11475 return val;
11476 }
11477}
11478
11479function cacheStyleFunction(key, fn) {
11480 key = hashString(key);
11481 return function cachedStyleFunction(ele) {
11482 return styleCache(key, fn, ele);
11483 };
11484}
11485
11486function cachePrototypeStyleFunction(key, fn) {
11487 key = hashString(key);
11488
11489 var selfFn = function selfFn(ele) {
11490 return fn.call(ele);
11491 };
11492
11493 return function cachedPrototypeStyleFunction() {
11494 var ele = this[0];
11495
11496 if (ele) {
11497 return styleCache(key, selfFn, ele);
11498 }
11499 };
11500}
11501
11502var elesfn$r = {
11503 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11504 var cy = this.cy();
11505 var renderer = cy.renderer();
11506 var styleEnabled = cy.styleEnabled();
11507
11508 if (renderer && styleEnabled) {
11509 renderer.recalculateRenderedStyle(this, useCache);
11510 }
11511
11512 return this;
11513 },
11514 dirtyStyleCache: function dirtyStyleCache() {
11515 var cy = this.cy();
11516
11517 var dirty = function dirty(ele) {
11518 return ele._private.styleCache = null;
11519 };
11520
11521 if (cy.hasCompoundNodes()) {
11522 var eles;
11523 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11524 eles.merge(eles.connectedEdges());
11525 eles.forEach(dirty);
11526 } else {
11527 this.forEach(function (ele) {
11528 dirty(ele);
11529 ele.connectedEdges().forEach(dirty);
11530 });
11531 }
11532
11533 return this;
11534 },
11535 // fully updates (recalculates) the style for the elements
11536 updateStyle: function updateStyle(notifyRenderer) {
11537 var cy = this._private.cy;
11538
11539 if (!cy.styleEnabled()) {
11540 return this;
11541 }
11542
11543 if (cy.batching()) {
11544 var bEles = cy._private.batchStyleEles;
11545 bEles.merge(this);
11546 return this; // chaining and exit early when batching
11547 }
11548
11549 var hasCompounds = cy.hasCompoundNodes();
11550 var updatedEles = this;
11551 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11552
11553 if (hasCompounds) {
11554 // then add everything up and down for compound selector checks
11555 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11556 } // let changedEles = style.apply( updatedEles );
11557
11558
11559 var changedEles = updatedEles;
11560
11561 if (notifyRenderer) {
11562 changedEles.emitAndNotify('style'); // let renderer know we changed style
11563 } else {
11564 changedEles.emit('style'); // just fire the event
11565 }
11566
11567 updatedEles.forEach(function (ele) {
11568 return ele._private.styleDirty = true;
11569 });
11570 return this; // chaining
11571 },
11572 // private: clears dirty flag and recalculates style
11573 cleanStyle: function cleanStyle() {
11574 var cy = this.cy();
11575
11576 if (!cy.styleEnabled()) {
11577 return;
11578 }
11579
11580 for (var i = 0; i < this.length; i++) {
11581 var ele = this[i];
11582
11583 if (ele._private.styleDirty) {
11584 // n.b. this flag should be set before apply() to avoid potential infinite recursion
11585 ele._private.styleDirty = false;
11586 cy.style().apply(ele);
11587 }
11588 }
11589 },
11590 // get the internal parsed style object for the specified property
11591 parsedStyle: function parsedStyle(property) {
11592 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11593 var ele = this[0];
11594 var cy = ele.cy();
11595
11596 if (!cy.styleEnabled()) {
11597 return;
11598 }
11599
11600 if (ele) {
11601 this.cleanStyle();
11602 var overriddenStyle = ele._private.style[property];
11603
11604 if (overriddenStyle != null) {
11605 return overriddenStyle;
11606 } else if (includeNonDefault) {
11607 return cy.style().getDefaultProperty(property);
11608 } else {
11609 return null;
11610 }
11611 }
11612 },
11613 numericStyle: function numericStyle(property) {
11614 var ele = this[0];
11615
11616 if (!ele.cy().styleEnabled()) {
11617 return;
11618 }
11619
11620 if (ele) {
11621 var pstyle = ele.pstyle(property);
11622 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11623 }
11624 },
11625 numericStyleUnits: function numericStyleUnits(property) {
11626 var ele = this[0];
11627
11628 if (!ele.cy().styleEnabled()) {
11629 return;
11630 }
11631
11632 if (ele) {
11633 return ele.pstyle(property).units;
11634 }
11635 },
11636 // get the specified css property as a rendered value (i.e. on-screen value)
11637 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11638 renderedStyle: function renderedStyle(property) {
11639 var cy = this.cy();
11640
11641 if (!cy.styleEnabled()) {
11642 return this;
11643 }
11644
11645 var ele = this[0];
11646
11647 if (ele) {
11648 return cy.style().getRenderedStyle(ele, property);
11649 }
11650 },
11651 // read the calculated css style of the element or override the style (via a bypass)
11652 style: function style(name, value) {
11653 var cy = this.cy();
11654
11655 if (!cy.styleEnabled()) {
11656 return this;
11657 }
11658
11659 var updateTransitions = false;
11660 var style = cy.style();
11661
11662 if (plainObject(name)) {
11663 // then extend the bypass
11664 var props = name;
11665 style.applyBypass(this, props, updateTransitions);
11666 this.emitAndNotify('style'); // let the renderer know we've updated style
11667 } else if (string(name)) {
11668 if (value === undefined) {
11669 // then get the property from the style
11670 var ele = this[0];
11671
11672 if (ele) {
11673 return style.getStylePropertyValue(ele, name);
11674 } else {
11675 // empty collection => can't get any value
11676 return;
11677 }
11678 } else {
11679 // then set the bypass with the property value
11680 style.applyBypass(this, name, value, updateTransitions);
11681 this.emitAndNotify('style'); // let the renderer know we've updated style
11682 }
11683 } else if (name === undefined) {
11684 var _ele = this[0];
11685
11686 if (_ele) {
11687 return style.getRawStyle(_ele);
11688 } else {
11689 // empty collection => can't get any value
11690 return;
11691 }
11692 }
11693
11694 return this; // chaining
11695 },
11696 removeStyle: function removeStyle(names) {
11697 var cy = this.cy();
11698
11699 if (!cy.styleEnabled()) {
11700 return this;
11701 }
11702
11703 var updateTransitions = false;
11704 var style = cy.style();
11705 var eles = this;
11706
11707 if (names === undefined) {
11708 for (var i = 0; i < eles.length; i++) {
11709 var ele = eles[i];
11710 style.removeAllBypasses(ele, updateTransitions);
11711 }
11712 } else {
11713 names = names.split(/\s+/);
11714
11715 for (var _i = 0; _i < eles.length; _i++) {
11716 var _ele2 = eles[_i];
11717 style.removeBypasses(_ele2, names, updateTransitions);
11718 }
11719 }
11720
11721 this.emitAndNotify('style'); // let the renderer know we've updated style
11722
11723 return this; // chaining
11724 },
11725 show: function show() {
11726 this.css('display', 'element');
11727 return this; // chaining
11728 },
11729 hide: function hide() {
11730 this.css('display', 'none');
11731 return this; // chaining
11732 },
11733 effectiveOpacity: function effectiveOpacity() {
11734 var cy = this.cy();
11735
11736 if (!cy.styleEnabled()) {
11737 return 1;
11738 }
11739
11740 var hasCompoundNodes = cy.hasCompoundNodes();
11741 var ele = this[0];
11742
11743 if (ele) {
11744 var _p = ele._private;
11745 var parentOpacity = ele.pstyle('opacity').value;
11746
11747 if (!hasCompoundNodes) {
11748 return parentOpacity;
11749 }
11750
11751 var parents = !_p.data.parent ? null : ele.parents();
11752
11753 if (parents) {
11754 for (var i = 0; i < parents.length; i++) {
11755 var parent = parents[i];
11756 var opacity = parent.pstyle('opacity').value;
11757 parentOpacity = opacity * parentOpacity;
11758 }
11759 }
11760
11761 return parentOpacity;
11762 }
11763 },
11764 transparent: function transparent() {
11765 var cy = this.cy();
11766
11767 if (!cy.styleEnabled()) {
11768 return false;
11769 }
11770
11771 var ele = this[0];
11772 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11773
11774 if (ele) {
11775 if (!hasCompoundNodes) {
11776 return ele.pstyle('opacity').value === 0;
11777 } else {
11778 return ele.effectiveOpacity() === 0;
11779 }
11780 }
11781 },
11782 backgrounding: function backgrounding() {
11783 var cy = this.cy();
11784
11785 if (!cy.styleEnabled()) {
11786 return false;
11787 }
11788
11789 var ele = this[0];
11790 return ele._private.backgrounding ? true : false;
11791 }
11792};
11793
11794function checkCompound(ele, parentOk) {
11795 var _p = ele._private;
11796 var parents = _p.data.parent ? ele.parents() : null;
11797
11798 if (parents) {
11799 for (var i = 0; i < parents.length; i++) {
11800 var parent = parents[i];
11801
11802 if (!parentOk(parent)) {
11803 return false;
11804 }
11805 }
11806 }
11807
11808 return true;
11809}
11810
11811function defineDerivedStateFunction(specs) {
11812 var ok = specs.ok;
11813 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11814 var parentOk = specs.parentOk || specs.ok;
11815 return function () {
11816 var cy = this.cy();
11817
11818 if (!cy.styleEnabled()) {
11819 return true;
11820 }
11821
11822 var ele = this[0];
11823 var hasCompoundNodes = cy.hasCompoundNodes();
11824
11825 if (ele) {
11826 var _p = ele._private;
11827
11828 if (!ok(ele)) {
11829 return false;
11830 }
11831
11832 if (ele.isNode()) {
11833 return !hasCompoundNodes || checkCompound(ele, parentOk);
11834 } else {
11835 var src = _p.source;
11836 var tgt = _p.target;
11837 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11838 }
11839 }
11840 };
11841}
11842
11843var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11844 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11845});
11846elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11847 ok: eleTakesUpSpace
11848}));
11849var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11850 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11851});
11852var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11853 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11854});
11855elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11856 ok: eleInteractive,
11857 parentOk: parentInteractive,
11858 edgeOkViaNode: eleTakesUpSpace
11859}));
11860
11861elesfn$r.noninteractive = function () {
11862 var ele = this[0];
11863
11864 if (ele) {
11865 return !ele.interactive();
11866 }
11867};
11868
11869var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11870 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11871});
11872var edgeVisibleViaNode = eleTakesUpSpace;
11873elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11874 ok: eleVisible,
11875 edgeOkViaNode: edgeVisibleViaNode
11876}));
11877
11878elesfn$r.hidden = function () {
11879 var ele = this[0];
11880
11881 if (ele) {
11882 return !ele.visible();
11883 }
11884};
11885
11886elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11887 if (!this.cy().styleEnabled()) {
11888 return false;
11889 }
11890
11891 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11892});
11893elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11894elesfn$r.renderedCss = elesfn$r.renderedStyle;
11895elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11896elesfn$r.pstyle = elesfn$r.parsedStyle;
11897
11898var elesfn$s = {};
11899
11900function defineSwitchFunction(params) {
11901 return function () {
11902 var args = arguments;
11903 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11904
11905 if (args.length === 2) {
11906 var data = args[0];
11907 var handler = args[1];
11908 this.on(params.event, data, handler);
11909 } // e.g. cy.nodes().select( handler )
11910 else if (args.length === 1 && fn(args[0])) {
11911 var _handler = args[0];
11912 this.on(params.event, _handler);
11913 } // e.g. cy.nodes().select()
11914 // e.g. (private) cy.nodes().select(['tapselect'])
11915 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11916 var addlEvents = args.length === 1 ? args[0] : null;
11917
11918 for (var i = 0; i < this.length; i++) {
11919 var ele = this[i];
11920 var able = !params.ableField || ele._private[params.ableField];
11921 var changed = ele._private[params.field] != params.value;
11922
11923 if (params.overrideAble) {
11924 var overrideAble = params.overrideAble(ele);
11925
11926 if (overrideAble !== undefined) {
11927 able = overrideAble;
11928
11929 if (!overrideAble) {
11930 return this;
11931 } // to save cycles assume not able for all on override
11932
11933 }
11934 }
11935
11936 if (able) {
11937 ele._private[params.field] = params.value;
11938
11939 if (changed) {
11940 changedEles.push(ele);
11941 }
11942 }
11943 }
11944
11945 var changedColl = this.spawn(changedEles);
11946 changedColl.updateStyle(); // change of state => possible change of style
11947
11948 changedColl.emit(params.event);
11949
11950 if (addlEvents) {
11951 changedColl.emit(addlEvents);
11952 }
11953 }
11954
11955 return this;
11956 };
11957}
11958
11959function defineSwitchSet(params) {
11960 elesfn$s[params.field] = function () {
11961 var ele = this[0];
11962
11963 if (ele) {
11964 if (params.overrideField) {
11965 var val = params.overrideField(ele);
11966
11967 if (val !== undefined) {
11968 return val;
11969 }
11970 }
11971
11972 return ele._private[params.field];
11973 }
11974 };
11975
11976 elesfn$s[params.on] = defineSwitchFunction({
11977 event: params.on,
11978 field: params.field,
11979 ableField: params.ableField,
11980 overrideAble: params.overrideAble,
11981 value: true
11982 });
11983 elesfn$s[params.off] = defineSwitchFunction({
11984 event: params.off,
11985 field: params.field,
11986 ableField: params.ableField,
11987 overrideAble: params.overrideAble,
11988 value: false
11989 });
11990}
11991
11992defineSwitchSet({
11993 field: 'locked',
11994 overrideField: function overrideField(ele) {
11995 return ele.cy().autolock() ? true : undefined;
11996 },
11997 on: 'lock',
11998 off: 'unlock'
11999});
12000defineSwitchSet({
12001 field: 'grabbable',
12002 overrideField: function overrideField(ele) {
12003 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12004 },
12005 on: 'grabify',
12006 off: 'ungrabify'
12007});
12008defineSwitchSet({
12009 field: 'selected',
12010 ableField: 'selectable',
12011 overrideAble: function overrideAble(ele) {
12012 return ele.cy().autounselectify() ? false : undefined;
12013 },
12014 on: 'select',
12015 off: 'unselect'
12016});
12017defineSwitchSet({
12018 field: 'selectable',
12019 overrideField: function overrideField(ele) {
12020 return ele.cy().autounselectify() ? false : undefined;
12021 },
12022 on: 'selectify',
12023 off: 'unselectify'
12024});
12025elesfn$s.deselect = elesfn$s.unselect;
12026
12027elesfn$s.grabbed = function () {
12028 var ele = this[0];
12029
12030 if (ele) {
12031 return ele._private.grabbed;
12032 }
12033};
12034
12035defineSwitchSet({
12036 field: 'active',
12037 on: 'activate',
12038 off: 'unactivate'
12039});
12040defineSwitchSet({
12041 field: 'pannable',
12042 on: 'panify',
12043 off: 'unpanify'
12044});
12045
12046elesfn$s.inactive = function () {
12047 var ele = this[0];
12048
12049 if (ele) {
12050 return !ele._private.active;
12051 }
12052};
12053
12054var elesfn$t = {}; // DAG functions
12055////////////////
12056
12057var defineDagExtremity = function defineDagExtremity(params) {
12058 return function dagExtremityImpl(selector) {
12059 var eles = this;
12060 var ret = [];
12061
12062 for (var i = 0; i < eles.length; i++) {
12063 var ele = eles[i];
12064
12065 if (!ele.isNode()) {
12066 continue;
12067 }
12068
12069 var disqualified = false;
12070 var edges = ele.connectedEdges();
12071
12072 for (var j = 0; j < edges.length; j++) {
12073 var edge = edges[j];
12074 var src = edge.source();
12075 var tgt = edge.target();
12076
12077 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12078 disqualified = true;
12079 break;
12080 }
12081 }
12082
12083 if (!disqualified) {
12084 ret.push(ele);
12085 }
12086 }
12087
12088 return this.spawn(ret, true).filter(selector);
12089 };
12090};
12091
12092var defineDagOneHop = function defineDagOneHop(params) {
12093 return function (selector) {
12094 var eles = this;
12095 var oEles = [];
12096
12097 for (var i = 0; i < eles.length; i++) {
12098 var ele = eles[i];
12099
12100 if (!ele.isNode()) {
12101 continue;
12102 }
12103
12104 var edges = ele.connectedEdges();
12105
12106 for (var j = 0; j < edges.length; j++) {
12107 var edge = edges[j];
12108 var src = edge.source();
12109 var tgt = edge.target();
12110
12111 if (params.outgoing && src === ele) {
12112 oEles.push(edge);
12113 oEles.push(tgt);
12114 } else if (params.incoming && tgt === ele) {
12115 oEles.push(edge);
12116 oEles.push(src);
12117 }
12118 }
12119 }
12120
12121 return this.spawn(oEles, true).filter(selector);
12122 };
12123};
12124
12125var defineDagAllHops = function defineDagAllHops(params) {
12126 return function (selector) {
12127 var eles = this;
12128 var sEles = [];
12129 var sElesIds = {};
12130
12131 for (;;) {
12132 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12133
12134 if (next.length === 0) {
12135 break;
12136 } // done if none left
12137
12138
12139 var newNext = false;
12140
12141 for (var i = 0; i < next.length; i++) {
12142 var n = next[i];
12143 var nid = n.id();
12144
12145 if (!sElesIds[nid]) {
12146 sElesIds[nid] = true;
12147 sEles.push(n);
12148 newNext = true;
12149 }
12150 }
12151
12152 if (!newNext) {
12153 break;
12154 } // done if touched all outgoers already
12155
12156
12157 eles = next;
12158 }
12159
12160 return this.spawn(sEles, true).filter(selector);
12161 };
12162};
12163
12164elesfn$t.clearTraversalCache = function () {
12165 for (var i = 0; i < this.length; i++) {
12166 this[i]._private.traversalCache = null;
12167 }
12168};
12169
12170extend(elesfn$t, {
12171 // get the root nodes in the DAG
12172 roots: defineDagExtremity({
12173 noIncomingEdges: true
12174 }),
12175 // get the leaf nodes in the DAG
12176 leaves: defineDagExtremity({
12177 noOutgoingEdges: true
12178 }),
12179 // normally called children in graph theory
12180 // these nodes =edges=> outgoing nodes
12181 outgoers: cache(defineDagOneHop({
12182 outgoing: true
12183 }), 'outgoers'),
12184 // aka DAG descendants
12185 successors: defineDagAllHops({
12186 outgoing: true
12187 }),
12188 // normally called parents in graph theory
12189 // these nodes <=edges= incoming nodes
12190 incomers: cache(defineDagOneHop({
12191 incoming: true
12192 }), 'incomers'),
12193 // aka DAG ancestors
12194 predecessors: defineDagAllHops({
12195 incoming: true
12196 })
12197}); // Neighbourhood functions
12198//////////////////////////
12199
12200extend(elesfn$t, {
12201 neighborhood: cache(function (selector) {
12202 var elements = [];
12203 var nodes = this.nodes();
12204
12205 for (var i = 0; i < nodes.length; i++) {
12206 // for all nodes
12207 var node = nodes[i];
12208 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12209
12210 for (var j = 0; j < connectedEdges.length; j++) {
12211 var edge = connectedEdges[j];
12212 var src = edge.source();
12213 var tgt = edge.target();
12214 var otherNode = node === src ? tgt : src; // need check in case of loop
12215
12216 if (otherNode.length > 0) {
12217 elements.push(otherNode[0]); // add node 1 hop away
12218 } // add connected edge
12219
12220
12221 elements.push(edge[0]);
12222 }
12223 }
12224
12225 return this.spawn(elements, true).filter(selector);
12226 }, 'neighborhood'),
12227 closedNeighborhood: function closedNeighborhood(selector) {
12228 return this.neighborhood().add(this).filter(selector);
12229 },
12230 openNeighborhood: function openNeighborhood(selector) {
12231 return this.neighborhood(selector);
12232 }
12233}); // aliases
12234
12235elesfn$t.neighbourhood = elesfn$t.neighborhood;
12236elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12237elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12238/////////////////
12239
12240extend(elesfn$t, {
12241 source: cache(function sourceImpl(selector) {
12242 var ele = this[0];
12243 var src;
12244
12245 if (ele) {
12246 src = ele._private.source || ele.cy().collection();
12247 }
12248
12249 return src && selector ? src.filter(selector) : src;
12250 }, 'source'),
12251 target: cache(function targetImpl(selector) {
12252 var ele = this[0];
12253 var tgt;
12254
12255 if (ele) {
12256 tgt = ele._private.target || ele.cy().collection();
12257 }
12258
12259 return tgt && selector ? tgt.filter(selector) : tgt;
12260 }, 'target'),
12261 sources: defineSourceFunction({
12262 attr: 'source'
12263 }),
12264 targets: defineSourceFunction({
12265 attr: 'target'
12266 })
12267});
12268
12269function defineSourceFunction(params) {
12270 return function sourceImpl(selector) {
12271 var sources = [];
12272
12273 for (var i = 0; i < this.length; i++) {
12274 var ele = this[i];
12275 var src = ele._private[params.attr];
12276
12277 if (src) {
12278 sources.push(src);
12279 }
12280 }
12281
12282 return this.spawn(sources, true).filter(selector);
12283 };
12284}
12285
12286extend(elesfn$t, {
12287 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12288 edgesTo: cache(defineEdgesWithFunction({
12289 thisIsSrc: true
12290 }), 'edgesTo')
12291});
12292
12293function defineEdgesWithFunction(params) {
12294 return function edgesWithImpl(otherNodes) {
12295 var elements = [];
12296 var cy = this._private.cy;
12297 var p = params || {}; // get elements if a selector is specified
12298
12299 if (string(otherNodes)) {
12300 otherNodes = cy.$(otherNodes);
12301 }
12302
12303 for (var h = 0; h < otherNodes.length; h++) {
12304 var edges = otherNodes[h]._private.edges;
12305
12306 for (var i = 0; i < edges.length; i++) {
12307 var edge = edges[i];
12308 var edgeData = edge._private.data;
12309 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12310 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12311 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12312
12313 if (!edgeConnectsThisAndOther) {
12314 continue;
12315 }
12316
12317 if (p.thisIsSrc || p.thisIsTgt) {
12318 if (p.thisIsSrc && !thisToOther) {
12319 continue;
12320 }
12321
12322 if (p.thisIsTgt && !otherToThis) {
12323 continue;
12324 }
12325 }
12326
12327 elements.push(edge);
12328 }
12329 }
12330
12331 return this.spawn(elements, true);
12332 };
12333}
12334
12335extend(elesfn$t, {
12336 connectedEdges: cache(function (selector) {
12337 var retEles = [];
12338 var eles = this;
12339
12340 for (var i = 0; i < eles.length; i++) {
12341 var node = eles[i];
12342
12343 if (!node.isNode()) {
12344 continue;
12345 }
12346
12347 var edges = node._private.edges;
12348
12349 for (var j = 0; j < edges.length; j++) {
12350 var edge = edges[j];
12351 retEles.push(edge);
12352 }
12353 }
12354
12355 return this.spawn(retEles, true).filter(selector);
12356 }, 'connectedEdges'),
12357 connectedNodes: cache(function (selector) {
12358 var retEles = [];
12359 var eles = this;
12360
12361 for (var i = 0; i < eles.length; i++) {
12362 var edge = eles[i];
12363
12364 if (!edge.isEdge()) {
12365 continue;
12366 }
12367
12368 retEles.push(edge.source()[0]);
12369 retEles.push(edge.target()[0]);
12370 }
12371
12372 return this.spawn(retEles, true).filter(selector);
12373 }, 'connectedNodes'),
12374 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12375 codirectedEdges: cache(defineParallelEdgesFunction({
12376 codirected: true
12377 }), 'codirectedEdges')
12378});
12379
12380function defineParallelEdgesFunction(params) {
12381 var defaults = {
12382 codirected: false
12383 };
12384 params = extend({}, defaults, params);
12385 return function parallelEdgesImpl(selector) {
12386 // micro-optimised for renderer
12387 var elements = [];
12388 var edges = this.edges();
12389 var p = params; // look at all the edges in the collection
12390
12391 for (var i = 0; i < edges.length; i++) {
12392 var edge1 = edges[i];
12393 var edge1_p = edge1._private;
12394 var src1 = edge1_p.source;
12395 var srcid1 = src1._private.data.id;
12396 var tgtid1 = edge1_p.data.target;
12397 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12398
12399 for (var j = 0; j < srcEdges1.length; j++) {
12400 var edge2 = srcEdges1[j];
12401 var edge2data = edge2._private.data;
12402 var tgtid2 = edge2data.target;
12403 var srcid2 = edge2data.source;
12404 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12405 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12406
12407 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12408 elements.push(edge2);
12409 }
12410 }
12411 }
12412
12413 return this.spawn(elements, true).filter(selector);
12414 };
12415} // Misc functions
12416/////////////////
12417
12418
12419extend(elesfn$t, {
12420 components: function components(root) {
12421 var self = this;
12422 var cy = self.cy();
12423 var visited = cy.collection();
12424 var unvisited = root == null ? self.nodes() : root.nodes();
12425 var components = [];
12426
12427 if (root != null && unvisited.empty()) {
12428 // root may contain only edges
12429 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12430 }
12431
12432 var visitInComponent = function visitInComponent(node, component) {
12433 visited.merge(node);
12434 unvisited.unmerge(node);
12435 component.merge(node);
12436 };
12437
12438 if (unvisited.empty()) {
12439 return self.spawn();
12440 }
12441
12442 var _loop = function _loop() {
12443 // each iteration yields a component
12444 var cmpt = cy.collection();
12445 components.push(cmpt);
12446 var root = unvisited[0];
12447 visitInComponent(root, cmpt);
12448 self.bfs({
12449 directed: false,
12450 roots: root,
12451 visit: function visit(v) {
12452 return visitInComponent(v, cmpt);
12453 }
12454 });
12455 cmpt.forEach(function (node) {
12456 node.connectedEdges().forEach(function (e) {
12457 // connectedEdges() usually cached
12458 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12459 // has() is cheap
12460 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12461 }
12462 });
12463 });
12464 };
12465
12466 do {
12467 _loop();
12468 } while (unvisited.length > 0);
12469
12470 return components;
12471 },
12472 component: function component() {
12473 var ele = this[0];
12474 return ele.cy().mutableElements().components(ele)[0];
12475 }
12476});
12477elesfn$t.componentsOf = elesfn$t.components;
12478
12479var Collection = function Collection(cy, elements) {
12480 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12481
12482 if (cy === undefined) {
12483 error('A collection must have a reference to the core');
12484 return;
12485 }
12486
12487 var map = new Map$1();
12488 var createdElements = false;
12489
12490 if (!elements) {
12491 elements = [];
12492 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12493 createdElements = true; // make elements from json and restore all at once later
12494
12495 var eles = [];
12496 var elesIds = new Set$1();
12497
12498 for (var i = 0, l = elements.length; i < l; i++) {
12499 var json = elements[i];
12500
12501 if (json.data == null) {
12502 json.data = {};
12503 }
12504
12505 var _data = json.data; // make sure newly created elements have valid ids
12506
12507 if (_data.id == null) {
12508 _data.id = uuid();
12509 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12510 continue; // can't create element if prior id already exists
12511 }
12512
12513 var ele = new Element(cy, json, false);
12514 eles.push(ele);
12515 elesIds.add(_data.id);
12516 }
12517
12518 elements = eles;
12519 }
12520
12521 this.length = 0;
12522
12523 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12524 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12525
12526 if (element$1 == null) {
12527 continue;
12528 }
12529
12530 var id = element$1._private.data.id;
12531
12532 if (!unique || !map.has(id)) {
12533 if (unique) {
12534 map.set(id, {
12535 index: this.length,
12536 ele: element$1
12537 });
12538 }
12539
12540 this[this.length] = element$1;
12541 this.length++;
12542 }
12543 }
12544
12545 this._private = {
12546 eles: this,
12547 cy: cy,
12548
12549 get map() {
12550 if (this.lazyMap == null) {
12551 this.rebuildMap();
12552 }
12553
12554 return this.lazyMap;
12555 },
12556
12557 set map(m) {
12558 this.lazyMap = m;
12559 },
12560
12561 rebuildMap: function rebuildMap() {
12562 var m = this.lazyMap = new Map$1();
12563 var eles = this.eles;
12564
12565 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12566 var _ele = eles[_i2];
12567 m.set(_ele.id(), {
12568 index: _i2,
12569 ele: _ele
12570 });
12571 }
12572 }
12573 };
12574
12575 if (unique) {
12576 this._private.map = map;
12577 } // restore the elements if we created them from json
12578
12579
12580 if (createdElements) {
12581 this.restore();
12582 }
12583}; // Functions
12584////////////////////////////////////////////////////////////////////////////////////////////////////
12585// keep the prototypes in sync (an element has the same functions as a collection)
12586// and use elefn and elesfn as shorthands to the prototypes
12587
12588
12589var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12590
12591elesfn$u.instanceString = function () {
12592 return 'collection';
12593};
12594
12595elesfn$u.spawn = function (eles, unique) {
12596 return new Collection(this.cy(), eles, unique);
12597};
12598
12599elesfn$u.spawnSelf = function () {
12600 return this.spawn(this);
12601};
12602
12603elesfn$u.cy = function () {
12604 return this._private.cy;
12605};
12606
12607elesfn$u.renderer = function () {
12608 return this._private.cy.renderer();
12609};
12610
12611elesfn$u.element = function () {
12612 return this[0];
12613};
12614
12615elesfn$u.collection = function () {
12616 if (collection(this)) {
12617 return this;
12618 } else {
12619 // an element
12620 return new Collection(this._private.cy, [this]);
12621 }
12622};
12623
12624elesfn$u.unique = function () {
12625 return new Collection(this._private.cy, this, true);
12626};
12627
12628elesfn$u.hasElementWithId = function (id) {
12629 id = '' + id; // id must be string
12630
12631 return this._private.map.has(id);
12632};
12633
12634elesfn$u.getElementById = function (id) {
12635 id = '' + id; // id must be string
12636
12637 var cy = this._private.cy;
12638
12639 var entry = this._private.map.get(id);
12640
12641 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12642};
12643
12644elesfn$u.$id = elesfn$u.getElementById;
12645
12646elesfn$u.poolIndex = function () {
12647 var cy = this._private.cy;
12648 var eles = cy._private.elements;
12649 var id = this[0]._private.data.id;
12650 return eles._private.map.get(id).index;
12651};
12652
12653elesfn$u.indexOf = function (ele) {
12654 var id = ele[0]._private.data.id;
12655 return this._private.map.get(id).index;
12656};
12657
12658elesfn$u.indexOfId = function (id) {
12659 id = '' + id; // id must be string
12660
12661 return this._private.map.get(id).index;
12662};
12663
12664elesfn$u.json = function (obj) {
12665 var ele = this.element();
12666 var cy = this.cy();
12667
12668 if (ele == null && obj) {
12669 return this;
12670 } // can't set to no eles
12671
12672
12673 if (ele == null) {
12674 return undefined;
12675 } // can't get from no eles
12676
12677
12678 var p = ele._private;
12679
12680 if (plainObject(obj)) {
12681 // set
12682 cy.startBatch();
12683
12684 if (obj.data) {
12685 ele.data(obj.data);
12686 var _data2 = p.data;
12687
12688 if (ele.isEdge()) {
12689 // source and target are immutable via data()
12690 var move = false;
12691 var spec = {};
12692 var src = obj.data.source;
12693 var tgt = obj.data.target;
12694
12695 if (src != null && src != _data2.source) {
12696 spec.source = '' + src; // id must be string
12697
12698 move = true;
12699 }
12700
12701 if (tgt != null && tgt != _data2.target) {
12702 spec.target = '' + tgt; // id must be string
12703
12704 move = true;
12705 }
12706
12707 if (move) {
12708 ele = ele.move(spec);
12709 }
12710 } else {
12711 // parent is immutable via data()
12712 var newParentValSpecd = 'parent' in obj.data;
12713 var parent = obj.data.parent;
12714
12715 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12716 if (parent === undefined) {
12717 // can't set undefined imperatively, so use null
12718 parent = null;
12719 }
12720
12721 if (parent != null) {
12722 parent = '' + parent; // id must be string
12723 }
12724
12725 ele = ele.move({
12726 parent: parent
12727 });
12728 }
12729 }
12730 }
12731
12732 if (obj.position) {
12733 ele.position(obj.position);
12734 } // ignore group -- immutable
12735
12736
12737 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12738 var obj_k = obj[k];
12739
12740 if (obj_k != null && obj_k !== p[k]) {
12741 if (obj_k) {
12742 ele[trueFnName]();
12743 } else {
12744 ele[falseFnName]();
12745 }
12746 }
12747 };
12748
12749 checkSwitch('removed', 'remove', 'restore');
12750 checkSwitch('selected', 'select', 'unselect');
12751 checkSwitch('selectable', 'selectify', 'unselectify');
12752 checkSwitch('locked', 'lock', 'unlock');
12753 checkSwitch('grabbable', 'grabify', 'ungrabify');
12754 checkSwitch('pannable', 'panify', 'unpanify');
12755
12756 if (obj.classes != null) {
12757 ele.classes(obj.classes);
12758 }
12759
12760 cy.endBatch();
12761 return this;
12762 } else if (obj === undefined) {
12763 // get
12764 var json = {
12765 data: copy(p.data),
12766 position: copy(p.position),
12767 group: p.group,
12768 removed: p.removed,
12769 selected: p.selected,
12770 selectable: p.selectable,
12771 locked: p.locked,
12772 grabbable: p.grabbable,
12773 pannable: p.pannable,
12774 classes: null
12775 };
12776 json.classes = '';
12777 var i = 0;
12778 p.classes.forEach(function (cls) {
12779 return json.classes += i++ === 0 ? cls : ' ' + cls;
12780 });
12781 return json;
12782 }
12783};
12784
12785elesfn$u.jsons = function () {
12786 var jsons = [];
12787
12788 for (var i = 0; i < this.length; i++) {
12789 var ele = this[i];
12790 var json = ele.json();
12791 jsons.push(json);
12792 }
12793
12794 return jsons;
12795};
12796
12797elesfn$u.clone = function () {
12798 var cy = this.cy();
12799 var elesArr = [];
12800
12801 for (var i = 0; i < this.length; i++) {
12802 var ele = this[i];
12803 var json = ele.json();
12804 var clone = new Element(cy, json, false); // NB no restore
12805
12806 elesArr.push(clone);
12807 }
12808
12809 return new Collection(cy, elesArr);
12810};
12811
12812elesfn$u.copy = elesfn$u.clone;
12813
12814elesfn$u.restore = function () {
12815 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12816 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12817 var self = this;
12818 var cy = self.cy();
12819 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12820 // restore the nodes first
12821
12822 var nodes = [];
12823 var edges = [];
12824 var elements;
12825
12826 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12827 var ele = self[_i3];
12828
12829 if (addToPool && !ele.removed()) {
12830 // don't need to handle this ele
12831 continue;
12832 } // keep nodes first in the array and edges after
12833
12834
12835 if (ele.isNode()) {
12836 // put to front of array if node
12837 nodes.push(ele);
12838 } else {
12839 // put to end of array if edge
12840 edges.push(ele);
12841 }
12842 }
12843
12844 elements = nodes.concat(edges);
12845 var i;
12846
12847 var removeFromElements = function removeFromElements() {
12848 elements.splice(i, 1);
12849 i--;
12850 }; // now, restore each element
12851
12852
12853 for (i = 0; i < elements.length; i++) {
12854 var _ele2 = elements[i];
12855 var _private = _ele2._private;
12856 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12857
12858 _ele2.clearTraversalCache(); // set id and validate
12859
12860
12861 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12862 _data3.id = uuid();
12863 } else if (number(_data3.id)) {
12864 _data3.id = '' + _data3.id; // now it's a string
12865 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12866 error('Can not create element with invalid string ID `' + _data3.id + '`'); // can't create element if it has empty string as id or non-string id
12867
12868 removeFromElements();
12869 continue;
12870 } else if (cy.hasElementWithId(_data3.id)) {
12871 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12872
12873 removeFromElements();
12874 continue;
12875 }
12876
12877 var id = _data3.id; // id is finalised, now let's keep a ref
12878
12879 if (_ele2.isNode()) {
12880 // extra checks for nodes
12881 var pos = _private.position; // make sure the nodes have a defined position
12882
12883 if (pos.x == null) {
12884 pos.x = 0;
12885 }
12886
12887 if (pos.y == null) {
12888 pos.y = 0;
12889 }
12890 }
12891
12892 if (_ele2.isEdge()) {
12893 // extra checks for edges
12894 var edge = _ele2;
12895 var fields = ['source', 'target'];
12896 var fieldsLength = fields.length;
12897 var badSourceOrTarget = false;
12898
12899 for (var j = 0; j < fieldsLength; j++) {
12900 var field = fields[j];
12901 var val = _data3[field];
12902
12903 if (number(val)) {
12904 val = _data3[field] = '' + _data3[field]; // now string
12905 }
12906
12907 if (val == null || val === '') {
12908 // can't create if source or target is not defined properly
12909 error('Can not create edge `' + id + '` with unspecified ' + field);
12910 badSourceOrTarget = true;
12911 } else if (!cy.hasElementWithId(val)) {
12912 // can't create edge if one of its nodes doesn't exist
12913 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12914 badSourceOrTarget = true;
12915 }
12916 }
12917
12918 if (badSourceOrTarget) {
12919 removeFromElements();
12920 continue;
12921 } // can't create this
12922
12923
12924 var src = cy.getElementById(_data3.source);
12925 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
12926
12927 if (src.same(tgt)) {
12928 src._private.edges.push(edge);
12929 } else {
12930 src._private.edges.push(edge);
12931
12932 tgt._private.edges.push(edge);
12933 }
12934
12935 edge._private.source = src;
12936 edge._private.target = tgt;
12937 } // if is edge
12938 // create mock ids / indexes maps for element so it can be used like collections
12939
12940
12941 _private.map = new Map$1();
12942
12943 _private.map.set(id, {
12944 ele: _ele2,
12945 index: 0
12946 });
12947
12948 _private.removed = false;
12949
12950 if (addToPool) {
12951 cy.addToPool(_ele2);
12952 }
12953 } // for each element
12954 // do compound node sanity checks
12955
12956
12957 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
12958 // each node
12959 var node = nodes[_i4];
12960 var _data4 = node._private.data;
12961
12962 if (number(_data4.parent)) {
12963 // then automake string
12964 _data4.parent = '' + _data4.parent;
12965 }
12966
12967 var parentId = _data4.parent;
12968 var specifiedParent = parentId != null;
12969
12970 if (specifiedParent) {
12971 var parent = cy.getElementById(parentId);
12972
12973 if (parent.empty()) {
12974 // non-existant parent; just remove it
12975 _data4.parent = undefined;
12976 } else {
12977 var selfAsParent = false;
12978 var ancestor = parent;
12979
12980 while (!ancestor.empty()) {
12981 if (node.same(ancestor)) {
12982 // mark self as parent and remove from data
12983 selfAsParent = true;
12984 _data4.parent = undefined; // remove parent reference
12985 // exit or we loop forever
12986
12987 break;
12988 }
12989
12990 ancestor = ancestor.parent();
12991 }
12992
12993 if (!selfAsParent) {
12994 // connect with children
12995 parent[0]._private.children.push(node);
12996
12997 node._private.parent = parent[0]; // let the core know we have a compound graph
12998
12999 cy_p.hasCompoundNodes = true;
13000 }
13001 } // else
13002
13003 } // if specified parent
13004
13005 } // for each node
13006
13007
13008 if (elements.length > 0) {
13009 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13010
13011 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13012 var _ele3 = restored[_i5];
13013
13014 if (_ele3.isNode()) {
13015 continue;
13016 } // adding an edge invalidates the traversal caches for the parallel edges
13017
13018
13019 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13020
13021
13022 _ele3.source().clearTraversalCache();
13023
13024 _ele3.target().clearTraversalCache();
13025 }
13026
13027 var toUpdateStyle;
13028
13029 if (cy_p.hasCompoundNodes) {
13030 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13031 } else {
13032 toUpdateStyle = restored;
13033 }
13034
13035 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13036
13037 if (notifyRenderer) {
13038 restored.emitAndNotify('add');
13039 } else if (addToPool) {
13040 restored.emit('add');
13041 }
13042 }
13043
13044 return self; // chainability
13045};
13046
13047elesfn$u.removed = function () {
13048 var ele = this[0];
13049 return ele && ele._private.removed;
13050};
13051
13052elesfn$u.inside = function () {
13053 var ele = this[0];
13054 return ele && !ele._private.removed;
13055};
13056
13057elesfn$u.remove = function () {
13058 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13059 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13060 var self = this;
13061 var elesToRemove = [];
13062 var elesToRemoveIds = {};
13063 var cy = self._private.cy; // add connected edges
13064
13065 function addConnectedEdges(node) {
13066 var edges = node._private.edges;
13067
13068 for (var i = 0; i < edges.length; i++) {
13069 add(edges[i]);
13070 }
13071 } // add descendant nodes
13072
13073
13074 function addChildren(node) {
13075 var children = node._private.children;
13076
13077 for (var i = 0; i < children.length; i++) {
13078 add(children[i]);
13079 }
13080 }
13081
13082 function add(ele) {
13083 var alreadyAdded = elesToRemoveIds[ele.id()];
13084
13085 if (removeFromPool && ele.removed() || alreadyAdded) {
13086 return;
13087 } else {
13088 elesToRemoveIds[ele.id()] = true;
13089 }
13090
13091 if (ele.isNode()) {
13092 elesToRemove.push(ele); // nodes are removed last
13093
13094 addConnectedEdges(ele);
13095 addChildren(ele);
13096 } else {
13097 elesToRemove.unshift(ele); // edges are removed first
13098 }
13099 } // make the list of elements to remove
13100 // (may be removing more than specified due to connected edges etc)
13101
13102
13103 for (var i = 0, l = self.length; i < l; i++) {
13104 var ele = self[i];
13105 add(ele);
13106 }
13107
13108 function removeEdgeRef(node, edge) {
13109 var connectedEdges = node._private.edges;
13110 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13111
13112 node.clearTraversalCache();
13113 }
13114
13115 function removeParallelRef(pllEdge) {
13116 // removing an edge invalidates the traversal caches for the parallel edges
13117 pllEdge.clearTraversalCache();
13118 }
13119
13120 var alteredParents = [];
13121 alteredParents.ids = {};
13122
13123 function removeChildRef(parent, ele) {
13124 ele = ele[0];
13125 parent = parent[0];
13126 var children = parent._private.children;
13127 var pid = parent.id();
13128 removeFromArray(children, ele); // remove parent => child ref
13129
13130 ele._private.parent = null; // remove child => parent ref
13131
13132 if (!alteredParents.ids[pid]) {
13133 alteredParents.ids[pid] = true;
13134 alteredParents.push(parent);
13135 }
13136 }
13137
13138 self.dirtyCompoundBoundsCache();
13139
13140 if (removeFromPool) {
13141 cy.removeFromPool(elesToRemove); // remove from core pool
13142 }
13143
13144 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13145 var _ele4 = elesToRemove[_i6];
13146
13147 if (_ele4.isEdge()) {
13148 // remove references to this edge in its connected nodes
13149 var src = _ele4.source()[0];
13150
13151 var tgt = _ele4.target()[0];
13152
13153 removeEdgeRef(src, _ele4);
13154 removeEdgeRef(tgt, _ele4);
13155
13156 var pllEdges = _ele4.parallelEdges();
13157
13158 for (var j = 0; j < pllEdges.length; j++) {
13159 var pllEdge = pllEdges[j];
13160 removeParallelRef(pllEdge);
13161
13162 if (pllEdge.isBundledBezier()) {
13163 pllEdge.dirtyBoundingBoxCache();
13164 }
13165 }
13166 } else {
13167 // remove reference to parent
13168 var parent = _ele4.parent();
13169
13170 if (parent.length !== 0) {
13171 removeChildRef(parent, _ele4);
13172 }
13173 }
13174
13175 if (removeFromPool) {
13176 // mark as removed
13177 _ele4._private.removed = true;
13178 }
13179 } // check to see if we have a compound graph or not
13180
13181
13182 var elesStillInside = cy._private.elements;
13183 cy._private.hasCompoundNodes = false;
13184
13185 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13186 var _ele5 = elesStillInside[_i7];
13187
13188 if (_ele5.isParent()) {
13189 cy._private.hasCompoundNodes = true;
13190 break;
13191 }
13192 }
13193
13194 var removedElements = new Collection(this.cy(), elesToRemove);
13195
13196 if (removedElements.size() > 0) {
13197 // must manually notify since trigger won't do this automatically once removed
13198 if (notifyRenderer) {
13199 removedElements.emitAndNotify('remove');
13200 } else if (removeFromPool) {
13201 removedElements.emit('remove');
13202 }
13203 } // the parents who were modified by the removal need their style updated
13204
13205
13206 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13207 var _ele6 = alteredParents[_i8];
13208
13209 if (!removeFromPool || !_ele6.removed()) {
13210 _ele6.updateStyle();
13211 }
13212 }
13213
13214 return removedElements;
13215};
13216
13217elesfn$u.move = function (struct) {
13218 var cy = this._private.cy;
13219 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13220 // (our calls to remove/restore do not remove from the graph or make events)
13221
13222 var notifyRenderer = false;
13223 var modifyPool = false;
13224
13225 var toString = function toString(id) {
13226 return id == null ? id : '' + id;
13227 }; // id must be string
13228
13229
13230 if (struct.source !== undefined || struct.target !== undefined) {
13231 var srcId = toString(struct.source);
13232 var tgtId = toString(struct.target);
13233 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13234 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13235
13236 if (srcExists || tgtExists) {
13237 cy.batch(function () {
13238 // avoid duplicate style updates
13239 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13240
13241 eles.emitAndNotify('moveout');
13242
13243 for (var i = 0; i < eles.length; i++) {
13244 var ele = eles[i];
13245 var _data5 = ele._private.data;
13246
13247 if (ele.isEdge()) {
13248 if (srcExists) {
13249 _data5.source = srcId;
13250 }
13251
13252 if (tgtExists) {
13253 _data5.target = tgtId;
13254 }
13255 }
13256 }
13257
13258 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13259 });
13260 eles.emitAndNotify('move');
13261 }
13262 } else if (struct.parent !== undefined) {
13263 // move node to new parent
13264 var parentId = toString(struct.parent);
13265 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13266
13267 if (parentExists) {
13268 var pidToAssign = parentId === null ? undefined : parentId;
13269 cy.batch(function () {
13270 // avoid duplicate style updates
13271 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13272
13273 updated.emitAndNotify('moveout');
13274
13275 for (var i = 0; i < eles.length; i++) {
13276 var ele = eles[i];
13277 var _data6 = ele._private.data;
13278
13279 if (ele.isNode()) {
13280 _data6.parent = pidToAssign;
13281 }
13282 }
13283
13284 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13285 });
13286 eles.emitAndNotify('move');
13287 }
13288 }
13289
13290 return this;
13291};
13292
13293[elesfn$c, elesfn$d, elesfn$e, elesfn$f, elesfn$g, data$1, elesfn$i, dimensions, elesfn$m, elesfn$n, elesfn$o, elesfn$p, elesfn$q, elesfn$r, elesfn$s, elesfn$t].forEach(function (props) {
13294 extend(elesfn$u, props);
13295});
13296
13297var corefn = {
13298 add: function add(opts) {
13299 var elements;
13300 var cy = this; // add the elements
13301
13302 if (elementOrCollection(opts)) {
13303 var eles = opts;
13304
13305 if (eles._private.cy === cy) {
13306 // same instance => just restore
13307 elements = eles.restore();
13308 } else {
13309 // otherwise, copy from json
13310 var jsons = [];
13311
13312 for (var i = 0; i < eles.length; i++) {
13313 var ele = eles[i];
13314 jsons.push(ele.json());
13315 }
13316
13317 elements = new Collection(cy, jsons);
13318 }
13319 } // specify an array of options
13320 else if (array(opts)) {
13321 var _jsons = opts;
13322 elements = new Collection(cy, _jsons);
13323 } // specify via opts.nodes and opts.edges
13324 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13325 var elesByGroup = opts;
13326 var _jsons2 = [];
13327 var grs = ['nodes', 'edges'];
13328
13329 for (var _i = 0, il = grs.length; _i < il; _i++) {
13330 var group = grs[_i];
13331 var elesArray = elesByGroup[group];
13332
13333 if (array(elesArray)) {
13334 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13335 var json = extend({
13336 group: group
13337 }, elesArray[j]);
13338
13339 _jsons2.push(json);
13340 }
13341 }
13342 }
13343
13344 elements = new Collection(cy, _jsons2);
13345 } // specify options for one element
13346 else {
13347 var _json = opts;
13348 elements = new Element(cy, _json).collection();
13349 }
13350
13351 return elements;
13352 },
13353 remove: function remove(collection) {
13354 if (elementOrCollection(collection)) ; else if (string(collection)) {
13355 var selector = collection;
13356 collection = this.$(selector);
13357 }
13358
13359 return collection.remove();
13360 }
13361};
13362
13363/* global Float32Array */
13364
13365/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13366function generateCubicBezier(mX1, mY1, mX2, mY2) {
13367 var NEWTON_ITERATIONS = 4,
13368 NEWTON_MIN_SLOPE = 0.001,
13369 SUBDIVISION_PRECISION = 0.0000001,
13370 SUBDIVISION_MAX_ITERATIONS = 10,
13371 kSplineTableSize = 11,
13372 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13373 float32ArraySupported = typeof Float32Array !== 'undefined';
13374 /* Must contain four arguments. */
13375
13376 if (arguments.length !== 4) {
13377 return false;
13378 }
13379 /* Arguments must be numbers. */
13380
13381
13382 for (var i = 0; i < 4; ++i) {
13383 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13384 return false;
13385 }
13386 }
13387 /* X values must be in the [0, 1] range. */
13388
13389
13390 mX1 = Math.min(mX1, 1);
13391 mX2 = Math.min(mX2, 1);
13392 mX1 = Math.max(mX1, 0);
13393 mX2 = Math.max(mX2, 0);
13394 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13395
13396 function A(aA1, aA2) {
13397 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13398 }
13399
13400 function B(aA1, aA2) {
13401 return 3.0 * aA2 - 6.0 * aA1;
13402 }
13403
13404 function C(aA1) {
13405 return 3.0 * aA1;
13406 }
13407
13408 function calcBezier(aT, aA1, aA2) {
13409 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13410 }
13411
13412 function getSlope(aT, aA1, aA2) {
13413 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13414 }
13415
13416 function newtonRaphsonIterate(aX, aGuessT) {
13417 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13418 var currentSlope = getSlope(aGuessT, mX1, mX2);
13419
13420 if (currentSlope === 0.0) {
13421 return aGuessT;
13422 }
13423
13424 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13425 aGuessT -= currentX / currentSlope;
13426 }
13427
13428 return aGuessT;
13429 }
13430
13431 function calcSampleValues() {
13432 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13433 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13434 }
13435 }
13436
13437 function binarySubdivide(aX, aA, aB) {
13438 var currentX,
13439 currentT,
13440 i = 0;
13441
13442 do {
13443 currentT = aA + (aB - aA) / 2.0;
13444 currentX = calcBezier(currentT, mX1, mX2) - aX;
13445
13446 if (currentX > 0.0) {
13447 aB = currentT;
13448 } else {
13449 aA = currentT;
13450 }
13451 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13452
13453 return currentT;
13454 }
13455
13456 function getTForX(aX) {
13457 var intervalStart = 0.0,
13458 currentSample = 1,
13459 lastSample = kSplineTableSize - 1;
13460
13461 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13462 intervalStart += kSampleStepSize;
13463 }
13464
13465 --currentSample;
13466 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13467 guessForT = intervalStart + dist * kSampleStepSize,
13468 initialSlope = getSlope(guessForT, mX1, mX2);
13469
13470 if (initialSlope >= NEWTON_MIN_SLOPE) {
13471 return newtonRaphsonIterate(aX, guessForT);
13472 } else if (initialSlope === 0.0) {
13473 return guessForT;
13474 } else {
13475 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13476 }
13477 }
13478
13479 var _precomputed = false;
13480
13481 function precompute() {
13482 _precomputed = true;
13483
13484 if (mX1 !== mY1 || mX2 !== mY2) {
13485 calcSampleValues();
13486 }
13487 }
13488
13489 var f = function f(aX) {
13490 if (!_precomputed) {
13491 precompute();
13492 }
13493
13494 if (mX1 === mY1 && mX2 === mY2) {
13495 return aX;
13496 }
13497
13498 if (aX === 0) {
13499 return 0;
13500 }
13501
13502 if (aX === 1) {
13503 return 1;
13504 }
13505
13506 return calcBezier(getTForX(aX), mY1, mY2);
13507 };
13508
13509 f.getControlPoints = function () {
13510 return [{
13511 x: mX1,
13512 y: mY1
13513 }, {
13514 x: mX2,
13515 y: mY2
13516 }];
13517 };
13518
13519 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13520
13521 f.toString = function () {
13522 return str;
13523 };
13524
13525 return f;
13526}
13527
13528/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13529
13530/* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass
13531 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13532var generateSpringRK4 = function () {
13533 function springAccelerationForState(state) {
13534 return -state.tension * state.x - state.friction * state.v;
13535 }
13536
13537 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13538 var state = {
13539 x: initialState.x + derivative.dx * dt,
13540 v: initialState.v + derivative.dv * dt,
13541 tension: initialState.tension,
13542 friction: initialState.friction
13543 };
13544 return {
13545 dx: state.v,
13546 dv: springAccelerationForState(state)
13547 };
13548 }
13549
13550 function springIntegrateState(state, dt) {
13551 var a = {
13552 dx: state.v,
13553 dv: springAccelerationForState(state)
13554 },
13555 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13556 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13557 d = springEvaluateStateWithDerivative(state, dt, c),
13558 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13559 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13560 state.x = state.x + dxdt * dt;
13561 state.v = state.v + dvdt * dt;
13562 return state;
13563 }
13564
13565 return function springRK4Factory(tension, friction, duration) {
13566 var initState = {
13567 x: -1,
13568 v: 0,
13569 tension: null,
13570 friction: null
13571 },
13572 path = [0],
13573 time_lapsed = 0,
13574 tolerance = 1 / 10000,
13575 DT = 16 / 1000,
13576 have_duration,
13577 dt,
13578 last_state;
13579 tension = parseFloat(tension) || 500;
13580 friction = parseFloat(friction) || 20;
13581 duration = duration || null;
13582 initState.tension = tension;
13583 initState.friction = friction;
13584 have_duration = duration !== null;
13585 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13586
13587 if (have_duration) {
13588 /* Run the simulation without a duration. */
13589 time_lapsed = springRK4Factory(tension, friction);
13590 /* Compute the adjusted time delta. */
13591
13592 dt = time_lapsed / duration * DT;
13593 } else {
13594 dt = DT;
13595 }
13596
13597 for (;;) {
13598 /* Next/step function .*/
13599 last_state = springIntegrateState(last_state || initState, dt);
13600 /* Store the position. */
13601
13602 path.push(1 + last_state.x);
13603 time_lapsed += 16;
13604 /* If the change threshold is reached, break. */
13605
13606 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13607 break;
13608 }
13609 }
13610 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13611 computed path and returns a snapshot of the position according to a given percentComplete. */
13612
13613
13614 return !have_duration ? time_lapsed : function (percentComplete) {
13615 return path[percentComplete * (path.length - 1) | 0];
13616 };
13617 };
13618}();
13619
13620var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13621 var bezier = generateCubicBezier(t1, p1, t2, p2);
13622 return function (start, end, percent) {
13623 return start + (end - start) * bezier(percent);
13624 };
13625};
13626
13627var easings = {
13628 'linear': function linear(start, end, percent) {
13629 return start + (end - start) * percent;
13630 },
13631 // default easings
13632 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13633 'ease-in': cubicBezier(0.42, 0, 1, 1),
13634 'ease-out': cubicBezier(0, 0, 0.58, 1),
13635 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13636 // sine
13637 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13638 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13639 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13640 // quad
13641 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13642 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13643 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13644 // cubic
13645 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13646 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13647 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13648 // quart
13649 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13650 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13651 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13652 // quint
13653 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13654 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13655 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13656 // expo
13657 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13658 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13659 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13660 // circ
13661 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13662 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13663 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13664 // user param easings...
13665 'spring': function spring(tension, friction, duration) {
13666 if (duration === 0) {
13667 // can't get a spring w/ duration 0
13668 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13669 }
13670
13671 var spring = generateSpringRK4(tension, friction, duration);
13672 return function (start, end, percent) {
13673 return start + (end - start) * spring(percent);
13674 };
13675 },
13676 'cubic-bezier': cubicBezier
13677};
13678
13679function getEasedValue(type, start, end, percent, easingFn) {
13680 if (percent === 1) {
13681 return end;
13682 }
13683
13684 if (start === end) {
13685 return end;
13686 }
13687
13688 var val = easingFn(start, end, percent);
13689
13690 if (type == null) {
13691 return val;
13692 }
13693
13694 if (type.roundValue || type.color) {
13695 val = Math.round(val);
13696 }
13697
13698 if (type.min !== undefined) {
13699 val = Math.max(val, type.min);
13700 }
13701
13702 if (type.max !== undefined) {
13703 val = Math.min(val, type.max);
13704 }
13705
13706 return val;
13707}
13708
13709function getValue(prop, spec) {
13710 if (prop.pfValue != null || prop.value != null) {
13711 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13712 return prop.pfValue;
13713 } else {
13714 return prop.value;
13715 }
13716 } else {
13717 return prop;
13718 }
13719}
13720
13721function ease(startProp, endProp, percent, easingFn, propSpec) {
13722 var type = propSpec != null ? propSpec.type : null;
13723
13724 if (percent < 0) {
13725 percent = 0;
13726 } else if (percent > 1) {
13727 percent = 1;
13728 }
13729
13730 var start = getValue(startProp, propSpec);
13731 var end = getValue(endProp, propSpec);
13732
13733 if (number(start) && number(end)) {
13734 return getEasedValue(type, start, end, percent, easingFn);
13735 } else if (array(start) && array(end)) {
13736 var easedArr = [];
13737
13738 for (var i = 0; i < end.length; i++) {
13739 var si = start[i];
13740 var ei = end[i];
13741
13742 if (si != null && ei != null) {
13743 var val = getEasedValue(type, si, ei, percent, easingFn);
13744 easedArr.push(val);
13745 } else {
13746 easedArr.push(ei);
13747 }
13748 }
13749
13750 return easedArr;
13751 }
13752
13753 return undefined;
13754}
13755
13756function step(self, ani, now, isCore) {
13757 var isEles = !isCore;
13758 var _p = self._private;
13759 var ani_p = ani._private;
13760 var pEasing = ani_p.easing;
13761 var startTime = ani_p.startTime;
13762 var cy = isCore ? self : self.cy();
13763 var style = cy.style();
13764
13765 if (!ani_p.easingImpl) {
13766 if (pEasing == null) {
13767 // use default
13768 ani_p.easingImpl = easings['linear'];
13769 } else {
13770 // then define w/ name
13771 var easingVals;
13772
13773 if (string(pEasing)) {
13774 var easingProp = style.parse('transition-timing-function', pEasing);
13775 easingVals = easingProp.value;
13776 } else {
13777 // then assume preparsed array
13778 easingVals = pEasing;
13779 }
13780
13781 var name, args;
13782
13783 if (string(easingVals)) {
13784 name = easingVals;
13785 args = [];
13786 } else {
13787 name = easingVals[1];
13788 args = easingVals.slice(2).map(function (n) {
13789 return +n;
13790 });
13791 }
13792
13793 if (args.length > 0) {
13794 // create with args
13795 if (name === 'spring') {
13796 args.push(ani_p.duration); // need duration to generate spring
13797 }
13798
13799 ani_p.easingImpl = easings[name].apply(null, args);
13800 } else {
13801 // static impl by name
13802 ani_p.easingImpl = easings[name];
13803 }
13804 }
13805 }
13806
13807 var easing = ani_p.easingImpl;
13808 var percent;
13809
13810 if (ani_p.duration === 0) {
13811 percent = 1;
13812 } else {
13813 percent = (now - startTime) / ani_p.duration;
13814 }
13815
13816 if (ani_p.applying) {
13817 percent = ani_p.progress;
13818 }
13819
13820 if (percent < 0) {
13821 percent = 0;
13822 } else if (percent > 1) {
13823 percent = 1;
13824 }
13825
13826 if (ani_p.delay == null) {
13827 // then update
13828 var startPos = ani_p.startPosition;
13829 var endPos = ani_p.position;
13830
13831 if (endPos && isEles && !self.locked()) {
13832 var newPos = {};
13833
13834 if (valid(startPos.x, endPos.x)) {
13835 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13836 }
13837
13838 if (valid(startPos.y, endPos.y)) {
13839 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13840 }
13841
13842 self.position(newPos);
13843 }
13844
13845 var startPan = ani_p.startPan;
13846 var endPan = ani_p.pan;
13847 var pan = _p.pan;
13848 var animatingPan = endPan != null && isCore;
13849
13850 if (animatingPan) {
13851 if (valid(startPan.x, endPan.x)) {
13852 pan.x = ease(startPan.x, endPan.x, percent, easing);
13853 }
13854
13855 if (valid(startPan.y, endPan.y)) {
13856 pan.y = ease(startPan.y, endPan.y, percent, easing);
13857 }
13858
13859 self.emit('pan');
13860 }
13861
13862 var startZoom = ani_p.startZoom;
13863 var endZoom = ani_p.zoom;
13864 var animatingZoom = endZoom != null && isCore;
13865
13866 if (animatingZoom) {
13867 if (valid(startZoom, endZoom)) {
13868 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13869 }
13870
13871 self.emit('zoom');
13872 }
13873
13874 if (animatingPan || animatingZoom) {
13875 self.emit('viewport');
13876 }
13877
13878 var props = ani_p.style;
13879
13880 if (props && props.length > 0 && isEles) {
13881 for (var i = 0; i < props.length; i++) {
13882 var prop = props[i];
13883 var _name = prop.name;
13884 var end = prop;
13885 var start = ani_p.startStyle[_name];
13886 var propSpec = style.properties[start.name];
13887 var easedVal = ease(start, end, percent, easing, propSpec);
13888 style.overrideBypass(self, _name, easedVal);
13889 } // for props
13890
13891
13892 self.emit('style');
13893 } // if
13894
13895 }
13896
13897 ani_p.progress = percent;
13898 return percent;
13899}
13900
13901function valid(start, end) {
13902 if (start == null || end == null) {
13903 return false;
13904 }
13905
13906 if (number(start) && number(end)) {
13907 return true;
13908 } else if (start && end) {
13909 return true;
13910 }
13911
13912 return false;
13913}
13914
13915function startAnimation(self, ani, now, isCore) {
13916 var ani_p = ani._private;
13917 ani_p.started = true;
13918 ani_p.startTime = now - ani_p.progress * ani_p.duration;
13919}
13920
13921function stepAll(now, cy) {
13922 var eles = cy._private.aniEles;
13923 var doneEles = [];
13924
13925 function stepOne(ele, isCore) {
13926 var _p = ele._private;
13927 var current = _p.animation.current;
13928 var queue = _p.animation.queue;
13929 var ranAnis = false; // if nothing currently animating, get something from the queue
13930
13931 if (current.length === 0) {
13932 var next = queue.shift();
13933
13934 if (next) {
13935 current.push(next);
13936 }
13937 }
13938
13939 var callbacks = function callbacks(_callbacks) {
13940 for (var j = _callbacks.length - 1; j >= 0; j--) {
13941 var cb = _callbacks[j];
13942 cb();
13943 }
13944
13945 _callbacks.splice(0, _callbacks.length);
13946 }; // step and remove if done
13947
13948
13949 for (var i = current.length - 1; i >= 0; i--) {
13950 var ani = current[i];
13951 var ani_p = ani._private;
13952
13953 if (ani_p.stopped) {
13954 current.splice(i, 1);
13955 ani_p.hooked = false;
13956 ani_p.playing = false;
13957 ani_p.started = false;
13958 callbacks(ani_p.frames);
13959 continue;
13960 }
13961
13962 if (!ani_p.playing && !ani_p.applying) {
13963 continue;
13964 } // an apply() while playing shouldn't do anything
13965
13966
13967 if (ani_p.playing && ani_p.applying) {
13968 ani_p.applying = false;
13969 }
13970
13971 if (!ani_p.started) {
13972 startAnimation(ele, ani, now);
13973 }
13974
13975 step(ele, ani, now, isCore);
13976
13977 if (ani_p.applying) {
13978 ani_p.applying = false;
13979 }
13980
13981 callbacks(ani_p.frames);
13982
13983 if (ani_p.step != null) {
13984 ani_p.step(now);
13985 }
13986
13987 if (ani.completed()) {
13988 current.splice(i, 1);
13989 ani_p.hooked = false;
13990 ani_p.playing = false;
13991 ani_p.started = false;
13992 callbacks(ani_p.completes);
13993 }
13994
13995 ranAnis = true;
13996 }
13997
13998 if (!isCore && current.length === 0 && queue.length === 0) {
13999 doneEles.push(ele);
14000 }
14001
14002 return ranAnis;
14003 } // stepElement
14004 // handle all eles
14005
14006
14007 var ranEleAni = false;
14008
14009 for (var e = 0; e < eles.length; e++) {
14010 var ele = eles[e];
14011 var handledThisEle = stepOne(ele);
14012 ranEleAni = ranEleAni || handledThisEle;
14013 } // each element
14014
14015
14016 var ranCoreAni = stepOne(cy, true); // notify renderer
14017
14018 if (ranEleAni || ranCoreAni) {
14019 if (eles.length > 0) {
14020 cy.notify('draw', eles);
14021 } else {
14022 cy.notify('draw');
14023 }
14024 } // remove elements from list of currently animating if its queues are empty
14025
14026
14027 eles.unmerge(doneEles);
14028 cy.emit('step');
14029} // stepAll
14030
14031var corefn$1 = {
14032 // pull in animation functions
14033 animate: define$3.animate(),
14034 animation: define$3.animation(),
14035 animated: define$3.animated(),
14036 clearQueue: define$3.clearQueue(),
14037 delay: define$3.delay(),
14038 delayAnimation: define$3.delayAnimation(),
14039 stop: define$3.stop(),
14040 addToAnimationPool: function addToAnimationPool(eles) {
14041 var cy = this;
14042
14043 if (!cy.styleEnabled()) {
14044 return;
14045 } // save cycles when no style used
14046
14047
14048 cy._private.aniEles.merge(eles);
14049 },
14050 stopAnimationLoop: function stopAnimationLoop() {
14051 this._private.animationsRunning = false;
14052 },
14053 startAnimationLoop: function startAnimationLoop() {
14054 var cy = this;
14055 cy._private.animationsRunning = true;
14056
14057 if (!cy.styleEnabled()) {
14058 return;
14059 } // save cycles when no style used
14060 // NB the animation loop will exec in headless environments if style enabled
14061 // and explicit cy.destroy() is necessary to stop the loop
14062
14063
14064 function headlessStep() {
14065 if (!cy._private.animationsRunning) {
14066 return;
14067 }
14068
14069 requestAnimationFrame(function animationStep(now) {
14070 stepAll(now, cy);
14071 headlessStep();
14072 });
14073 }
14074
14075 var renderer = cy.renderer();
14076
14077 if (renderer && renderer.beforeRender) {
14078 // let the renderer schedule animations
14079 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14080 stepAll(now, cy);
14081 }, renderer.beforeRenderPriorities.animations);
14082 } else {
14083 // manage the animation loop ourselves
14084 headlessStep(); // first call
14085 }
14086 }
14087};
14088
14089var emitterOptions$1 = {
14090 qualifierCompare: function qualifierCompare(selector1, selector2) {
14091 if (selector1 == null || selector2 == null) {
14092 return selector1 == null && selector2 == null;
14093 } else {
14094 return selector1.sameText(selector2);
14095 }
14096 },
14097 eventMatches: function eventMatches(cy, listener, eventObj) {
14098 var selector = listener.qualifier;
14099
14100 if (selector != null) {
14101 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14102 }
14103
14104 return true;
14105 },
14106 addEventFields: function addEventFields(cy, evt) {
14107 evt.cy = cy;
14108 evt.target = cy;
14109 },
14110 callbackContext: function callbackContext(cy, listener, eventObj) {
14111 return listener.qualifier != null ? eventObj.target : cy;
14112 }
14113};
14114
14115var argSelector$1 = function argSelector(arg) {
14116 if (string(arg)) {
14117 return new Selector(arg);
14118 } else {
14119 return arg;
14120 }
14121};
14122
14123var elesfn$v = {
14124 createEmitter: function createEmitter() {
14125 var _p = this._private;
14126
14127 if (!_p.emitter) {
14128 _p.emitter = new Emitter(emitterOptions$1, this);
14129 }
14130
14131 return this;
14132 },
14133 emitter: function emitter() {
14134 return this._private.emitter;
14135 },
14136 on: function on(events, selector, callback) {
14137 this.emitter().on(events, argSelector$1(selector), callback);
14138 return this;
14139 },
14140 removeListener: function removeListener(events, selector, callback) {
14141 this.emitter().removeListener(events, argSelector$1(selector), callback);
14142 return this;
14143 },
14144 removeAllListeners: function removeAllListeners() {
14145 this.emitter().removeAllListeners();
14146 return this;
14147 },
14148 one: function one(events, selector, callback) {
14149 this.emitter().one(events, argSelector$1(selector), callback);
14150 return this;
14151 },
14152 once: function once(events, selector, callback) {
14153 this.emitter().one(events, argSelector$1(selector), callback);
14154 return this;
14155 },
14156 emit: function emit(events, extraParams) {
14157 this.emitter().emit(events, extraParams);
14158 return this;
14159 },
14160 emitAndNotify: function emitAndNotify(event, eles) {
14161 this.emit(event);
14162 this.notify(event, eles);
14163 return this;
14164 }
14165};
14166define$3.eventAliasesOn(elesfn$v);
14167
14168var corefn$2 = {
14169 png: function png(options) {
14170 var renderer = this._private.renderer;
14171 options = options || {};
14172 return renderer.png(options);
14173 },
14174 jpg: function jpg(options) {
14175 var renderer = this._private.renderer;
14176 options = options || {};
14177 options.bg = options.bg || '#fff';
14178 return renderer.jpg(options);
14179 }
14180};
14181corefn$2.jpeg = corefn$2.jpg;
14182
14183var corefn$3 = {
14184 layout: function layout(options) {
14185 var cy = this;
14186
14187 if (options == null) {
14188 error('Layout options must be specified to make a layout');
14189 return;
14190 }
14191
14192 if (options.name == null) {
14193 error('A `name` must be specified to make a layout');
14194 return;
14195 }
14196
14197 var name = options.name;
14198 var Layout = cy.extension('layout', name);
14199
14200 if (Layout == null) {
14201 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14202 return;
14203 }
14204
14205 var eles;
14206
14207 if (string(options.eles)) {
14208 eles = cy.$(options.eles);
14209 } else {
14210 eles = options.eles != null ? options.eles : cy.$();
14211 }
14212
14213 var layout = new Layout(extend({}, options, {
14214 cy: cy,
14215 eles: eles
14216 }));
14217 return layout;
14218 }
14219};
14220corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14221
14222var corefn$4 = {
14223 notify: function notify(eventName, eventEles) {
14224 var _p = this._private;
14225
14226 if (this.batching()) {
14227 _p.batchNotifications = _p.batchNotifications || {};
14228 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14229
14230 if (eventEles != null) {
14231 eles.merge(eventEles);
14232 }
14233
14234 return; // notifications are disabled during batching
14235 }
14236
14237 if (!_p.notificationsEnabled) {
14238 return;
14239 } // exit on disabled
14240
14241
14242 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14243
14244 if (this.destroyed() || !renderer) {
14245 return;
14246 }
14247
14248 renderer.notify(eventName, eventEles);
14249 },
14250 notifications: function notifications(bool) {
14251 var p = this._private;
14252
14253 if (bool === undefined) {
14254 return p.notificationsEnabled;
14255 } else {
14256 p.notificationsEnabled = bool ? true : false;
14257 }
14258
14259 return this;
14260 },
14261 noNotifications: function noNotifications(callback) {
14262 this.notifications(false);
14263 callback();
14264 this.notifications(true);
14265 },
14266 batching: function batching() {
14267 return this._private.batchCount > 0;
14268 },
14269 startBatch: function startBatch() {
14270 var _p = this._private;
14271
14272 if (_p.batchCount == null) {
14273 _p.batchCount = 0;
14274 }
14275
14276 if (_p.batchCount === 0) {
14277 _p.batchStyleEles = this.collection();
14278 _p.batchNotifications = {};
14279 }
14280
14281 _p.batchCount++;
14282 return this;
14283 },
14284 endBatch: function endBatch() {
14285 var _p = this._private;
14286
14287 if (_p.batchCount === 0) {
14288 return this;
14289 }
14290
14291 _p.batchCount--;
14292
14293 if (_p.batchCount === 0) {
14294 // update style for dirty eles
14295 _p.batchStyleEles.updateStyle();
14296
14297 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14298
14299 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14300 var eles = _p.batchNotifications[eventName];
14301
14302 if (eles.empty()) {
14303 renderer.notify(eventName);
14304 } else {
14305 renderer.notify(eventName, eles);
14306 }
14307 });
14308 }
14309
14310 return this;
14311 },
14312 batch: function batch(callback) {
14313 this.startBatch();
14314 callback();
14315 this.endBatch();
14316 return this;
14317 },
14318 // for backwards compatibility
14319 batchData: function batchData(map) {
14320 var cy = this;
14321 return this.batch(function () {
14322 var ids = Object.keys(map);
14323
14324 for (var i = 0; i < ids.length; i++) {
14325 var id = ids[i];
14326 var data = map[id];
14327 var ele = cy.getElementById(id);
14328 ele.data(data);
14329 }
14330 });
14331 }
14332};
14333
14334var rendererDefaults = defaults({
14335 hideEdgesOnViewport: false,
14336 textureOnViewport: false,
14337 motionBlur: false,
14338 motionBlurOpacity: 0.05,
14339 pixelRatio: undefined,
14340 desktopTapThreshold: 4,
14341 touchTapThreshold: 8,
14342 wheelSensitivity: 1,
14343 debug: false,
14344 showFps: false
14345});
14346var corefn$5 = {
14347 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14348 var r = this._private.renderer;
14349 r.renderTo(context, zoom, pan, pxRatio);
14350 return this;
14351 },
14352 renderer: function renderer() {
14353 return this._private.renderer;
14354 },
14355 forceRender: function forceRender() {
14356 this.notify('draw');
14357 return this;
14358 },
14359 resize: function resize() {
14360 this.invalidateSize();
14361 this.emitAndNotify('resize');
14362 return this;
14363 },
14364 initRenderer: function initRenderer(options) {
14365 var cy = this;
14366 var RendererProto = cy.extension('renderer', options.name);
14367
14368 if (RendererProto == null) {
14369 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14370 return;
14371 }
14372
14373 if (options.wheelSensitivity !== undefined) {
14374 warn("You have set a custom wheel sensitivity. This will make your app zoom unnaturally when using mainstream mice. You should change this value from the default only if you can guarantee that all your users will use the same hardware and OS configuration as your current machine.");
14375 }
14376
14377 var rOpts = rendererDefaults(options);
14378 rOpts.cy = cy;
14379 cy._private.renderer = new RendererProto(rOpts);
14380 this.notify('init');
14381 },
14382 destroyRenderer: function destroyRenderer() {
14383 var cy = this;
14384 cy.notify('destroy'); // destroy the renderer
14385
14386 var domEle = cy.container();
14387
14388 if (domEle) {
14389 domEle._cyreg = null;
14390
14391 while (domEle.childNodes.length > 0) {
14392 domEle.removeChild(domEle.childNodes[0]);
14393 }
14394 }
14395
14396 cy._private.renderer = null; // to be extra safe, remove the ref
14397
14398 cy.mutableElements().forEach(function (ele) {
14399 var _p = ele._private;
14400 _p.rscratch = {};
14401 _p.rstyle = {};
14402 _p.animation.current = [];
14403 _p.animation.queue = [];
14404 });
14405 },
14406 onRender: function onRender(fn) {
14407 return this.on('render', fn);
14408 },
14409 offRender: function offRender(fn) {
14410 return this.off('render', fn);
14411 }
14412};
14413corefn$5.invalidateDimensions = corefn$5.resize;
14414
14415var corefn$6 = {
14416 // get a collection
14417 // - empty collection on no args
14418 // - collection of elements in the graph on selector arg
14419 // - guarantee a returned collection when elements or collection specified
14420 collection: function collection(eles, opts) {
14421 if (string(eles)) {
14422 return this.$(eles);
14423 } else if (elementOrCollection(eles)) {
14424 return eles.collection();
14425 } else if (array(eles)) {
14426 return new Collection(this, eles, opts);
14427 }
14428
14429 return new Collection(this);
14430 },
14431 nodes: function nodes(selector) {
14432 var nodes = this.$(function (ele) {
14433 return ele.isNode();
14434 });
14435
14436 if (selector) {
14437 return nodes.filter(selector);
14438 }
14439
14440 return nodes;
14441 },
14442 edges: function edges(selector) {
14443 var edges = this.$(function (ele) {
14444 return ele.isEdge();
14445 });
14446
14447 if (selector) {
14448 return edges.filter(selector);
14449 }
14450
14451 return edges;
14452 },
14453 // search the graph like jQuery
14454 $: function $(selector) {
14455 var eles = this._private.elements;
14456
14457 if (selector) {
14458 return eles.filter(selector);
14459 } else {
14460 return eles.spawnSelf();
14461 }
14462 },
14463 mutableElements: function mutableElements() {
14464 return this._private.elements;
14465 }
14466}; // aliases
14467
14468corefn$6.elements = corefn$6.filter = corefn$6.$;
14469
14470var styfn = {}; // keys for style blocks, e.g. ttfftt
14471
14472var TRUE = 't';
14473var FALSE = 'f'; // (potentially expensive calculation)
14474// apply the style to the element based on
14475// - its bypass
14476// - what selectors match it
14477
14478styfn.apply = function (eles) {
14479 var self = this;
14480 var _p = self._private;
14481 var cy = _p.cy;
14482 var updatedEles = cy.collection();
14483
14484 for (var ie = 0; ie < eles.length; ie++) {
14485 var ele = eles[ie];
14486 var cxtMeta = self.getContextMeta(ele);
14487
14488 if (cxtMeta.empty) {
14489 continue;
14490 }
14491
14492 var cxtStyle = self.getContextStyle(cxtMeta);
14493 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14494
14495 if (ele._private.appliedInitStyle) {
14496 self.updateTransitions(ele, app.diffProps);
14497 } else {
14498 ele._private.appliedInitStyle = true;
14499 }
14500
14501 var hintsDiff = self.updateStyleHints(ele);
14502
14503 if (hintsDiff) {
14504 updatedEles.push(ele);
14505 }
14506 } // for elements
14507
14508
14509 return updatedEles;
14510};
14511
14512styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14513 var self = this;
14514 var cache = self._private.propDiffs = self._private.propDiffs || {};
14515 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14516 var cachedVal = cache[dualCxtKey];
14517
14518 if (cachedVal) {
14519 return cachedVal;
14520 }
14521
14522 var diffProps = [];
14523 var addedProp = {};
14524
14525 for (var i = 0; i < self.length; i++) {
14526 var cxt = self[i];
14527 var oldHasCxt = oldCxtKey[i] === TRUE;
14528 var newHasCxt = newCxtKey[i] === TRUE;
14529 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14530 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14531
14532 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14533 var props = void 0;
14534
14535 if (cxtHasDiffed && cxtHasMappedProps) {
14536 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14537 } else if (cxtHasDiffed) {
14538 props = cxt.properties; // need to check them all
14539 } else if (cxtHasMappedProps) {
14540 props = cxt.mappedProperties; // only need to check mapped
14541 }
14542
14543 for (var j = 0; j < props.length; j++) {
14544 var prop = props[j];
14545 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14546 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14547 // is cached)
14548
14549 var laterCxtOverrides = false;
14550
14551 for (var k = i + 1; k < self.length; k++) {
14552 var laterCxt = self[k];
14553 var hasLaterCxt = newCxtKey[k] === TRUE;
14554
14555 if (!hasLaterCxt) {
14556 continue;
14557 } // can't override unless the context is active
14558
14559
14560 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14561
14562 if (laterCxtOverrides) {
14563 break;
14564 } // exit early as long as one later context overrides
14565
14566 }
14567
14568 if (!addedProp[name] && !laterCxtOverrides) {
14569 addedProp[name] = true;
14570 diffProps.push(name);
14571 }
14572 } // for props
14573
14574 } // if
14575
14576 } // for contexts
14577
14578
14579 cache[dualCxtKey] = diffProps;
14580 return diffProps;
14581};
14582
14583styfn.getContextMeta = function (ele) {
14584 var self = this;
14585 var cxtKey = '';
14586 var diffProps;
14587 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
14588
14589 for (var i = 0; i < self.length; i++) {
14590 var context = self[i];
14591 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14592
14593 if (contextSelectorMatches) {
14594 cxtKey += TRUE;
14595 } else {
14596 cxtKey += FALSE;
14597 }
14598 } // for context
14599
14600
14601 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14602 ele._private.styleCxtKey = cxtKey;
14603 return {
14604 key: cxtKey,
14605 diffPropNames: diffProps,
14606 empty: diffProps.length === 0
14607 };
14608}; // gets a computed ele style object based on matched contexts
14609
14610
14611styfn.getContextStyle = function (cxtMeta) {
14612 var cxtKey = cxtMeta.key;
14613 var self = this;
14614 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14615
14616 if (cxtStyles[cxtKey]) {
14617 return cxtStyles[cxtKey];
14618 }
14619
14620 var style = {
14621 _private: {
14622 key: cxtKey
14623 }
14624 };
14625
14626 for (var i = 0; i < self.length; i++) {
14627 var cxt = self[i];
14628 var hasCxt = cxtKey[i] === TRUE;
14629
14630 if (!hasCxt) {
14631 continue;
14632 }
14633
14634 for (var j = 0; j < cxt.properties.length; j++) {
14635 var prop = cxt.properties[j];
14636 style[prop.name] = prop;
14637 }
14638 }
14639
14640 cxtStyles[cxtKey] = style;
14641 return style;
14642};
14643
14644styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14645 var self = this;
14646 var diffProps = cxtMeta.diffPropNames;
14647 var retDiffProps = {};
14648 var types = self.types;
14649
14650 for (var i = 0; i < diffProps.length; i++) {
14651 var diffPropName = diffProps[i];
14652 var cxtProp = cxtStyle[diffPropName];
14653 var eleProp = ele.pstyle(diffPropName);
14654
14655 if (!cxtProp) {
14656 // no context prop means delete
14657 if (!eleProp) {
14658 continue; // no existing prop means nothing needs to be removed
14659 // nb affects initial application on mapped values like control-point-distances
14660 } else if (eleProp.bypass) {
14661 cxtProp = {
14662 name: diffPropName,
14663 deleteBypassed: true
14664 };
14665 } else {
14666 cxtProp = {
14667 name: diffPropName,
14668 "delete": true
14669 };
14670 }
14671 } // save cycles when the context prop doesn't need to be applied
14672
14673
14674 if (eleProp === cxtProp) {
14675 continue;
14676 } // save cycles when a mapped context prop doesn't need to be applied
14677
14678
14679 if (cxtProp.mapped === types.fn // context prop is function mapper
14680 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14681 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14682 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14683 ) {
14684 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14685 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14686
14687 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14688
14689 if (fnValue === mapping.prevFnValue) {
14690 continue;
14691 }
14692 }
14693
14694 var retDiffProp = retDiffProps[diffPropName] = {
14695 prev: eleProp
14696 };
14697 self.applyParsedProperty(ele, cxtProp);
14698 retDiffProp.next = ele.pstyle(diffPropName);
14699
14700 if (retDiffProp.next && retDiffProp.next.bypass) {
14701 retDiffProp.next = retDiffProp.next.bypassed;
14702 }
14703 }
14704
14705 return {
14706 diffProps: retDiffProps
14707 };
14708};
14709
14710styfn.updateStyleHints = function (ele) {
14711 var _p = ele._private;
14712 var self = this;
14713 var propNames = self.propertyGroupNames;
14714 var propGrKeys = self.propertyGroupKeys;
14715
14716 var propHash = function propHash(ele, propNames, seedKey) {
14717 return self.getPropertiesHash(ele, propNames, seedKey);
14718 };
14719
14720 var oldStyleKey = _p.styleKey;
14721
14722 if (ele.removed()) {
14723 return false;
14724 }
14725
14726 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14727 // but lazily -- only use non-default prop values to reduce the number of hashes
14728 //
14729
14730 var overriddenStyles = ele._private.style;
14731 propNames = Object.keys(overriddenStyles);
14732
14733 for (var i = 0; i < propGrKeys.length; i++) {
14734 var grKey = propGrKeys[i];
14735 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14736 }
14737
14738 var updateGrKey1 = function updateGrKey1(val, grKey) {
14739 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14740 };
14741
14742 var updateGrKey2 = function updateGrKey2(val, grKey) {
14743 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14744 };
14745
14746 var updateGrKey = function updateGrKey(val, grKey) {
14747 updateGrKey1(val, grKey);
14748 updateGrKey2(val, grKey);
14749 };
14750
14751 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14752 for (var j = 0; j < strVal.length; j++) {
14753 var ch = strVal.charCodeAt(j);
14754 updateGrKey1(ch, grKey);
14755 updateGrKey2(ch, grKey);
14756 }
14757 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14758 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14759 // - raise up small numbers so more significant digits are seen by hashing
14760 // - make small numbers larger than a normal value to avoid collisions
14761 // - works in practice and it's relatively cheap
14762
14763
14764 var N = 2000000000;
14765
14766 var cleanNum = function cleanNum(val) {
14767 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14768 };
14769
14770 for (var _i = 0; _i < propNames.length; _i++) {
14771 var name = propNames[_i];
14772 var parsedProp = overriddenStyles[name];
14773
14774 if (parsedProp == null) {
14775 continue;
14776 }
14777
14778 var propInfo = this.properties[name];
14779 var type = propInfo.type;
14780 var _grKey = propInfo.groupKey;
14781 var normalizedNumberVal = void 0;
14782
14783 if (propInfo.hashOverride != null) {
14784 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14785 } else if (parsedProp.pfValue != null) {
14786 normalizedNumberVal = parsedProp.pfValue;
14787 } // might not be a number if it allows enums
14788
14789
14790 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14791 var haveNormNum = normalizedNumberVal != null;
14792 var haveUnitedNum = numberVal != null;
14793 var haveNum = haveNormNum || haveUnitedNum;
14794 var units = parsedProp.units; // numbers are cheaper to hash than strings
14795 // 1 hash op vs n hash ops (for length n string)
14796
14797 if (type.number && haveNum && !type.multiple) {
14798 var v = haveNormNum ? normalizedNumberVal : numberVal;
14799 updateGrKey(cleanNum(v), _grKey);
14800
14801 if (!haveNormNum && units != null) {
14802 updateGrKeyWStr(units, _grKey);
14803 }
14804 } else {
14805 updateGrKeyWStr(parsedProp.strValue, _grKey);
14806 }
14807 } // overall style key
14808 //
14809
14810
14811 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14812
14813 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14814 var _grKey2 = propGrKeys[_i2];
14815 var grHash = _p.styleKeys[_grKey2];
14816 hash[0] = hashInt(grHash[0], hash[0]);
14817 hash[1] = hashIntAlt(grHash[1], hash[1]);
14818 }
14819
14820 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14821 //
14822
14823 var sk = _p.styleKeys;
14824 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14825 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14826 _p.labelKey = combineHashesArray(labelKeys);
14827 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14828
14829 if (!isNode) {
14830 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14831 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14832 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14833 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14834 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14835 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14836 } // node
14837 //
14838
14839
14840 if (isNode) {
14841 var _p$styleKeys = _p.styleKeys,
14842 nodeBody = _p$styleKeys.nodeBody,
14843 nodeBorder = _p$styleKeys.nodeBorder,
14844 backgroundImage = _p$styleKeys.backgroundImage,
14845 compound = _p$styleKeys.compound,
14846 pie = _p$styleKeys.pie;
14847 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14848 return k != null;
14849 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14850 _p.nodeKey = combineHashesArray(nodeKeys);
14851 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14852 }
14853
14854 return oldStyleKey !== _p.styleKey;
14855};
14856
14857styfn.clearStyleHints = function (ele) {
14858 var _p = ele._private;
14859 _p.styleCxtKey = '';
14860 _p.styleKeys = {};
14861 _p.styleKey = null;
14862 _p.labelKey = null;
14863 _p.labelStyleKey = null;
14864 _p.sourceLabelKey = null;
14865 _p.sourceLabelStyleKey = null;
14866 _p.targetLabelKey = null;
14867 _p.targetLabelStyleKey = null;
14868 _p.nodeKey = null;
14869 _p.hasPie = null;
14870}; // apply a property to the style (for internal use)
14871// returns whether application was successful
14872//
14873// now, this function flattens the property, and here's how:
14874//
14875// for parsedProp:{ bypass: true, deleteBypass: true }
14876// no property is generated, instead the bypass property in the
14877// element's style is replaced by what's pointed to by the `bypassed`
14878// field in the bypass property (i.e. restoring the property the
14879// bypass was overriding)
14880//
14881// for parsedProp:{ mapped: truthy }
14882// the generated flattenedProp:{ mapping: prop }
14883//
14884// for parsedProp:{ bypass: true }
14885// the generated flattenedProp:{ bypassed: parsedProp }
14886
14887
14888styfn.applyParsedProperty = function (ele, parsedProp) {
14889 var self = this;
14890 var prop = parsedProp;
14891 var style = ele._private.style;
14892 var flatProp;
14893 var types = self.types;
14894 var type = self.properties[prop.name].type;
14895 var propIsBypass = prop.bypass;
14896 var origProp = style[prop.name];
14897 var origPropIsBypass = origProp && origProp.bypass;
14898 var _p = ele._private;
14899 var flatPropMapping = 'mapping';
14900
14901 var getVal = function getVal(p) {
14902 if (p == null) {
14903 return null;
14904 } else if (p.pfValue != null) {
14905 return p.pfValue;
14906 } else {
14907 return p.value;
14908 }
14909 };
14910
14911 var checkTriggers = function checkTriggers() {
14912 var fromVal = getVal(origProp);
14913 var toVal = getVal(prop);
14914 self.checkTriggers(ele, prop.name, fromVal, toVal);
14915 };
14916
14917 if (prop && prop.name.substr(0, 3) === 'pie') {
14918 warn('The pie style properties are deprecated. Create charts using background images instead.');
14919 } // edge sanity checks to prevent the client from making serious mistakes
14920
14921
14922 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
14923 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
14924 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
14925 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
14926 }
14927
14928 if (prop["delete"]) {
14929 // delete the property and use the default value on falsey value
14930 style[prop.name] = undefined;
14931 checkTriggers();
14932 return true;
14933 }
14934
14935 if (prop.deleteBypassed) {
14936 // delete the property that the
14937 if (!origProp) {
14938 checkTriggers();
14939 return true; // can't delete if no prop
14940 } else if (origProp.bypass) {
14941 // delete bypassed
14942 origProp.bypassed = undefined;
14943 checkTriggers();
14944 return true;
14945 } else {
14946 return false; // we're unsuccessful deleting the bypassed
14947 }
14948 } // check if we need to delete the current bypass
14949
14950
14951 if (prop.deleteBypass) {
14952 // then this property is just here to indicate we need to delete
14953 if (!origProp) {
14954 checkTriggers();
14955 return true; // property is already not defined
14956 } else if (origProp.bypass) {
14957 // then replace the bypass property with the original
14958 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
14959 style[prop.name] = origProp.bypassed;
14960 checkTriggers();
14961 return true;
14962 } else {
14963 return false; // we're unsuccessful deleting the bypass
14964 }
14965 }
14966
14967 var printMappingErr = function printMappingErr() {
14968 warn('Do not assign mappings to elements without corresponding data (i.e. ele `' + ele.id() + '` has no mapping for property `' + prop.name + '` with data field `' + prop.field + '`); try a `[' + prop.field + ']` selector to limit scope to elements with `' + prop.field + '` defined');
14969 }; // put the property in the style objects
14970
14971
14972 switch (prop.mapped) {
14973 // flatten the property if mapped
14974 case types.mapData:
14975 {
14976 // flatten the field (e.g. data.foo.bar)
14977 var fields = prop.field.split('.');
14978 var fieldVal = _p.data;
14979
14980 for (var i = 0; i < fields.length && fieldVal; i++) {
14981 var field = fields[i];
14982 fieldVal = fieldVal[field];
14983 }
14984
14985 if (fieldVal == null) {
14986 printMappingErr();
14987 return false;
14988 }
14989
14990 var percent;
14991
14992 if (!number(fieldVal)) {
14993 // then don't apply and fall back on the existing style
14994 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
14995 return false;
14996 } else {
14997 var fieldWidth = prop.fieldMax - prop.fieldMin;
14998
14999 if (fieldWidth === 0) {
15000 // safety check -- not strictly necessary as no props of zero range should be passed here
15001 percent = 0;
15002 } else {
15003 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15004 }
15005 } // make sure to bound percent value
15006
15007
15008 if (percent < 0) {
15009 percent = 0;
15010 } else if (percent > 1) {
15011 percent = 1;
15012 }
15013
15014 if (type.color) {
15015 var r1 = prop.valueMin[0];
15016 var r2 = prop.valueMax[0];
15017 var g1 = prop.valueMin[1];
15018 var g2 = prop.valueMax[1];
15019 var b1 = prop.valueMin[2];
15020 var b2 = prop.valueMax[2];
15021 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15022 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15023 var clr = [Math.round(r1 + (r2 - r1) * percent), Math.round(g1 + (g2 - g1) * percent), Math.round(b1 + (b2 - b1) * percent), Math.round(a1 + (a2 - a1) * percent)];
15024 flatProp = {
15025 // colours are simple, so just create the flat property instead of expensive string parsing
15026 bypass: prop.bypass,
15027 // we're a bypass if the mapping property is a bypass
15028 name: prop.name,
15029 value: clr,
15030 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15031 };
15032 } else if (type.number) {
15033 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15034 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15035 } else {
15036 return false; // can only map to colours and numbers
15037 }
15038
15039 if (!flatProp) {
15040 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15041 printMappingErr();
15042 return false;
15043 }
15044
15045 flatProp.mapping = prop; // keep a reference to the mapping
15046
15047 prop = flatProp; // the flattened (mapped) property is the one we want
15048
15049 break;
15050 }
15051 // direct mapping
15052
15053 case types.data:
15054 {
15055 // flatten the field (e.g. data.foo.bar)
15056 var _fields = prop.field.split('.');
15057
15058 var _fieldVal = _p.data;
15059
15060 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15061 var _field = _fields[_i3];
15062 _fieldVal = _fieldVal[_field];
15063 }
15064
15065 if (_fieldVal != null) {
15066 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15067 }
15068
15069 if (!flatProp) {
15070 // if we can't flatten the property, then don't apply and fall back on the existing style
15071 printMappingErr();
15072 return false;
15073 }
15074
15075 flatProp.mapping = prop; // keep a reference to the mapping
15076
15077 prop = flatProp; // the flattened (mapped) property is the one we want
15078
15079 break;
15080 }
15081
15082 case types.fn:
15083 {
15084 var fn = prop.value;
15085 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15086
15087 prop.prevFnValue = fnRetVal;
15088
15089 if (fnRetVal == null) {
15090 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15091 return false;
15092 }
15093
15094 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15095
15096 if (!flatProp) {
15097 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15098 return false;
15099 }
15100
15101 flatProp.mapping = copy(prop); // keep a reference to the mapping
15102
15103 prop = flatProp; // the flattened (mapped) property is the one we want
15104
15105 break;
15106 }
15107
15108 case undefined:
15109 break;
15110 // just set the property
15111
15112 default:
15113 return false;
15114 // not a valid mapping
15115 } // if the property is a bypass property, then link the resultant property to the original one
15116
15117
15118 if (propIsBypass) {
15119 if (origPropIsBypass) {
15120 // then this bypass overrides the existing one
15121 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15122 } else {
15123 // then link the orig prop to the new bypass
15124 prop.bypassed = origProp;
15125 }
15126
15127 style[prop.name] = prop; // and set
15128 } else {
15129 // prop is not bypass
15130 if (origPropIsBypass) {
15131 // then keep the orig prop (since it's a bypass) and link to the new prop
15132 origProp.bypassed = prop;
15133 } else {
15134 // then just replace the old prop with the new one
15135 style[prop.name] = prop;
15136 }
15137 }
15138
15139 checkTriggers();
15140 return true;
15141};
15142
15143styfn.cleanElements = function (eles, keepBypasses) {
15144 for (var i = 0; i < eles.length; i++) {
15145 var ele = eles[i];
15146 this.clearStyleHints(ele);
15147 ele.dirtyCompoundBoundsCache();
15148 ele.dirtyBoundingBoxCache();
15149
15150 if (!keepBypasses) {
15151 ele._private.style = {};
15152 } else {
15153 var style = ele._private.style;
15154 var propNames = Object.keys(style);
15155
15156 for (var j = 0; j < propNames.length; j++) {
15157 var propName = propNames[j];
15158 var eleProp = style[propName];
15159
15160 if (eleProp != null) {
15161 if (eleProp.bypass) {
15162 eleProp.bypassed = null;
15163 } else {
15164 style[propName] = null;
15165 }
15166 }
15167 }
15168 }
15169 }
15170}; // updates the visual style for all elements (useful for manual style modification after init)
15171
15172
15173styfn.update = function () {
15174 var cy = this._private.cy;
15175 var eles = cy.mutableElements();
15176 eles.updateStyle();
15177}; // diffProps : { name => { prev, next } }
15178
15179
15180styfn.updateTransitions = function (ele, diffProps) {
15181 var self = this;
15182 var _p = ele._private;
15183 var props = ele.pstyle('transition-property').value;
15184 var duration = ele.pstyle('transition-duration').pfValue;
15185 var delay = ele.pstyle('transition-delay').pfValue;
15186
15187 if (props.length > 0 && duration > 0) {
15188 var style = {}; // build up the style to animate towards
15189
15190 var anyPrev = false;
15191
15192 for (var i = 0; i < props.length; i++) {
15193 var prop = props[i];
15194 var styProp = ele.pstyle(prop);
15195 var diffProp = diffProps[prop];
15196
15197 if (!diffProp) {
15198 continue;
15199 }
15200
15201 var prevProp = diffProp.prev;
15202 var fromProp = prevProp;
15203 var toProp = diffProp.next != null ? diffProp.next : styProp;
15204 var diff = false;
15205 var initVal = void 0;
15206 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15207
15208 if (!fromProp) {
15209 continue;
15210 } // consider px values
15211
15212
15213 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15214 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15215
15216 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15217 } else if (number(fromProp.value) && number(toProp.value)) {
15218 diff = toProp.value - fromProp.value; // nonzero is truthy
15219
15220 initVal = fromProp.value + initDt * diff; // consider colour values
15221 } else if (array(fromProp.value) && array(toProp.value)) {
15222 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15223 initVal = fromProp.strValue;
15224 } // the previous value is good for an animation only if it's different
15225
15226
15227 if (diff) {
15228 style[prop] = toProp.strValue; // to val
15229
15230 this.applyBypass(ele, prop, initVal); // from val
15231
15232 anyPrev = true;
15233 }
15234 } // end if props allow ani
15235 // can't transition if there's nothing previous to transition from
15236
15237
15238 if (!anyPrev) {
15239 return;
15240 }
15241
15242 _p.transitioning = true;
15243 new Promise$1(function (resolve) {
15244 if (delay > 0) {
15245 ele.delayAnimation(delay).play().promise().then(resolve);
15246 } else {
15247 resolve();
15248 }
15249 }).then(function () {
15250 return ele.animation({
15251 style: style,
15252 duration: duration,
15253 easing: ele.pstyle('transition-timing-function').value,
15254 queue: false
15255 }).play().promise();
15256 }).then(function () {
15257 // if( !isBypass ){
15258 self.removeBypasses(ele, props);
15259 ele.emitAndNotify('style'); // }
15260
15261 _p.transitioning = false;
15262 });
15263 } else if (_p.transitioning) {
15264 this.removeBypasses(ele, props);
15265 ele.emitAndNotify('style');
15266 _p.transitioning = false;
15267 }
15268};
15269
15270styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15271 var prop = this.properties[name];
15272 var triggerCheck = getTrigger(prop);
15273
15274 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15275 onTrigger(prop);
15276 }
15277};
15278
15279styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15280 var _this = this;
15281
15282 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15283 return prop.triggersZOrder;
15284 }, function () {
15285 _this._private.cy.notify('zorder', ele);
15286 });
15287};
15288
15289styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15290 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15291 return prop.triggersBounds;
15292 }, function (prop) {
15293 ele.dirtyCompoundBoundsCache();
15294 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15295 // then dirty the pll edge bb cache as well
15296
15297 if ( // only for beziers -- so performance of other edges isn't affected
15298 name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') && prop.triggersBoundsOfParallelBeziers) {
15299 ele.parallelEdges().forEach(function (pllEdge) {
15300 if (pllEdge.isBundledBezier()) {
15301 pllEdge.dirtyBoundingBoxCache();
15302 }
15303 });
15304 }
15305 });
15306};
15307
15308styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15309 ele.dirtyStyleCache();
15310 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15311 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15312};
15313
15314var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15315// returns true iff application was successful for at least 1 specified property
15316
15317styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15318 var self = this;
15319 var props = [];
15320 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15321
15322 if (name === '*' || name === '**') {
15323 // apply to all property names
15324 if (value !== undefined) {
15325 for (var i = 0; i < self.properties.length; i++) {
15326 var prop = self.properties[i];
15327 var _name = prop.name;
15328 var parsedProp = this.parse(_name, value, true);
15329
15330 if (parsedProp) {
15331 props.push(parsedProp);
15332 }
15333 }
15334 }
15335 } else if (string(name)) {
15336 // then parse the single property
15337 var _parsedProp = this.parse(name, value, true);
15338
15339 if (_parsedProp) {
15340 props.push(_parsedProp);
15341 }
15342 } else if (plainObject(name)) {
15343 // then parse each property
15344 var specifiedProps = name;
15345 updateTransitions = value;
15346 var names = Object.keys(specifiedProps);
15347
15348 for (var _i = 0; _i < names.length; _i++) {
15349 var _name2 = names[_i];
15350 var _value = specifiedProps[_name2];
15351
15352 if (_value === undefined) {
15353 // try camel case name too
15354 _value = specifiedProps[dash2camel(_name2)];
15355 }
15356
15357 if (_value !== undefined) {
15358 var _parsedProp2 = this.parse(_name2, _value, true);
15359
15360 if (_parsedProp2) {
15361 props.push(_parsedProp2);
15362 }
15363 }
15364 }
15365 } else {
15366 // can't do anything without well defined properties
15367 return false;
15368 } // we've failed if there are no valid properties
15369
15370
15371 if (props.length === 0) {
15372 return false;
15373 } // now, apply the bypass properties on the elements
15374
15375
15376 var ret = false; // return true if at least one succesful bypass applied
15377
15378 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15379 // for each ele
15380 var ele = eles[_i2];
15381 var diffProps = {};
15382 var diffProp = void 0;
15383
15384 for (var j = 0; j < props.length; j++) {
15385 // for each prop
15386 var _prop = props[j];
15387
15388 if (updateTransitions) {
15389 var prevProp = ele.pstyle(_prop.name);
15390 diffProp = diffProps[_prop.name] = {
15391 prev: prevProp
15392 };
15393 }
15394
15395 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15396
15397 if (updateTransitions) {
15398 diffProp.next = ele.pstyle(_prop.name);
15399 }
15400 } // for props
15401
15402
15403 if (ret) {
15404 this.updateStyleHints(ele);
15405 }
15406
15407 if (updateTransitions) {
15408 this.updateTransitions(ele, diffProps, isBypass);
15409 }
15410 } // for eles
15411
15412
15413 return ret;
15414}; // only useful in specific cases like animation
15415
15416
15417styfn$1.overrideBypass = function (eles, name, value) {
15418 name = camel2dash(name);
15419
15420 for (var i = 0; i < eles.length; i++) {
15421 var ele = eles[i];
15422 var prop = ele._private.style[name];
15423 var type = this.properties[name].type;
15424 var isColor = type.color;
15425 var isMulti = type.mutiple;
15426 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15427
15428 if (!prop || !prop.bypass) {
15429 // need a bypass if one doesn't exist
15430 this.applyBypass(ele, name, value);
15431 } else {
15432 prop.value = value;
15433
15434 if (prop.pfValue != null) {
15435 prop.pfValue = value;
15436 }
15437
15438 if (isColor) {
15439 prop.strValue = 'rgb(' + value.join(',') + ')';
15440 } else if (isMulti) {
15441 prop.strValue = value.join(' ');
15442 } else {
15443 prop.strValue = '' + value;
15444 }
15445
15446 this.updateStyleHints(ele);
15447 }
15448
15449 this.checkTriggers(ele, name, oldValue, value);
15450 }
15451};
15452
15453styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15454 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15455};
15456
15457styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15458 var isBypass = true;
15459
15460 for (var j = 0; j < eles.length; j++) {
15461 var ele = eles[j];
15462 var diffProps = {};
15463
15464 for (var i = 0; i < props.length; i++) {
15465 var name = props[i];
15466 var prop = this.properties[name];
15467 var prevProp = ele.pstyle(prop.name);
15468
15469 if (!prevProp || !prevProp.bypass) {
15470 // if a bypass doesn't exist for the prop, nothing needs to be removed
15471 continue;
15472 }
15473
15474 var value = ''; // empty => remove bypass
15475
15476 var parsedProp = this.parse(name, value, true);
15477 var diffProp = diffProps[prop.name] = {
15478 prev: prevProp
15479 };
15480 this.applyParsedProperty(ele, parsedProp);
15481 diffProp.next = ele.pstyle(prop.name);
15482 } // for props
15483
15484
15485 this.updateStyleHints(ele);
15486
15487 if (updateTransitions) {
15488 this.updateTransitions(ele, diffProps, isBypass);
15489 }
15490 } // for eles
15491
15492};
15493
15494var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15495
15496styfn$2.getEmSizeInPixels = function () {
15497 var px = this.containerCss('font-size');
15498
15499 if (px != null) {
15500 return parseFloat(px);
15501 } else {
15502 return 1; // for headless
15503 }
15504}; // gets css property from the core container
15505
15506
15507styfn$2.containerCss = function (propName) {
15508 var cy = this._private.cy;
15509 var domElement = cy.container();
15510
15511 if (window$1 && domElement && window$1.getComputedStyle) {
15512 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15513 }
15514};
15515
15516var styfn$3 = {}; // gets the rendered style for an element
15517
15518styfn$3.getRenderedStyle = function (ele, prop) {
15519 if (prop) {
15520 return this.getStylePropertyValue(ele, prop, true);
15521 } else {
15522 return this.getRawStyle(ele, true);
15523 }
15524}; // gets the raw style for an element
15525
15526
15527styfn$3.getRawStyle = function (ele, isRenderedVal) {
15528 var self = this;
15529 ele = ele[0]; // insure it's an element
15530
15531 if (ele) {
15532 var rstyle = {};
15533
15534 for (var i = 0; i < self.properties.length; i++) {
15535 var prop = self.properties[i];
15536 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15537
15538 if (val != null) {
15539 rstyle[prop.name] = val;
15540 rstyle[dash2camel(prop.name)] = val;
15541 }
15542 }
15543
15544 return rstyle;
15545 }
15546};
15547
15548styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15549 var pstyle = ele.pstyle(property)[subproperty][index];
15550 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15551};
15552
15553styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15554 var self = this;
15555 ele = ele[0]; // insure it's an element
15556
15557 if (ele) {
15558 var prop = self.properties[propName];
15559
15560 if (prop.alias) {
15561 prop = prop.pointsTo;
15562 }
15563
15564 var type = prop.type;
15565 var styleProp = ele.pstyle(prop.name);
15566
15567 if (styleProp) {
15568 var value = styleProp.value,
15569 units = styleProp.units,
15570 strValue = styleProp.strValue;
15571
15572 if (isRenderedVal && type.number && value != null && number(value)) {
15573 var zoom = ele.cy().zoom();
15574
15575 var getRenderedValue = function getRenderedValue(val) {
15576 return val * zoom;
15577 };
15578
15579 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15580 return getRenderedValue(val) + units;
15581 };
15582
15583 var isArrayValue = array(value);
15584 var haveUnits = isArrayValue ? units.every(function (u) {
15585 return u != null;
15586 }) : units != null;
15587
15588 if (haveUnits) {
15589 if (isArrayValue) {
15590 return value.map(function (v, i) {
15591 return getValueStringWithUnits(v, units[i]);
15592 }).join(' ');
15593 } else {
15594 return getValueStringWithUnits(value, units);
15595 }
15596 } else {
15597 if (isArrayValue) {
15598 return value.map(function (v) {
15599 return string(v) ? v : '' + getRenderedValue(v);
15600 }).join(' ');
15601 } else {
15602 return '' + getRenderedValue(value);
15603 }
15604 }
15605 } else if (strValue != null) {
15606 return strValue;
15607 }
15608 }
15609
15610 return null;
15611 }
15612};
15613
15614styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15615 var rstyle = {};
15616
15617 for (var i = 0; i < aniProps.length; i++) {
15618 var aniProp = aniProps[i];
15619 var name = aniProp.name;
15620 var styleProp = ele.pstyle(name);
15621
15622 if (styleProp !== undefined) {
15623 // then make a prop of it
15624 if (plainObject(styleProp)) {
15625 styleProp = this.parse(name, styleProp.strValue);
15626 } else {
15627 styleProp = this.parse(name, styleProp);
15628 }
15629 }
15630
15631 if (styleProp) {
15632 rstyle[name] = styleProp;
15633 }
15634 }
15635
15636 return rstyle;
15637};
15638
15639styfn$3.getPropsList = function (propsObj) {
15640 var self = this;
15641 var rstyle = [];
15642 var style = propsObj;
15643 var props = self.properties;
15644
15645 if (style) {
15646 var names = Object.keys(style);
15647
15648 for (var i = 0; i < names.length; i++) {
15649 var name = names[i];
15650 var val = style[name];
15651 var prop = props[name] || props[camel2dash(name)];
15652 var styleProp = this.parse(prop.name, val);
15653
15654 if (styleProp) {
15655 rstyle.push(styleProp);
15656 }
15657 }
15658 }
15659
15660 return rstyle;
15661};
15662
15663styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15664 var hash = seed.slice();
15665 var name, val, strVal, chVal;
15666 var i, j;
15667
15668 for (i = 0; i < propNames.length; i++) {
15669 name = propNames[i];
15670 val = ele.pstyle(name, false);
15671
15672 if (val == null) {
15673 continue;
15674 } else if (val.pfValue != null) {
15675 hash[0] = hashInt(chVal, hash[0]);
15676 hash[1] = hashIntAlt(chVal, hash[1]);
15677 } else {
15678 strVal = val.strValue;
15679
15680 for (j = 0; j < strVal.length; j++) {
15681 chVal = strVal.charCodeAt(j);
15682 hash[0] = hashInt(chVal, hash[0]);
15683 hash[1] = hashIntAlt(chVal, hash[1]);
15684 }
15685 }
15686 }
15687
15688 return hash;
15689};
15690
15691styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15692
15693var styfn$4 = {};
15694
15695styfn$4.appendFromJson = function (json) {
15696 var style = this;
15697
15698 for (var i = 0; i < json.length; i++) {
15699 var context = json[i];
15700 var selector = context.selector;
15701 var props = context.style || context.css;
15702 var names = Object.keys(props);
15703 style.selector(selector); // apply selector
15704
15705 for (var j = 0; j < names.length; j++) {
15706 var name = names[j];
15707 var value = props[name];
15708 style.css(name, value); // apply property
15709 }
15710 }
15711
15712 return style;
15713}; // accessible cy.style() function
15714
15715
15716styfn$4.fromJson = function (json) {
15717 var style = this;
15718 style.resetToDefault();
15719 style.appendFromJson(json);
15720 return style;
15721}; // get json from cy.style() api
15722
15723
15724styfn$4.json = function () {
15725 var json = [];
15726
15727 for (var i = this.defaultLength; i < this.length; i++) {
15728 var cxt = this[i];
15729 var selector = cxt.selector;
15730 var props = cxt.properties;
15731 var css = {};
15732
15733 for (var j = 0; j < props.length; j++) {
15734 var prop = props[j];
15735 css[prop.name] = prop.strValue;
15736 }
15737
15738 json.push({
15739 selector: !selector ? 'core' : selector.toString(),
15740 style: css
15741 });
15742 }
15743
15744 return json;
15745};
15746
15747var styfn$5 = {};
15748
15749styfn$5.appendFromString = function (string) {
15750 var self = this;
15751 var style = this;
15752 var remaining = '' + string;
15753 var selAndBlockStr;
15754 var blockRem;
15755 var propAndValStr; // remove comments from the style string
15756
15757 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15758
15759 function removeSelAndBlockFromRemaining() {
15760 // remove the parsed selector and block from the remaining text to parse
15761 if (remaining.length > selAndBlockStr.length) {
15762 remaining = remaining.substr(selAndBlockStr.length);
15763 } else {
15764 remaining = '';
15765 }
15766 }
15767
15768 function removePropAndValFromRem() {
15769 // remove the parsed property and value from the remaining block text to parse
15770 if (blockRem.length > propAndValStr.length) {
15771 blockRem = blockRem.substr(propAndValStr.length);
15772 } else {
15773 blockRem = '';
15774 }
15775 }
15776
15777 for (;;) {
15778 var nothingLeftToParse = remaining.match(/^\s*$/);
15779
15780 if (nothingLeftToParse) {
15781 break;
15782 }
15783
15784 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15785
15786 if (!selAndBlock) {
15787 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15788 break;
15789 }
15790
15791 selAndBlockStr = selAndBlock[0]; // parse the selector
15792
15793 var selectorStr = selAndBlock[1];
15794
15795 if (selectorStr !== 'core') {
15796 var selector = new Selector(selectorStr);
15797
15798 if (selector.invalid) {
15799 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15800
15801 removeSelAndBlockFromRemaining();
15802 continue;
15803 }
15804 } // parse the block of properties and values
15805
15806
15807 var blockStr = selAndBlock[2];
15808 var invalidBlock = false;
15809 blockRem = blockStr;
15810 var props = [];
15811
15812 for (;;) {
15813 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15814
15815 if (_nothingLeftToParse) {
15816 break;
15817 }
15818
15819 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15820
15821 if (!propAndVal) {
15822 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15823 invalidBlock = true;
15824 break;
15825 }
15826
15827 propAndValStr = propAndVal[0];
15828 var propStr = propAndVal[1];
15829 var valStr = propAndVal[2];
15830 var prop = self.properties[propStr];
15831
15832 if (!prop) {
15833 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15834
15835 removePropAndValFromRem();
15836 continue;
15837 }
15838
15839 var parsedProp = style.parse(propStr, valStr);
15840
15841 if (!parsedProp) {
15842 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15843
15844 removePropAndValFromRem();
15845 continue;
15846 }
15847
15848 props.push({
15849 name: propStr,
15850 val: valStr
15851 });
15852 removePropAndValFromRem();
15853 }
15854
15855 if (invalidBlock) {
15856 removeSelAndBlockFromRemaining();
15857 break;
15858 } // put the parsed block in the style
15859
15860
15861 style.selector(selectorStr);
15862
15863 for (var i = 0; i < props.length; i++) {
15864 var _prop = props[i];
15865 style.css(_prop.name, _prop.val);
15866 }
15867
15868 removeSelAndBlockFromRemaining();
15869 }
15870
15871 return style;
15872};
15873
15874styfn$5.fromString = function (string) {
15875 var style = this;
15876 style.resetToDefault();
15877 style.appendFromString(string);
15878 return style;
15879};
15880
15881var styfn$6 = {};
15882
15883(function () {
15884 var number = number$1;
15885 var rgba = rgbaNoBackRefs;
15886 var hsla = hslaNoBackRefs;
15887 var hex3$1 = hex3;
15888 var hex6$1 = hex6;
15889
15890 var data = function data(prefix) {
15891 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15892 };
15893
15894 var mapData = function mapData(prefix) {
15895 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15896 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15897 };
15898
15899 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15900
15901 styfn$6.types = {
15902 time: {
15903 number: true,
15904 min: 0,
15905 units: 's|ms',
15906 implicitUnits: 'ms'
15907 },
15908 percent: {
15909 number: true,
15910 min: 0,
15911 max: 100,
15912 units: '%',
15913 implicitUnits: '%'
15914 },
15915 percentages: {
15916 number: true,
15917 min: 0,
15918 max: 100,
15919 units: '%',
15920 implicitUnits: '%',
15921 multiple: true
15922 },
15923 zeroOneNumber: {
15924 number: true,
15925 min: 0,
15926 max: 1,
15927 unitless: true
15928 },
15929 zeroOneNumbers: {
15930 number: true,
15931 min: 0,
15932 max: 1,
15933 unitless: true,
15934 multiple: true
15935 },
15936 nOneOneNumber: {
15937 number: true,
15938 min: -1,
15939 max: 1,
15940 unitless: true
15941 },
15942 nonNegativeInt: {
15943 number: true,
15944 min: 0,
15945 integer: true,
15946 unitless: true
15947 },
15948 position: {
15949 enums: ['parent', 'origin']
15950 },
15951 nodeSize: {
15952 number: true,
15953 min: 0,
15954 enums: ['label']
15955 },
15956 number: {
15957 number: true,
15958 unitless: true
15959 },
15960 numbers: {
15961 number: true,
15962 unitless: true,
15963 multiple: true
15964 },
15965 positiveNumber: {
15966 number: true,
15967 unitless: true,
15968 min: 0,
15969 strictMin: true
15970 },
15971 size: {
15972 number: true,
15973 min: 0
15974 },
15975 bidirectionalSize: {
15976 number: true
15977 },
15978 // allows negative
15979 bidirectionalSizeMaybePercent: {
15980 number: true,
15981 allowPercent: true
15982 },
15983 // allows negative
15984 bidirectionalSizes: {
15985 number: true,
15986 multiple: true
15987 },
15988 // allows negative
15989 sizeMaybePercent: {
15990 number: true,
15991 min: 0,
15992 allowPercent: true
15993 },
15994 axisDirection: {
15995 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
15996 },
15997 paddingRelativeTo: {
15998 enums: ['width', 'height', 'average', 'min', 'max']
15999 },
16000 bgWH: {
16001 number: true,
16002 min: 0,
16003 allowPercent: true,
16004 enums: ['auto'],
16005 multiple: true
16006 },
16007 bgPos: {
16008 number: true,
16009 allowPercent: true,
16010 multiple: true
16011 },
16012 bgRelativeTo: {
16013 enums: ['inner', 'include-padding'],
16014 multiple: true
16015 },
16016 bgRepeat: {
16017 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16018 multiple: true
16019 },
16020 bgFit: {
16021 enums: ['none', 'contain', 'cover'],
16022 multiple: true
16023 },
16024 bgCrossOrigin: {
16025 enums: ['anonymous', 'use-credentials'],
16026 multiple: true
16027 },
16028 bgClip: {
16029 enums: ['none', 'node'],
16030 multiple: true
16031 },
16032 bgContainment: {
16033 enums: ['inside', 'over'],
16034 multiple: true
16035 },
16036 color: {
16037 color: true
16038 },
16039 colors: {
16040 color: true,
16041 multiple: true
16042 },
16043 fill: {
16044 enums: ['solid', 'linear-gradient', 'radial-gradient']
16045 },
16046 bool: {
16047 enums: ['yes', 'no']
16048 },
16049 bools: {
16050 enums: ['yes', 'no'],
16051 multiple: true
16052 },
16053 lineStyle: {
16054 enums: ['solid', 'dotted', 'dashed']
16055 },
16056 lineCap: {
16057 enums: ['butt', 'round', 'square']
16058 },
16059 borderStyle: {
16060 enums: ['solid', 'dotted', 'dashed', 'double']
16061 },
16062 curveStyle: {
16063 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16064 },
16065 fontFamily: {
16066 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16067 },
16068 fontStyle: {
16069 enums: ['italic', 'normal', 'oblique']
16070 },
16071 fontWeight: {
16072 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16073 },
16074 textDecoration: {
16075 enums: ['none', 'underline', 'overline', 'line-through']
16076 },
16077 textTransform: {
16078 enums: ['none', 'uppercase', 'lowercase']
16079 },
16080 textWrap: {
16081 enums: ['none', 'wrap', 'ellipsis']
16082 },
16083 textOverflowWrap: {
16084 enums: ['whitespace', 'anywhere']
16085 },
16086 textBackgroundShape: {
16087 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16088 },
16089 nodeShape: {
16090 enums: ['rectangle', 'roundrectangle', 'round-rectangle', 'cutrectangle', 'cut-rectangle', 'bottomroundrectangle', 'bottom-round-rectangle', 'barrel', 'ellipse', 'triangle', 'round-triangle', 'square', 'pentagon', 'round-pentagon', 'hexagon', 'round-hexagon', 'concavehexagon', 'concave-hexagon', 'heptagon', 'round-heptagon', 'octagon', 'round-octagon', 'tag', 'round-tag', 'star', 'diamond', 'round-diamond', 'vee', 'rhomboid', 'polygon']
16091 },
16092 compoundIncludeLabels: {
16093 enums: ['include', 'exclude']
16094 },
16095 arrowShape: {
16096 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16097 },
16098 arrowFill: {
16099 enums: ['filled', 'hollow']
16100 },
16101 display: {
16102 enums: ['element', 'none']
16103 },
16104 visibility: {
16105 enums: ['hidden', 'visible']
16106 },
16107 zCompoundDepth: {
16108 enums: ['bottom', 'orphan', 'auto', 'top']
16109 },
16110 zIndexCompare: {
16111 enums: ['auto', 'manual']
16112 },
16113 valign: {
16114 enums: ['top', 'center', 'bottom']
16115 },
16116 halign: {
16117 enums: ['left', 'center', 'right']
16118 },
16119 justification: {
16120 enums: ['left', 'center', 'right', 'auto']
16121 },
16122 text: {
16123 string: true
16124 },
16125 data: {
16126 mapping: true,
16127 regex: data('data')
16128 },
16129 layoutData: {
16130 mapping: true,
16131 regex: data('layoutData')
16132 },
16133 scratch: {
16134 mapping: true,
16135 regex: data('scratch')
16136 },
16137 mapData: {
16138 mapping: true,
16139 regex: mapData('mapData')
16140 },
16141 mapLayoutData: {
16142 mapping: true,
16143 regex: mapData('mapLayoutData')
16144 },
16145 mapScratch: {
16146 mapping: true,
16147 regex: mapData('mapScratch')
16148 },
16149 fn: {
16150 mapping: true,
16151 fn: true
16152 },
16153 url: {
16154 regexes: urlRegexes,
16155 singleRegexMatchValue: true
16156 },
16157 urls: {
16158 regexes: urlRegexes,
16159 singleRegexMatchValue: true,
16160 multiple: true
16161 },
16162 propList: {
16163 propList: true
16164 },
16165 angle: {
16166 number: true,
16167 units: 'deg|rad',
16168 implicitUnits: 'rad'
16169 },
16170 textRotation: {
16171 number: true,
16172 units: 'deg|rad',
16173 implicitUnits: 'rad',
16174 enums: ['none', 'autorotate']
16175 },
16176 polygonPointList: {
16177 number: true,
16178 multiple: true,
16179 evenMultiple: true,
16180 min: -1,
16181 max: 1,
16182 unitless: true
16183 },
16184 edgeDistances: {
16185 enums: ['intersection', 'node-position']
16186 },
16187 edgeEndpoint: {
16188 number: true,
16189 multiple: true,
16190 units: '%|px|em|deg|rad',
16191 implicitUnits: 'px',
16192 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16193 singleEnum: true,
16194 validate: function validate(valArr, unitsArr) {
16195 switch (valArr.length) {
16196 case 2:
16197 // can be % or px only
16198 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16199
16200 case 1:
16201 // can be enum, deg, or rad only
16202 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16203
16204 default:
16205 return false;
16206 }
16207 }
16208 },
16209 easing: {
16210 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16211 enums: ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'ease-in-sine', 'ease-out-sine', 'ease-in-out-sine', 'ease-in-quad', 'ease-out-quad', 'ease-in-out-quad', 'ease-in-cubic', 'ease-out-cubic', 'ease-in-out-cubic', 'ease-in-quart', 'ease-out-quart', 'ease-in-out-quart', 'ease-in-quint', 'ease-out-quint', 'ease-in-out-quint', 'ease-in-expo', 'ease-out-expo', 'ease-in-out-expo', 'ease-in-circ', 'ease-out-circ', 'ease-in-out-circ']
16212 },
16213 gradientDirection: {
16214 enums: ['to-bottom', 'to-top', 'to-left', 'to-right', 'to-bottom-right', 'to-bottom-left', 'to-top-right', 'to-top-left', 'to-right-bottom', 'to-left-bottom', 'to-right-top', 'to-left-top']
16215 },
16216 boundsExpansion: {
16217 number: true,
16218 multiple: true,
16219 min: 0,
16220 validate: function validate(valArr) {
16221 var length = valArr.length;
16222 return length === 1 || length === 2 || length === 4;
16223 }
16224 }
16225 };
16226 var diff = {
16227 zeroNonZero: function zeroNonZero(val1, val2) {
16228 if ((val1 == null || val2 == null) && val1 !== val2) {
16229 return true; // null cases could represent any value
16230 }
16231
16232 if (val1 == 0 && val2 != 0) {
16233 return true;
16234 } else if (val1 != 0 && val2 == 0) {
16235 return true;
16236 } else {
16237 return false;
16238 }
16239 },
16240 any: function any(val1, val2) {
16241 return val1 != val2;
16242 },
16243 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16244 var empty1 = emptyString(str1);
16245 var empty2 = emptyString(str2);
16246 return empty1 && !empty2 || !empty1 && empty2;
16247 }
16248 }; // define visual style properties
16249 //
16250 // - n.b. adding a new group of props may require updates to updateStyleHints()
16251 // - adding new props to an existing group gets handled automatically
16252
16253 var t = styfn$6.types;
16254 var mainLabel = [{
16255 name: 'label',
16256 type: t.text,
16257 triggersBounds: diff.any,
16258 triggersZOrder: diff.emptyNonEmpty
16259 }, {
16260 name: 'text-rotation',
16261 type: t.textRotation,
16262 triggersBounds: diff.any
16263 }, {
16264 name: 'text-margin-x',
16265 type: t.bidirectionalSize,
16266 triggersBounds: diff.any
16267 }, {
16268 name: 'text-margin-y',
16269 type: t.bidirectionalSize,
16270 triggersBounds: diff.any
16271 }];
16272 var sourceLabel = [{
16273 name: 'source-label',
16274 type: t.text,
16275 triggersBounds: diff.any
16276 }, {
16277 name: 'source-text-rotation',
16278 type: t.textRotation,
16279 triggersBounds: diff.any
16280 }, {
16281 name: 'source-text-margin-x',
16282 type: t.bidirectionalSize,
16283 triggersBounds: diff.any
16284 }, {
16285 name: 'source-text-margin-y',
16286 type: t.bidirectionalSize,
16287 triggersBounds: diff.any
16288 }, {
16289 name: 'source-text-offset',
16290 type: t.size,
16291 triggersBounds: diff.any
16292 }];
16293 var targetLabel = [{
16294 name: 'target-label',
16295 type: t.text,
16296 triggersBounds: diff.any
16297 }, {
16298 name: 'target-text-rotation',
16299 type: t.textRotation,
16300 triggersBounds: diff.any
16301 }, {
16302 name: 'target-text-margin-x',
16303 type: t.bidirectionalSize,
16304 triggersBounds: diff.any
16305 }, {
16306 name: 'target-text-margin-y',
16307 type: t.bidirectionalSize,
16308 triggersBounds: diff.any
16309 }, {
16310 name: 'target-text-offset',
16311 type: t.size,
16312 triggersBounds: diff.any
16313 }];
16314 var labelDimensions = [{
16315 name: 'font-family',
16316 type: t.fontFamily,
16317 triggersBounds: diff.any
16318 }, {
16319 name: 'font-style',
16320 type: t.fontStyle,
16321 triggersBounds: diff.any
16322 }, {
16323 name: 'font-weight',
16324 type: t.fontWeight,
16325 triggersBounds: diff.any
16326 }, {
16327 name: 'font-size',
16328 type: t.size,
16329 triggersBounds: diff.any
16330 }, {
16331 name: 'text-transform',
16332 type: t.textTransform,
16333 triggersBounds: diff.any
16334 }, {
16335 name: 'text-wrap',
16336 type: t.textWrap,
16337 triggersBounds: diff.any
16338 }, {
16339 name: 'text-overflow-wrap',
16340 type: t.textOverflowWrap,
16341 triggersBounds: diff.any
16342 }, {
16343 name: 'text-max-width',
16344 type: t.size,
16345 triggersBounds: diff.any
16346 }, {
16347 name: 'text-outline-width',
16348 type: t.size,
16349 triggersBounds: diff.any
16350 }, {
16351 name: 'line-height',
16352 type: t.positiveNumber,
16353 triggersBounds: diff.any
16354 }];
16355 var commonLabel = [{
16356 name: 'text-valign',
16357 type: t.valign,
16358 triggersBounds: diff.any
16359 }, {
16360 name: 'text-halign',
16361 type: t.halign,
16362 triggersBounds: diff.any
16363 }, {
16364 name: 'color',
16365 type: t.color
16366 }, {
16367 name: 'text-outline-color',
16368 type: t.color
16369 }, {
16370 name: 'text-outline-opacity',
16371 type: t.zeroOneNumber
16372 }, {
16373 name: 'text-background-color',
16374 type: t.color
16375 }, {
16376 name: 'text-background-opacity',
16377 type: t.zeroOneNumber
16378 }, {
16379 name: 'text-background-padding',
16380 type: t.size,
16381 triggersBounds: diff.any
16382 }, {
16383 name: 'text-border-opacity',
16384 type: t.zeroOneNumber
16385 }, {
16386 name: 'text-border-color',
16387 type: t.color
16388 }, {
16389 name: 'text-border-width',
16390 type: t.size,
16391 triggersBounds: diff.any
16392 }, {
16393 name: 'text-border-style',
16394 type: t.borderStyle,
16395 triggersBounds: diff.any
16396 }, {
16397 name: 'text-background-shape',
16398 type: t.textBackgroundShape,
16399 triggersBounds: diff.any
16400 }, {
16401 name: 'text-justification',
16402 type: t.justification
16403 }];
16404 var behavior = [{
16405 name: 'events',
16406 type: t.bool
16407 }, {
16408 name: 'text-events',
16409 type: t.bool
16410 }];
16411 var visibility = [{
16412 name: 'display',
16413 type: t.display,
16414 triggersZOrder: diff.any,
16415 triggersBounds: diff.any,
16416 triggersBoundsOfParallelBeziers: true
16417 }, {
16418 name: 'visibility',
16419 type: t.visibility,
16420 triggersZOrder: diff.any
16421 }, {
16422 name: 'opacity',
16423 type: t.zeroOneNumber,
16424 triggersZOrder: diff.zeroNonZero
16425 }, {
16426 name: 'text-opacity',
16427 type: t.zeroOneNumber
16428 }, {
16429 name: 'min-zoomed-font-size',
16430 type: t.size
16431 }, {
16432 name: 'z-compound-depth',
16433 type: t.zCompoundDepth,
16434 triggersZOrder: diff.any
16435 }, {
16436 name: 'z-index-compare',
16437 type: t.zIndexCompare,
16438 triggersZOrder: diff.any
16439 }, {
16440 name: 'z-index',
16441 type: t.nonNegativeInt,
16442 triggersZOrder: diff.any
16443 }];
16444 var overlay = [{
16445 name: 'overlay-padding',
16446 type: t.size,
16447 triggersBounds: diff.any
16448 }, {
16449 name: 'overlay-color',
16450 type: t.color
16451 }, {
16452 name: 'overlay-opacity',
16453 type: t.zeroOneNumber,
16454 triggersBounds: diff.zeroNonZero
16455 }];
16456 var transition = [{
16457 name: 'transition-property',
16458 type: t.propList
16459 }, {
16460 name: 'transition-duration',
16461 type: t.time
16462 }, {
16463 name: 'transition-delay',
16464 type: t.time
16465 }, {
16466 name: 'transition-timing-function',
16467 type: t.easing
16468 }];
16469
16470 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16471 if (parsedProp.value === 'label') {
16472 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16473 } else {
16474 return parsedProp.pfValue;
16475 }
16476 };
16477
16478 var nodeBody = [{
16479 name: 'height',
16480 type: t.nodeSize,
16481 triggersBounds: diff.any,
16482 hashOverride: nodeSizeHashOverride
16483 }, {
16484 name: 'width',
16485 type: t.nodeSize,
16486 triggersBounds: diff.any,
16487 hashOverride: nodeSizeHashOverride
16488 }, {
16489 name: 'shape',
16490 type: t.nodeShape,
16491 triggersBounds: diff.any
16492 }, {
16493 name: 'shape-polygon-points',
16494 type: t.polygonPointList,
16495 triggersBounds: diff.any
16496 }, {
16497 name: 'background-color',
16498 type: t.color
16499 }, {
16500 name: 'background-fill',
16501 type: t.fill
16502 }, {
16503 name: 'background-opacity',
16504 type: t.zeroOneNumber
16505 }, {
16506 name: 'background-blacken',
16507 type: t.nOneOneNumber
16508 }, {
16509 name: 'background-gradient-stop-colors',
16510 type: t.colors
16511 }, {
16512 name: 'background-gradient-stop-positions',
16513 type: t.percentages
16514 }, {
16515 name: 'background-gradient-direction',
16516 type: t.gradientDirection
16517 }, {
16518 name: 'padding',
16519 type: t.sizeMaybePercent,
16520 triggersBounds: diff.any
16521 }, {
16522 name: 'padding-relative-to',
16523 type: t.paddingRelativeTo,
16524 triggersBounds: diff.any
16525 }, {
16526 name: 'bounds-expansion',
16527 type: t.boundsExpansion,
16528 triggersBounds: diff.any
16529 }];
16530 var nodeBorder = [{
16531 name: 'border-color',
16532 type: t.color
16533 }, {
16534 name: 'border-opacity',
16535 type: t.zeroOneNumber
16536 }, {
16537 name: 'border-width',
16538 type: t.size,
16539 triggersBounds: diff.any
16540 }, {
16541 name: 'border-style',
16542 type: t.borderStyle
16543 }];
16544 var backgroundImage = [{
16545 name: 'background-image',
16546 type: t.urls
16547 }, {
16548 name: 'background-image-crossorigin',
16549 type: t.bgCrossOrigin
16550 }, {
16551 name: 'background-image-opacity',
16552 type: t.zeroOneNumbers
16553 }, {
16554 name: 'background-image-containment',
16555 type: t.bgContainment
16556 }, {
16557 name: 'background-image-smoothing',
16558 type: t.bools
16559 }, {
16560 name: 'background-position-x',
16561 type: t.bgPos
16562 }, {
16563 name: 'background-position-y',
16564 type: t.bgPos
16565 }, {
16566 name: 'background-width-relative-to',
16567 type: t.bgRelativeTo
16568 }, {
16569 name: 'background-height-relative-to',
16570 type: t.bgRelativeTo
16571 }, {
16572 name: 'background-repeat',
16573 type: t.bgRepeat
16574 }, {
16575 name: 'background-fit',
16576 type: t.bgFit
16577 }, {
16578 name: 'background-clip',
16579 type: t.bgClip
16580 }, {
16581 name: 'background-width',
16582 type: t.bgWH
16583 }, {
16584 name: 'background-height',
16585 type: t.bgWH
16586 }, {
16587 name: 'background-offset-x',
16588 type: t.bgPos
16589 }, {
16590 name: 'background-offset-y',
16591 type: t.bgPos
16592 }];
16593 var compound = [{
16594 name: 'position',
16595 type: t.position,
16596 triggersBounds: diff.any
16597 }, {
16598 name: 'compound-sizing-wrt-labels',
16599 type: t.compoundIncludeLabels,
16600 triggersBounds: diff.any
16601 }, {
16602 name: 'min-width',
16603 type: t.size,
16604 triggersBounds: diff.any
16605 }, {
16606 name: 'min-width-bias-left',
16607 type: t.sizeMaybePercent,
16608 triggersBounds: diff.any
16609 }, {
16610 name: 'min-width-bias-right',
16611 type: t.sizeMaybePercent,
16612 triggersBounds: diff.any
16613 }, {
16614 name: 'min-height',
16615 type: t.size,
16616 triggersBounds: diff.any
16617 }, {
16618 name: 'min-height-bias-top',
16619 type: t.sizeMaybePercent,
16620 triggersBounds: diff.any
16621 }, {
16622 name: 'min-height-bias-bottom',
16623 type: t.sizeMaybePercent,
16624 triggersBounds: diff.any
16625 }];
16626 var edgeLine = [{
16627 name: 'line-style',
16628 type: t.lineStyle
16629 }, {
16630 name: 'line-color',
16631 type: t.color
16632 }, {
16633 name: 'line-fill',
16634 type: t.fill
16635 }, {
16636 name: 'line-cap',
16637 type: t.lineCap
16638 }, {
16639 name: 'line-opacity',
16640 type: t.zeroOneNumber
16641 }, {
16642 name: 'line-dash-pattern',
16643 type: t.numbers
16644 }, {
16645 name: 'line-dash-offset',
16646 type: t.number
16647 }, {
16648 name: 'line-gradient-stop-colors',
16649 type: t.colors
16650 }, {
16651 name: 'line-gradient-stop-positions',
16652 type: t.percentages
16653 }, {
16654 name: 'curve-style',
16655 type: t.curveStyle,
16656 triggersBounds: diff.any,
16657 triggersBoundsOfParallelBeziers: true
16658 }, {
16659 name: 'haystack-radius',
16660 type: t.zeroOneNumber,
16661 triggersBounds: diff.any
16662 }, {
16663 name: 'source-endpoint',
16664 type: t.edgeEndpoint,
16665 triggersBounds: diff.any
16666 }, {
16667 name: 'target-endpoint',
16668 type: t.edgeEndpoint,
16669 triggersBounds: diff.any
16670 }, {
16671 name: 'control-point-step-size',
16672 type: t.size,
16673 triggersBounds: diff.any
16674 }, {
16675 name: 'control-point-distances',
16676 type: t.bidirectionalSizes,
16677 triggersBounds: diff.any
16678 }, {
16679 name: 'control-point-weights',
16680 type: t.numbers,
16681 triggersBounds: diff.any
16682 }, {
16683 name: 'segment-distances',
16684 type: t.bidirectionalSizes,
16685 triggersBounds: diff.any
16686 }, {
16687 name: 'segment-weights',
16688 type: t.numbers,
16689 triggersBounds: diff.any
16690 }, {
16691 name: 'taxi-turn',
16692 type: t.bidirectionalSizeMaybePercent,
16693 triggersBounds: diff.any
16694 }, {
16695 name: 'taxi-turn-min-distance',
16696 type: t.size,
16697 triggersBounds: diff.any
16698 }, {
16699 name: 'taxi-direction',
16700 type: t.axisDirection,
16701 triggersBounds: diff.any
16702 }, {
16703 name: 'edge-distances',
16704 type: t.edgeDistances,
16705 triggersBounds: diff.any
16706 }, {
16707 name: 'arrow-scale',
16708 type: t.positiveNumber,
16709 triggersBounds: diff.any
16710 }, {
16711 name: 'loop-direction',
16712 type: t.angle,
16713 triggersBounds: diff.any
16714 }, {
16715 name: 'loop-sweep',
16716 type: t.angle,
16717 triggersBounds: diff.any
16718 }, {
16719 name: 'source-distance-from-node',
16720 type: t.size,
16721 triggersBounds: diff.any
16722 }, {
16723 name: 'target-distance-from-node',
16724 type: t.size,
16725 triggersBounds: diff.any
16726 }];
16727 var ghost = [{
16728 name: 'ghost',
16729 type: t.bool,
16730 triggersBounds: diff.any
16731 }, {
16732 name: 'ghost-offset-x',
16733 type: t.bidirectionalSize,
16734 triggersBounds: diff.any
16735 }, {
16736 name: 'ghost-offset-y',
16737 type: t.bidirectionalSize,
16738 triggersBounds: diff.any
16739 }, {
16740 name: 'ghost-opacity',
16741 type: t.zeroOneNumber
16742 }];
16743 var core = [{
16744 name: 'selection-box-color',
16745 type: t.color
16746 }, {
16747 name: 'selection-box-opacity',
16748 type: t.zeroOneNumber
16749 }, {
16750 name: 'selection-box-border-color',
16751 type: t.color
16752 }, {
16753 name: 'selection-box-border-width',
16754 type: t.size
16755 }, {
16756 name: 'active-bg-color',
16757 type: t.color
16758 }, {
16759 name: 'active-bg-opacity',
16760 type: t.zeroOneNumber
16761 }, {
16762 name: 'active-bg-size',
16763 type: t.size
16764 }, {
16765 name: 'outside-texture-bg-color',
16766 type: t.color
16767 }, {
16768 name: 'outside-texture-bg-opacity',
16769 type: t.zeroOneNumber
16770 }]; // pie backgrounds for nodes
16771
16772 var pie = [];
16773 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16774
16775 pie.push({
16776 name: 'pie-size',
16777 type: t.sizeMaybePercent
16778 });
16779
16780 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16781 pie.push({
16782 name: 'pie-' + i + '-background-color',
16783 type: t.color
16784 });
16785 pie.push({
16786 name: 'pie-' + i + '-background-size',
16787 type: t.percent
16788 });
16789 pie.push({
16790 name: 'pie-' + i + '-background-opacity',
16791 type: t.zeroOneNumber
16792 });
16793 } // edge arrows
16794
16795
16796 var edgeArrow = [];
16797 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16798 [{
16799 name: 'arrow-shape',
16800 type: t.arrowShape,
16801 triggersBounds: diff.any
16802 }, {
16803 name: 'arrow-color',
16804 type: t.color
16805 }, {
16806 name: 'arrow-fill',
16807 type: t.arrowFill
16808 }].forEach(function (prop) {
16809 arrowPrefixes.forEach(function (prefix) {
16810 var name = prefix + '-' + prop.name;
16811 var type = prop.type,
16812 triggersBounds = prop.triggersBounds;
16813 edgeArrow.push({
16814 name: name,
16815 type: type,
16816 triggersBounds: triggersBounds
16817 });
16818 });
16819 }, {});
16820 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16821 var propGroups = styfn$6.propertyGroups = {
16822 // common to all eles
16823 behavior: behavior,
16824 transition: transition,
16825 visibility: visibility,
16826 overlay: overlay,
16827 ghost: ghost,
16828 // labels
16829 commonLabel: commonLabel,
16830 labelDimensions: labelDimensions,
16831 mainLabel: mainLabel,
16832 sourceLabel: sourceLabel,
16833 targetLabel: targetLabel,
16834 // node props
16835 nodeBody: nodeBody,
16836 nodeBorder: nodeBorder,
16837 backgroundImage: backgroundImage,
16838 pie: pie,
16839 compound: compound,
16840 // edge props
16841 edgeLine: edgeLine,
16842 edgeArrow: edgeArrow,
16843 core: core
16844 };
16845 var propGroupNames = styfn$6.propertyGroupNames = {};
16846 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16847 propGroupKeys.forEach(function (key) {
16848 propGroupNames[key] = propGroups[key].map(function (prop) {
16849 return prop.name;
16850 });
16851 propGroups[key].forEach(function (prop) {
16852 return prop.groupKey = key;
16853 });
16854 }); // define aliases
16855
16856 var aliases = styfn$6.aliases = [{
16857 name: 'content',
16858 pointsTo: 'label'
16859 }, {
16860 name: 'control-point-distance',
16861 pointsTo: 'control-point-distances'
16862 }, {
16863 name: 'control-point-weight',
16864 pointsTo: 'control-point-weights'
16865 }, {
16866 name: 'edge-text-rotation',
16867 pointsTo: 'text-rotation'
16868 }, {
16869 name: 'padding-left',
16870 pointsTo: 'padding'
16871 }, {
16872 name: 'padding-right',
16873 pointsTo: 'padding'
16874 }, {
16875 name: 'padding-top',
16876 pointsTo: 'padding'
16877 }, {
16878 name: 'padding-bottom',
16879 pointsTo: 'padding'
16880 }]; // list of property names
16881
16882 styfn$6.propertyNames = props.map(function (p) {
16883 return p.name;
16884 }); // allow access of properties by name ( e.g. style.properties.height )
16885
16886 for (var _i = 0; _i < props.length; _i++) {
16887 var prop = props[_i];
16888 props[prop.name] = prop; // allow lookup by name
16889 } // map aliases
16890
16891
16892 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
16893 var alias = aliases[_i2];
16894 var pointsToProp = props[alias.pointsTo];
16895 var aliasProp = {
16896 name: alias.name,
16897 alias: true,
16898 pointsTo: pointsToProp
16899 }; // add alias prop for parsing
16900
16901 props.push(aliasProp);
16902 props[alias.name] = aliasProp; // allow lookup by name
16903 }
16904})();
16905
16906styfn$6.getDefaultProperty = function (name) {
16907 return this.getDefaultProperties()[name];
16908};
16909
16910styfn$6.getDefaultProperties = function () {
16911 var _p = this._private;
16912
16913 if (_p.defaultProperties != null) {
16914 return _p.defaultProperties;
16915 }
16916
16917 var rawProps = extend({
16918 // core props
16919 'selection-box-color': '#ddd',
16920 'selection-box-opacity': 0.65,
16921 'selection-box-border-color': '#aaa',
16922 'selection-box-border-width': 1,
16923 'active-bg-color': 'black',
16924 'active-bg-opacity': 0.15,
16925 'active-bg-size': 30,
16926 'outside-texture-bg-color': '#000',
16927 'outside-texture-bg-opacity': 0.125,
16928 // common node/edge props
16929 'events': 'yes',
16930 'text-events': 'no',
16931 'text-valign': 'top',
16932 'text-halign': 'center',
16933 'text-justification': 'auto',
16934 'line-height': 1,
16935 'color': '#000',
16936 'text-outline-color': '#000',
16937 'text-outline-width': 0,
16938 'text-outline-opacity': 1,
16939 'text-opacity': 1,
16940 'text-decoration': 'none',
16941 'text-transform': 'none',
16942 'text-wrap': 'none',
16943 'text-overflow-wrap': 'whitespace',
16944 'text-max-width': 9999,
16945 'text-background-color': '#000',
16946 'text-background-opacity': 0,
16947 'text-background-shape': 'rectangle',
16948 'text-background-padding': 0,
16949 'text-border-opacity': 0,
16950 'text-border-width': 0,
16951 'text-border-style': 'solid',
16952 'text-border-color': '#000',
16953 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
16954 'font-style': 'normal',
16955 'font-weight': 'normal',
16956 'font-size': 16,
16957 'min-zoomed-font-size': 0,
16958 'text-rotation': 'none',
16959 'source-text-rotation': 'none',
16960 'target-text-rotation': 'none',
16961 'visibility': 'visible',
16962 'display': 'element',
16963 'opacity': 1,
16964 'z-compound-depth': 'auto',
16965 'z-index-compare': 'auto',
16966 'z-index': 0,
16967 'label': '',
16968 'text-margin-x': 0,
16969 'text-margin-y': 0,
16970 'source-label': '',
16971 'source-text-offset': 0,
16972 'source-text-margin-x': 0,
16973 'source-text-margin-y': 0,
16974 'target-label': '',
16975 'target-text-offset': 0,
16976 'target-text-margin-x': 0,
16977 'target-text-margin-y': 0,
16978 'overlay-opacity': 0,
16979 'overlay-color': '#000',
16980 'overlay-padding': 10,
16981 'transition-property': 'none',
16982 'transition-duration': 0,
16983 'transition-delay': 0,
16984 'transition-timing-function': 'linear',
16985 // node props
16986 'background-blacken': 0,
16987 'background-color': '#999',
16988 'background-fill': 'solid',
16989 'background-opacity': 1,
16990 'background-image': 'none',
16991 'background-image-crossorigin': 'anonymous',
16992 'background-image-opacity': 1,
16993 'background-image-containment': 'inside',
16994 'background-image-smoothing': 'yes',
16995 'background-position-x': '50%',
16996 'background-position-y': '50%',
16997 'background-offset-x': 0,
16998 'background-offset-y': 0,
16999 'background-width-relative-to': 'include-padding',
17000 'background-height-relative-to': 'include-padding',
17001 'background-repeat': 'no-repeat',
17002 'background-fit': 'none',
17003 'background-clip': 'node',
17004 'background-width': 'auto',
17005 'background-height': 'auto',
17006 'border-color': '#000',
17007 'border-opacity': 1,
17008 'border-width': 0,
17009 'border-style': 'solid',
17010 'height': 30,
17011 'width': 30,
17012 'shape': 'ellipse',
17013 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17014 'bounds-expansion': 0,
17015 // node gradient
17016 'background-gradient-direction': 'to-bottom',
17017 'background-gradient-stop-colors': '#999',
17018 'background-gradient-stop-positions': '0%',
17019 // ghost props
17020 'ghost': 'no',
17021 'ghost-offset-y': 0,
17022 'ghost-offset-x': 0,
17023 'ghost-opacity': 0,
17024 // compound props
17025 'padding': 0,
17026 'padding-relative-to': 'width',
17027 'position': 'origin',
17028 'compound-sizing-wrt-labels': 'include',
17029 'min-width': 0,
17030 'min-width-bias-left': 0,
17031 'min-width-bias-right': 0,
17032 'min-height': 0,
17033 'min-height-bias-top': 0,
17034 'min-height-bias-bottom': 0
17035 }, {
17036 // node pie bg
17037 'pie-size': '100%'
17038 }, [{
17039 name: 'pie-{{i}}-background-color',
17040 value: 'black'
17041 }, {
17042 name: 'pie-{{i}}-background-size',
17043 value: '0%'
17044 }, {
17045 name: 'pie-{{i}}-background-opacity',
17046 value: 1
17047 }].reduce(function (css, prop) {
17048 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17049 var name = prop.name.replace('{{i}}', i);
17050 var val = prop.value;
17051 css[name] = val;
17052 }
17053
17054 return css;
17055 }, {}), {
17056 // edge props
17057 'line-style': 'solid',
17058 'line-color': '#999',
17059 'line-fill': 'solid',
17060 'line-cap': 'butt',
17061 'line-opacity': 1,
17062 'line-gradient-stop-colors': '#999',
17063 'line-gradient-stop-positions': '0%',
17064 'control-point-step-size': 40,
17065 'control-point-weights': 0.5,
17066 'segment-weights': 0.5,
17067 'segment-distances': 20,
17068 'taxi-turn': '50%',
17069 'taxi-turn-min-distance': 10,
17070 'taxi-direction': 'auto',
17071 'edge-distances': 'intersection',
17072 'curve-style': 'haystack',
17073 'haystack-radius': 0,
17074 'arrow-scale': 1,
17075 'loop-direction': '-45deg',
17076 'loop-sweep': '-90deg',
17077 'source-distance-from-node': 0,
17078 'target-distance-from-node': 0,
17079 'source-endpoint': 'outside-to-node',
17080 'target-endpoint': 'outside-to-node',
17081 'line-dash-pattern': [6, 3],
17082 'line-dash-offset': 0
17083 }, [{
17084 name: 'arrow-shape',
17085 value: 'none'
17086 }, {
17087 name: 'arrow-color',
17088 value: '#999'
17089 }, {
17090 name: 'arrow-fill',
17091 value: 'filled'
17092 }].reduce(function (css, prop) {
17093 styfn$6.arrowPrefixes.forEach(function (prefix) {
17094 var name = prefix + '-' + prop.name;
17095 var val = prop.value;
17096 css[name] = val;
17097 });
17098 return css;
17099 }, {}));
17100 var parsedProps = {};
17101
17102 for (var i = 0; i < this.properties.length; i++) {
17103 var prop = this.properties[i];
17104
17105 if (prop.pointsTo) {
17106 continue;
17107 }
17108
17109 var name = prop.name;
17110 var val = rawProps[name];
17111 var parsedProp = this.parse(name, val);
17112 parsedProps[name] = parsedProp;
17113 }
17114
17115 _p.defaultProperties = parsedProps;
17116 return _p.defaultProperties;
17117};
17118
17119styfn$6.addDefaultStylesheet = function () {
17120 this.selector(':parent').css({
17121 'shape': 'rectangle',
17122 'padding': 10,
17123 'background-color': '#eee',
17124 'border-color': '#ccc',
17125 'border-width': 1
17126 }).selector('edge').css({
17127 'width': 3
17128 }).selector(':loop').css({
17129 'curve-style': 'bezier'
17130 }).selector('edge:compound').css({
17131 'curve-style': 'bezier',
17132 'source-endpoint': 'outside-to-line',
17133 'target-endpoint': 'outside-to-line'
17134 }).selector(':selected').css({
17135 'background-color': '#0169D9',
17136 'line-color': '#0169D9',
17137 'source-arrow-color': '#0169D9',
17138 'target-arrow-color': '#0169D9',
17139 'mid-source-arrow-color': '#0169D9',
17140 'mid-target-arrow-color': '#0169D9'
17141 }).selector(':parent:selected').css({
17142 'background-color': '#CCE1F9',
17143 'border-color': '#aec8e5'
17144 }).selector(':active').css({
17145 'overlay-color': 'black',
17146 'overlay-padding': 10,
17147 'overlay-opacity': 0.25
17148 });
17149 this.defaultLength = this.length;
17150};
17151
17152var styfn$7 = {}; // a caching layer for property parsing
17153
17154styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17155 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17156
17157 if (fn(value)) {
17158 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17159 }
17160
17161 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17162 var bypassKey = propIsBypass ? 't' : 'f';
17163 var valueKey = '' + value;
17164 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17165 var propCache = self.propCache = self.propCache || [];
17166 var ret;
17167
17168 if (!(ret = propCache[argHash])) {
17169 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17170 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17171 // - mappings can't be shared b/c mappings are per-element
17172
17173
17174 if (propIsBypass || propIsFlat === 'mapping') {
17175 // need a copy since props are mutated later in their lifecycles
17176 ret = copy(ret);
17177
17178 if (ret) {
17179 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17180 }
17181 }
17182
17183 return ret;
17184};
17185
17186styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17187 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17188
17189 if (!prop && value != null) {
17190 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17191 }
17192
17193 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17194 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17195 }
17196
17197 return prop;
17198}; // parse a property; return null on invalid; return parsed property otherwise
17199// fields :
17200// - name : the name of the property
17201// - value : the parsed, native-typed value of the property
17202// - strValue : a string value that represents the property value in valid css
17203// - bypass : true iff the property is a bypass property
17204
17205
17206styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17207 var self = this;
17208 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17209
17210 var property = self.properties[name];
17211 var passedValue = value;
17212 var types = self.types;
17213
17214 if (!property) {
17215 return null;
17216 } // return null on property of unknown name
17217
17218
17219 if (value === undefined) {
17220 return null;
17221 } // can't assign undefined
17222 // the property may be an alias
17223
17224
17225 if (property.alias) {
17226 property = property.pointsTo;
17227 name = property.name;
17228 }
17229
17230 var valueIsString = string(value);
17231
17232 if (valueIsString) {
17233 // trim the value to make parsing easier
17234 value = value.trim();
17235 }
17236
17237 var type = property.type;
17238
17239 if (!type) {
17240 return null;
17241 } // no type, no luck
17242 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17243
17244
17245 if (propIsBypass && (value === '' || value === null)) {
17246 return {
17247 name: name,
17248 value: value,
17249 bypass: true,
17250 deleteBypass: true
17251 };
17252 } // check if value is a function used as a mapper
17253
17254
17255 if (fn(value)) {
17256 return {
17257 name: name,
17258 value: value,
17259 strValue: 'fn',
17260 mapped: types.fn,
17261 bypass: propIsBypass
17262 };
17263 } // check if value is mapped
17264
17265
17266 var data, mapData;
17267
17268 if (!valueIsString || propIsFlat || value.length < 7 || value[1] !== 'a') ; else if (value.length >= 7 && value[0] === 'd' && (data = new RegExp(types.data.regex).exec(value))) {
17269 if (propIsBypass) {
17270 return false;
17271 } // mappers not allowed in bypass
17272
17273
17274 var mapped = types.data;
17275 return {
17276 name: name,
17277 value: data,
17278 strValue: '' + value,
17279 mapped: mapped,
17280 field: data[1],
17281 bypass: propIsBypass
17282 };
17283 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17284 if (propIsBypass) {
17285 return false;
17286 } // mappers not allowed in bypass
17287
17288
17289 if (type.multiple) {
17290 return false;
17291 } // impossible to map to num
17292
17293
17294 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17295
17296 if (!(type.color || type.number)) {
17297 return false;
17298 }
17299
17300 var valueMin = this.parse(name, mapData[4]); // parse to validate
17301
17302 if (!valueMin || valueMin.mapped) {
17303 return false;
17304 } // can't be invalid or mapped
17305
17306
17307 var valueMax = this.parse(name, mapData[5]); // parse to validate
17308
17309 if (!valueMax || valueMax.mapped) {
17310 return false;
17311 } // can't be invalid or mapped
17312 // check if valueMin and valueMax are the same
17313
17314
17315 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17316 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17317 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17318 } else if (type.color) {
17319 var c1 = valueMin.value;
17320 var c2 = valueMax.value;
17321 var same = c1[0] === c2[0] // red
17322 && c1[1] === c2[1] // green
17323 && c1[2] === c2[2] // blue
17324 && ( // optional alpha
17325 c1[3] === c2[3] // same alpha outright
17326 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17327 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17328 );
17329
17330 if (same) {
17331 return false;
17332 } // can't make a mapper without a range
17333
17334 }
17335
17336 return {
17337 name: name,
17338 value: mapData,
17339 strValue: '' + value,
17340 mapped: _mapped,
17341 field: mapData[1],
17342 fieldMin: parseFloat(mapData[2]),
17343 // min & max are numeric
17344 fieldMax: parseFloat(mapData[3]),
17345 valueMin: valueMin.value,
17346 valueMax: valueMax.value,
17347 bypass: propIsBypass
17348 };
17349 }
17350
17351 if (type.multiple && propIsFlat !== 'multiple') {
17352 var vals;
17353
17354 if (valueIsString) {
17355 vals = value.split(/\s+/);
17356 } else if (array(value)) {
17357 vals = value;
17358 } else {
17359 vals = [value];
17360 }
17361
17362 if (type.evenMultiple && vals.length % 2 !== 0) {
17363 return null;
17364 }
17365
17366 var valArr = [];
17367 var unitsArr = [];
17368 var pfValArr = [];
17369 var strVal = '';
17370 var hasEnum = false;
17371
17372 for (var i = 0; i < vals.length; i++) {
17373 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17374 hasEnum = hasEnum || string(p.value);
17375 valArr.push(p.value);
17376 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17377 unitsArr.push(p.units);
17378 strVal += (i > 0 ? ' ' : '') + p.strValue;
17379 }
17380
17381 if (type.validate && !type.validate(valArr, unitsArr)) {
17382 return null;
17383 }
17384
17385 if (type.singleEnum && hasEnum) {
17386 if (valArr.length === 1 && string(valArr[0])) {
17387 return {
17388 name: name,
17389 value: valArr[0],
17390 strValue: valArr[0],
17391 bypass: propIsBypass
17392 };
17393 } else {
17394 return null;
17395 }
17396 }
17397
17398 return {
17399 name: name,
17400 value: valArr,
17401 pfValue: pfValArr,
17402 strValue: strVal,
17403 bypass: propIsBypass,
17404 units: unitsArr
17405 };
17406 } // several types also allow enums
17407
17408
17409 var checkEnums = function checkEnums() {
17410 for (var _i = 0; _i < type.enums.length; _i++) {
17411 var en = type.enums[_i];
17412
17413 if (en === value) {
17414 return {
17415 name: name,
17416 value: value,
17417 strValue: '' + value,
17418 bypass: propIsBypass
17419 };
17420 }
17421 }
17422
17423 return null;
17424 }; // check the type and return the appropriate object
17425
17426
17427 if (type.number) {
17428 var units;
17429 var implicitUnits = 'px'; // not set => px
17430
17431 if (type.units) {
17432 // use specified units if set
17433 units = type.units;
17434 }
17435
17436 if (type.implicitUnits) {
17437 implicitUnits = type.implicitUnits;
17438 }
17439
17440 if (!type.unitless) {
17441 if (valueIsString) {
17442 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17443
17444 if (units) {
17445 unitsRegex = units;
17446 } // only allow explicit units if so set
17447
17448
17449 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17450
17451 if (match) {
17452 value = match[1];
17453 units = match[2] || implicitUnits;
17454 }
17455 } else if (!units || type.implicitUnits) {
17456 units = implicitUnits; // implicitly px if unspecified
17457 }
17458 }
17459
17460 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17461
17462 if (isNaN(value) && type.enums === undefined) {
17463 return null;
17464 } // check if this number type also accepts special keywords in place of numbers
17465 // (i.e. `left`, `auto`, etc)
17466
17467
17468 if (isNaN(value) && type.enums !== undefined) {
17469 value = passedValue;
17470 return checkEnums();
17471 } // check if value must be an integer
17472
17473
17474 if (type.integer && !integer(value)) {
17475 return null;
17476 } // check value is within range
17477
17478
17479 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17480 return null;
17481 }
17482
17483 var ret = {
17484 name: name,
17485 value: value,
17486 strValue: '' + value + (units ? units : ''),
17487 units: units,
17488 bypass: propIsBypass
17489 }; // normalise value in pixels
17490
17491 if (type.unitless || units !== 'px' && units !== 'em') {
17492 ret.pfValue = value;
17493 } else {
17494 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17495 } // normalise value in ms
17496
17497
17498 if (units === 'ms' || units === 's') {
17499 ret.pfValue = units === 'ms' ? value : 1000 * value;
17500 } // normalise value in rad
17501
17502
17503 if (units === 'deg' || units === 'rad') {
17504 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17505 } // normalize value in %
17506
17507
17508 if (units === '%') {
17509 ret.pfValue = value / 100;
17510 }
17511
17512 return ret;
17513 } else if (type.propList) {
17514 var props = [];
17515 var propsStr = '' + value;
17516
17517 if (propsStr === 'none') ; else {
17518 // go over each prop
17519 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17520
17521 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17522 var propName = propsSplit[_i2].trim();
17523
17524 if (self.properties[propName]) {
17525 props.push(propName);
17526 } else {
17527 warn('`' + propName + '` is not a valid property name');
17528 }
17529 }
17530
17531 if (props.length === 0) {
17532 return null;
17533 }
17534 }
17535
17536 return {
17537 name: name,
17538 value: props,
17539 strValue: props.length === 0 ? 'none' : props.join(' '),
17540 bypass: propIsBypass
17541 };
17542 } else if (type.color) {
17543 var tuple = color2tuple(value);
17544
17545 if (!tuple) {
17546 return null;
17547 }
17548
17549 return {
17550 name: name,
17551 value: tuple,
17552 pfValue: tuple,
17553 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17554 // n.b. no spaces b/c of multiple support
17555 bypass: propIsBypass
17556 };
17557 } else if (type.regex || type.regexes) {
17558 // first check enums
17559 if (type.enums) {
17560 var enumProp = checkEnums();
17561
17562 if (enumProp) {
17563 return enumProp;
17564 }
17565 }
17566
17567 var regexes = type.regexes ? type.regexes : [type.regex];
17568
17569 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17570 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17571
17572 var m = regex.exec(value);
17573
17574 if (m) {
17575 // regex matches
17576 return {
17577 name: name,
17578 value: type.singleRegexMatchValue ? m[1] : m,
17579 strValue: '' + value,
17580 bypass: propIsBypass
17581 };
17582 }
17583 }
17584
17585 return null; // didn't match any
17586 } else if (type.string) {
17587 // just return
17588 return {
17589 name: name,
17590 value: '' + value,
17591 strValue: '' + value,
17592 bypass: propIsBypass
17593 };
17594 } else if (type.enums) {
17595 // check enums last because it's a combo type in others
17596 return checkEnums();
17597 } else {
17598 return null; // not a type we can handle
17599 }
17600};
17601
17602var Style = function Style(cy) {
17603 if (!(this instanceof Style)) {
17604 return new Style(cy);
17605 }
17606
17607 if (!core(cy)) {
17608 error('A style must have a core reference');
17609 return;
17610 }
17611
17612 this._private = {
17613 cy: cy,
17614 coreStyle: {}
17615 };
17616 this.length = 0;
17617 this.resetToDefault();
17618};
17619
17620var styfn$8 = Style.prototype;
17621
17622styfn$8.instanceString = function () {
17623 return 'style';
17624}; // remove all contexts
17625
17626
17627styfn$8.clear = function () {
17628 var _p = this._private;
17629 var cy = _p.cy;
17630 var eles = cy.elements();
17631
17632 for (var i = 0; i < this.length; i++) {
17633 this[i] = undefined;
17634 }
17635
17636 this.length = 0;
17637 _p.contextStyles = {};
17638 _p.propDiffs = {};
17639 this.cleanElements(eles, true);
17640 eles.forEach(function (ele) {
17641 var ele_p = ele[0]._private;
17642 ele_p.styleDirty = true;
17643 ele_p.appliedInitStyle = false;
17644 });
17645 return this; // chaining
17646};
17647
17648styfn$8.resetToDefault = function () {
17649 this.clear();
17650 this.addDefaultStylesheet();
17651 return this;
17652}; // builds a style object for the 'core' selector
17653
17654
17655styfn$8.core = function (propName) {
17656 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17657}; // create a new context from the specified selector string and switch to that context
17658
17659
17660styfn$8.selector = function (selectorStr) {
17661 // 'core' is a special case and does not need a selector
17662 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17663 var i = this.length++; // new context means new index
17664
17665 this[i] = {
17666 selector: selector,
17667 properties: [],
17668 mappedProperties: [],
17669 index: i
17670 };
17671 return this; // chaining
17672}; // add one or many css rules to the current context
17673
17674
17675styfn$8.css = function () {
17676 var self = this;
17677 var args = arguments;
17678
17679 if (args.length === 1) {
17680 var map = args[0];
17681
17682 for (var i = 0; i < self.properties.length; i++) {
17683 var prop = self.properties[i];
17684 var mapVal = map[prop.name];
17685
17686 if (mapVal === undefined) {
17687 mapVal = map[dash2camel(prop.name)];
17688 }
17689
17690 if (mapVal !== undefined) {
17691 this.cssRule(prop.name, mapVal);
17692 }
17693 }
17694 } else if (args.length === 2) {
17695 this.cssRule(args[0], args[1]);
17696 } // do nothing if args are invalid
17697
17698
17699 return this; // chaining
17700};
17701
17702styfn$8.style = styfn$8.css; // add a single css rule to the current context
17703
17704styfn$8.cssRule = function (name, value) {
17705 // name-value pair
17706 var property = this.parse(name, value); // add property to current context if valid
17707
17708 if (property) {
17709 var i = this.length - 1;
17710 this[i].properties.push(property);
17711 this[i].properties[property.name] = property; // allow access by name as well
17712
17713 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17714 this._private.hasPie = true;
17715 }
17716
17717 if (property.mapped) {
17718 this[i].mappedProperties.push(property);
17719 } // add to core style if necessary
17720
17721
17722 var currentSelectorIsCore = !this[i].selector;
17723
17724 if (currentSelectorIsCore) {
17725 this._private.coreStyle[property.name] = property;
17726 }
17727 }
17728
17729 return this; // chaining
17730};
17731
17732styfn$8.append = function (style) {
17733 if (stylesheet(style)) {
17734 style.appendToStyle(this);
17735 } else if (array(style)) {
17736 this.appendFromJson(style);
17737 } else if (string(style)) {
17738 this.appendFromString(style);
17739 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17740
17741
17742 return this;
17743}; // static function
17744
17745
17746Style.fromJson = function (cy, json) {
17747 var style = new Style(cy);
17748 style.fromJson(json);
17749 return style;
17750};
17751
17752Style.fromString = function (cy, string) {
17753 return new Style(cy).fromString(string);
17754};
17755
17756[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17757 extend(styfn$8, props);
17758});
17759Style.types = styfn$8.types;
17760Style.properties = styfn$8.properties;
17761Style.propertyGroups = styfn$8.propertyGroups;
17762Style.propertyGroupNames = styfn$8.propertyGroupNames;
17763Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17764
17765var corefn$7 = {
17766 style: function style(newStyle) {
17767 if (newStyle) {
17768 var s = this.setStyle(newStyle);
17769 s.update();
17770 }
17771
17772 return this._private.style;
17773 },
17774 setStyle: function setStyle(style) {
17775 var _p = this._private;
17776
17777 if (stylesheet(style)) {
17778 _p.style = style.generateStyle(this);
17779 } else if (array(style)) {
17780 _p.style = Style.fromJson(this, style);
17781 } else if (string(style)) {
17782 _p.style = Style.fromString(this, style);
17783 } else {
17784 _p.style = Style(this);
17785 }
17786
17787 return _p.style;
17788 },
17789 // e.g. cy.data() changed => recalc ele mappers
17790 updateStyle: function updateStyle() {
17791 this.mutableElements().updateStyle(); // just send to all eles
17792 }
17793};
17794
17795var defaultSelectionType = 'single';
17796var corefn$8 = {
17797 autolock: function autolock(bool) {
17798 if (bool !== undefined) {
17799 this._private.autolock = bool ? true : false;
17800 } else {
17801 return this._private.autolock;
17802 }
17803
17804 return this; // chaining
17805 },
17806 autoungrabify: function autoungrabify(bool) {
17807 if (bool !== undefined) {
17808 this._private.autoungrabify = bool ? true : false;
17809 } else {
17810 return this._private.autoungrabify;
17811 }
17812
17813 return this; // chaining
17814 },
17815 autounselectify: function autounselectify(bool) {
17816 if (bool !== undefined) {
17817 this._private.autounselectify = bool ? true : false;
17818 } else {
17819 return this._private.autounselectify;
17820 }
17821
17822 return this; // chaining
17823 },
17824 selectionType: function selectionType(selType) {
17825 var _p = this._private;
17826
17827 if (_p.selectionType == null) {
17828 _p.selectionType = defaultSelectionType;
17829 }
17830
17831 if (selType !== undefined) {
17832 if (selType === 'additive' || selType === 'single') {
17833 _p.selectionType = selType;
17834 }
17835 } else {
17836 return _p.selectionType;
17837 }
17838
17839 return this;
17840 },
17841 panningEnabled: function panningEnabled(bool) {
17842 if (bool !== undefined) {
17843 this._private.panningEnabled = bool ? true : false;
17844 } else {
17845 return this._private.panningEnabled;
17846 }
17847
17848 return this; // chaining
17849 },
17850 userPanningEnabled: function userPanningEnabled(bool) {
17851 if (bool !== undefined) {
17852 this._private.userPanningEnabled = bool ? true : false;
17853 } else {
17854 return this._private.userPanningEnabled;
17855 }
17856
17857 return this; // chaining
17858 },
17859 zoomingEnabled: function zoomingEnabled(bool) {
17860 if (bool !== undefined) {
17861 this._private.zoomingEnabled = bool ? true : false;
17862 } else {
17863 return this._private.zoomingEnabled;
17864 }
17865
17866 return this; // chaining
17867 },
17868 userZoomingEnabled: function userZoomingEnabled(bool) {
17869 if (bool !== undefined) {
17870 this._private.userZoomingEnabled = bool ? true : false;
17871 } else {
17872 return this._private.userZoomingEnabled;
17873 }
17874
17875 return this; // chaining
17876 },
17877 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17878 if (bool !== undefined) {
17879 this._private.boxSelectionEnabled = bool ? true : false;
17880 } else {
17881 return this._private.boxSelectionEnabled;
17882 }
17883
17884 return this; // chaining
17885 },
17886 pan: function pan() {
17887 var args = arguments;
17888 var pan = this._private.pan;
17889 var dim, val, dims, x, y;
17890
17891 switch (args.length) {
17892 case 0:
17893 // .pan()
17894 return pan;
17895
17896 case 1:
17897 if (string(args[0])) {
17898 // .pan('x')
17899 dim = args[0];
17900 return pan[dim];
17901 } else if (plainObject(args[0])) {
17902 // .pan({ x: 0, y: 100 })
17903 if (!this._private.panningEnabled) {
17904 return this;
17905 }
17906
17907 dims = args[0];
17908 x = dims.x;
17909 y = dims.y;
17910
17911 if (number(x)) {
17912 pan.x = x;
17913 }
17914
17915 if (number(y)) {
17916 pan.y = y;
17917 }
17918
17919 this.emit('pan viewport');
17920 }
17921
17922 break;
17923
17924 case 2:
17925 // .pan('x', 100)
17926 if (!this._private.panningEnabled) {
17927 return this;
17928 }
17929
17930 dim = args[0];
17931 val = args[1];
17932
17933 if ((dim === 'x' || dim === 'y') && number(val)) {
17934 pan[dim] = val;
17935 }
17936
17937 this.emit('pan viewport');
17938 break;
17939 // invalid
17940 }
17941
17942 this.notify('viewport');
17943 return this; // chaining
17944 },
17945 panBy: function panBy(arg0, arg1) {
17946 var args = arguments;
17947 var pan = this._private.pan;
17948 var dim, val, dims, x, y;
17949
17950 if (!this._private.panningEnabled) {
17951 return this;
17952 }
17953
17954 switch (args.length) {
17955 case 1:
17956 if (plainObject(arg0)) {
17957 // .panBy({ x: 0, y: 100 })
17958 dims = args[0];
17959 x = dims.x;
17960 y = dims.y;
17961
17962 if (number(x)) {
17963 pan.x += x;
17964 }
17965
17966 if (number(y)) {
17967 pan.y += y;
17968 }
17969
17970 this.emit('pan viewport');
17971 }
17972
17973 break;
17974
17975 case 2:
17976 // .panBy('x', 100)
17977 dim = arg0;
17978 val = arg1;
17979
17980 if ((dim === 'x' || dim === 'y') && number(val)) {
17981 pan[dim] += val;
17982 }
17983
17984 this.emit('pan viewport');
17985 break;
17986 // invalid
17987 }
17988
17989 this.notify('viewport');
17990 return this; // chaining
17991 },
17992 fit: function fit(elements, padding) {
17993 var viewportState = this.getFitViewport(elements, padding);
17994
17995 if (viewportState) {
17996 var _p = this._private;
17997 _p.zoom = viewportState.zoom;
17998 _p.pan = viewportState.pan;
17999 this.emit('pan zoom viewport');
18000 this.notify('viewport');
18001 }
18002
18003 return this; // chaining
18004 },
18005 getFitViewport: function getFitViewport(elements, padding) {
18006 if (number(elements) && padding === undefined) {
18007 // elements is optional
18008 padding = elements;
18009 elements = undefined;
18010 }
18011
18012 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18013 return;
18014 }
18015
18016 var bb;
18017
18018 if (string(elements)) {
18019 var sel = elements;
18020 elements = this.$(sel);
18021 } else if (boundingBox(elements)) {
18022 // assume bb
18023 var bbe = elements;
18024 bb = {
18025 x1: bbe.x1,
18026 y1: bbe.y1,
18027 x2: bbe.x2,
18028 y2: bbe.y2
18029 };
18030 bb.w = bb.x2 - bb.x1;
18031 bb.h = bb.y2 - bb.y1;
18032 } else if (!elementOrCollection(elements)) {
18033 elements = this.mutableElements();
18034 }
18035
18036 if (elementOrCollection(elements) && elements.empty()) {
18037 return;
18038 } // can't fit to nothing
18039
18040
18041 bb = bb || elements.boundingBox();
18042 var w = this.width();
18043 var h = this.height();
18044 var zoom;
18045 padding = number(padding) ? padding : 0;
18046
18047 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18048 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18049
18050 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18051 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18052 var pan = {
18053 // now pan to middle
18054 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18055 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18056 };
18057 return {
18058 zoom: zoom,
18059 pan: pan
18060 };
18061 }
18062
18063 return;
18064 },
18065 zoomRange: function zoomRange(min, max) {
18066 var _p = this._private;
18067
18068 if (max == null) {
18069 var opts = min;
18070 min = opts.min;
18071 max = opts.max;
18072 }
18073
18074 if (number(min) && number(max) && min <= max) {
18075 _p.minZoom = min;
18076 _p.maxZoom = max;
18077 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18078 _p.minZoom = min;
18079 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18080 _p.maxZoom = max;
18081 }
18082
18083 return this;
18084 },
18085 minZoom: function minZoom(zoom) {
18086 if (zoom === undefined) {
18087 return this._private.minZoom;
18088 } else {
18089 return this.zoomRange({
18090 min: zoom
18091 });
18092 }
18093 },
18094 maxZoom: function maxZoom(zoom) {
18095 if (zoom === undefined) {
18096 return this._private.maxZoom;
18097 } else {
18098 return this.zoomRange({
18099 max: zoom
18100 });
18101 }
18102 },
18103 getZoomedViewport: function getZoomedViewport(params) {
18104 var _p = this._private;
18105 var currentPan = _p.pan;
18106 var currentZoom = _p.zoom;
18107 var pos; // in rendered px
18108
18109 var zoom;
18110 var bail = false;
18111
18112 if (!_p.zoomingEnabled) {
18113 // zooming disabled
18114 bail = true;
18115 }
18116
18117 if (number(params)) {
18118 // then set the zoom
18119 zoom = params;
18120 } else if (plainObject(params)) {
18121 // then zoom about a point
18122 zoom = params.level;
18123
18124 if (params.position != null) {
18125 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18126 } else if (params.renderedPosition != null) {
18127 pos = params.renderedPosition;
18128 }
18129
18130 if (pos != null && !_p.panningEnabled) {
18131 // panning disabled
18132 bail = true;
18133 }
18134 } // crop zoom
18135
18136
18137 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18138 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18139
18140 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18141 return null;
18142 }
18143
18144 if (pos != null) {
18145 // set zoom about position
18146 var pan1 = currentPan;
18147 var zoom1 = currentZoom;
18148 var zoom2 = zoom;
18149 var pan2 = {
18150 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18151 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18152 };
18153 return {
18154 zoomed: true,
18155 panned: true,
18156 zoom: zoom2,
18157 pan: pan2
18158 };
18159 } else {
18160 // just set the zoom
18161 return {
18162 zoomed: true,
18163 panned: false,
18164 zoom: zoom,
18165 pan: currentPan
18166 };
18167 }
18168 },
18169 zoom: function zoom(params) {
18170 if (params === undefined) {
18171 // get
18172 return this._private.zoom;
18173 } else {
18174 // set
18175 var vp = this.getZoomedViewport(params);
18176 var _p = this._private;
18177
18178 if (vp == null || !vp.zoomed) {
18179 return this;
18180 }
18181
18182 _p.zoom = vp.zoom;
18183
18184 if (vp.panned) {
18185 _p.pan.x = vp.pan.x;
18186 _p.pan.y = vp.pan.y;
18187 }
18188
18189 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18190 this.notify('viewport');
18191 return this; // chaining
18192 }
18193 },
18194 viewport: function viewport(opts) {
18195 var _p = this._private;
18196 var zoomDefd = true;
18197 var panDefd = true;
18198 var events = []; // to trigger
18199
18200 var zoomFailed = false;
18201 var panFailed = false;
18202
18203 if (!opts) {
18204 return this;
18205 }
18206
18207 if (!number(opts.zoom)) {
18208 zoomDefd = false;
18209 }
18210
18211 if (!plainObject(opts.pan)) {
18212 panDefd = false;
18213 }
18214
18215 if (!zoomDefd && !panDefd) {
18216 return this;
18217 }
18218
18219 if (zoomDefd) {
18220 var z = opts.zoom;
18221
18222 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18223 zoomFailed = true;
18224 } else {
18225 _p.zoom = z;
18226 events.push('zoom');
18227 }
18228 }
18229
18230 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18231 var p = opts.pan;
18232
18233 if (number(p.x)) {
18234 _p.pan.x = p.x;
18235 panFailed = false;
18236 }
18237
18238 if (number(p.y)) {
18239 _p.pan.y = p.y;
18240 panFailed = false;
18241 }
18242
18243 if (!panFailed) {
18244 events.push('pan');
18245 }
18246 }
18247
18248 if (events.length > 0) {
18249 events.push('viewport');
18250 this.emit(events.join(' '));
18251 this.notify('viewport');
18252 }
18253
18254 return this; // chaining
18255 },
18256 center: function center(elements) {
18257 var pan = this.getCenterPan(elements);
18258
18259 if (pan) {
18260 this._private.pan = pan;
18261 this.emit('pan viewport');
18262 this.notify('viewport');
18263 }
18264
18265 return this; // chaining
18266 },
18267 getCenterPan: function getCenterPan(elements, zoom) {
18268 if (!this._private.panningEnabled) {
18269 return;
18270 }
18271
18272 if (string(elements)) {
18273 var selector = elements;
18274 elements = this.mutableElements().filter(selector);
18275 } else if (!elementOrCollection(elements)) {
18276 elements = this.mutableElements();
18277 }
18278
18279 if (elements.length === 0) {
18280 return;
18281 } // can't centre pan to nothing
18282
18283
18284 var bb = elements.boundingBox();
18285 var w = this.width();
18286 var h = this.height();
18287 zoom = zoom === undefined ? this._private.zoom : zoom;
18288 var pan = {
18289 // middle
18290 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18291 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18292 };
18293 return pan;
18294 },
18295 reset: function reset() {
18296 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18297 return this;
18298 }
18299
18300 this.viewport({
18301 pan: {
18302 x: 0,
18303 y: 0
18304 },
18305 zoom: 1
18306 });
18307 return this; // chaining
18308 },
18309 invalidateSize: function invalidateSize() {
18310 this._private.sizeCache = null;
18311 },
18312 size: function size() {
18313 var _p = this._private;
18314 var container = _p.container;
18315 return _p.sizeCache = _p.sizeCache || (container ? function () {
18316 var style = window$1.getComputedStyle(container);
18317
18318 var val = function val(name) {
18319 return parseFloat(style.getPropertyValue(name));
18320 };
18321
18322 return {
18323 width: container.clientWidth - val('padding-left') - val('padding-right'),
18324 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18325 };
18326 }() : {
18327 // fallback if no container (not 0 b/c can be used for dividing etc)
18328 width: 1,
18329 height: 1
18330 });
18331 },
18332 width: function width() {
18333 return this.size().width;
18334 },
18335 height: function height() {
18336 return this.size().height;
18337 },
18338 extent: function extent() {
18339 var pan = this._private.pan;
18340 var zoom = this._private.zoom;
18341 var rb = this.renderedExtent();
18342 var b = {
18343 x1: (rb.x1 - pan.x) / zoom,
18344 x2: (rb.x2 - pan.x) / zoom,
18345 y1: (rb.y1 - pan.y) / zoom,
18346 y2: (rb.y2 - pan.y) / zoom
18347 };
18348 b.w = b.x2 - b.x1;
18349 b.h = b.y2 - b.y1;
18350 return b;
18351 },
18352 renderedExtent: function renderedExtent() {
18353 var width = this.width();
18354 var height = this.height();
18355 return {
18356 x1: 0,
18357 y1: 0,
18358 x2: width,
18359 y2: height,
18360 w: width,
18361 h: height
18362 };
18363 }
18364}; // aliases
18365
18366corefn$8.centre = corefn$8.center; // backwards compatibility
18367
18368corefn$8.autolockNodes = corefn$8.autolock;
18369corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18370
18371var fn$6 = {
18372 data: define$3.data({
18373 field: 'data',
18374 bindingEvent: 'data',
18375 allowBinding: true,
18376 allowSetting: true,
18377 settingEvent: 'data',
18378 settingTriggersEvent: true,
18379 triggerFnName: 'trigger',
18380 allowGetting: true,
18381 updateStyle: true
18382 }),
18383 removeData: define$3.removeData({
18384 field: 'data',
18385 event: 'data',
18386 triggerFnName: 'trigger',
18387 triggerEvent: true,
18388 updateStyle: true
18389 }),
18390 scratch: define$3.data({
18391 field: 'scratch',
18392 bindingEvent: 'scratch',
18393 allowBinding: true,
18394 allowSetting: true,
18395 settingEvent: 'scratch',
18396 settingTriggersEvent: true,
18397 triggerFnName: 'trigger',
18398 allowGetting: true,
18399 updateStyle: true
18400 }),
18401 removeScratch: define$3.removeData({
18402 field: 'scratch',
18403 event: 'scratch',
18404 triggerFnName: 'trigger',
18405 triggerEvent: true,
18406 updateStyle: true
18407 })
18408}; // aliases
18409
18410fn$6.attr = fn$6.data;
18411fn$6.removeAttr = fn$6.removeData;
18412
18413var Core = function Core(opts) {
18414 var cy = this;
18415 opts = extend({}, opts);
18416 var container = opts.container; // allow for passing a wrapped jquery object
18417 // e.g. cytoscape({ container: $('#cy') })
18418
18419 if (container && !htmlElement(container) && htmlElement(container[0])) {
18420 container = container[0];
18421 }
18422
18423 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18424
18425 reg = reg || {};
18426
18427 if (reg && reg.cy) {
18428 reg.cy.destroy();
18429 reg = {}; // old instance => replace reg completely
18430 }
18431
18432 var readies = reg.readies = reg.readies || [];
18433
18434 if (container) {
18435 container._cyreg = reg;
18436 } // make sure container assoc'd reg points to this cy
18437
18438
18439 reg.cy = cy;
18440 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18441 var options = opts;
18442 options.layout = extend({
18443 name: head ? 'grid' : 'null'
18444 }, options.layout);
18445 options.renderer = extend({
18446 name: head ? 'canvas' : 'null'
18447 }, options.renderer);
18448
18449 var defVal = function defVal(def, val, altVal) {
18450 if (val !== undefined) {
18451 return val;
18452 } else if (altVal !== undefined) {
18453 return altVal;
18454 } else {
18455 return def;
18456 }
18457 };
18458
18459 var _p = this._private = {
18460 container: container,
18461 // html dom ele container
18462 ready: false,
18463 // whether ready has been triggered
18464 options: options,
18465 // cached options
18466 elements: new Collection(this),
18467 // elements in the graph
18468 listeners: [],
18469 // list of listeners
18470 aniEles: new Collection(this),
18471 // elements being animated
18472 data: options.data || {},
18473 // data for the core
18474 scratch: {},
18475 // scratch object for core
18476 layout: null,
18477 renderer: null,
18478 destroyed: false,
18479 // whether destroy was called
18480 notificationsEnabled: true,
18481 // whether notifications are sent to the renderer
18482 minZoom: 1e-50,
18483 maxZoom: 1e50,
18484 zoomingEnabled: defVal(true, options.zoomingEnabled),
18485 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18486 panningEnabled: defVal(true, options.panningEnabled),
18487 userPanningEnabled: defVal(true, options.userPanningEnabled),
18488 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18489 autolock: defVal(false, options.autolock, options.autolockNodes),
18490 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18491 autounselectify: defVal(false, options.autounselectify),
18492 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18493 zoom: number(options.zoom) ? options.zoom : 1,
18494 pan: {
18495 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18496 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18497 },
18498 animation: {
18499 // object for currently-running animations
18500 current: [],
18501 queue: []
18502 },
18503 hasCompoundNodes: false
18504 };
18505
18506 this.createEmitter(); // set selection type
18507
18508 this.selectionType(options.selectionType); // init zoom bounds
18509
18510 this.zoomRange({
18511 min: options.minZoom,
18512 max: options.maxZoom
18513 });
18514
18515 var loadExtData = function loadExtData(extData, next) {
18516 var anyIsPromise = extData.some(promise);
18517
18518 if (anyIsPromise) {
18519 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18520 } else {
18521 next(extData); // exec synchronously for convenience
18522 }
18523 }; // start with the default stylesheet so we have something before loading an external stylesheet
18524
18525
18526 if (_p.styleEnabled) {
18527 cy.setStyle([]);
18528 } // create the renderer
18529
18530
18531 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18532
18533 cy.initRenderer(rendererOptions);
18534
18535 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18536 cy.notifications(false); // remove old elements
18537
18538 var oldEles = cy.mutableElements();
18539
18540 if (oldEles.length > 0) {
18541 oldEles.remove();
18542 }
18543
18544 if (elements != null) {
18545 if (plainObject(elements) || array(elements)) {
18546 cy.add(elements);
18547 }
18548 }
18549
18550 cy.one('layoutready', function (e) {
18551 cy.notifications(true);
18552 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18553
18554 cy.one('load', onload);
18555 cy.emitAndNotify('load');
18556 }).one('layoutstop', function () {
18557 cy.one('done', ondone);
18558 cy.emit('done');
18559 });
18560 var layoutOpts = extend({}, cy._private.options.layout);
18561 layoutOpts.eles = cy.elements();
18562 cy.layout(layoutOpts).run();
18563 };
18564
18565 loadExtData([options.style, options.elements], function (thens) {
18566 var initStyle = thens[0];
18567 var initEles = thens[1]; // init style
18568
18569 if (_p.styleEnabled) {
18570 cy.style().append(initStyle);
18571 } // initial load
18572
18573
18574 setElesAndLayout(initEles, function () {
18575 // onready
18576 cy.startAnimationLoop();
18577 _p.ready = true; // if a ready callback is specified as an option, the bind it
18578
18579 if (fn(options.ready)) {
18580 cy.on('ready', options.ready);
18581 } // bind all the ready handlers registered before creating this instance
18582
18583
18584 for (var i = 0; i < readies.length; i++) {
18585 var fn$1 = readies[i];
18586 cy.on('ready', fn$1);
18587 }
18588
18589 if (reg) {
18590 reg.readies = [];
18591 } // clear b/c we've bound them all and don't want to keep it around in case a new core uses the same div etc
18592
18593
18594 cy.emit('ready');
18595 }, options.done);
18596 });
18597};
18598
18599var corefn$9 = Core.prototype; // short alias
18600
18601extend(corefn$9, {
18602 instanceString: function instanceString() {
18603 return 'core';
18604 },
18605 isReady: function isReady() {
18606 return this._private.ready;
18607 },
18608 destroyed: function destroyed() {
18609 return this._private.destroyed;
18610 },
18611 ready: function ready(fn) {
18612 if (this.isReady()) {
18613 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18614 } else {
18615 this.on('ready', fn);
18616 }
18617
18618 return this;
18619 },
18620 destroy: function destroy() {
18621 var cy = this;
18622 if (cy.destroyed()) return;
18623 cy.stopAnimationLoop();
18624 cy.destroyRenderer();
18625 this.emit('destroy');
18626 cy._private.destroyed = true;
18627 return cy;
18628 },
18629 hasElementWithId: function hasElementWithId(id) {
18630 return this._private.elements.hasElementWithId(id);
18631 },
18632 getElementById: function getElementById(id) {
18633 return this._private.elements.getElementById(id);
18634 },
18635 hasCompoundNodes: function hasCompoundNodes() {
18636 return this._private.hasCompoundNodes;
18637 },
18638 headless: function headless() {
18639 return this._private.renderer.isHeadless();
18640 },
18641 styleEnabled: function styleEnabled() {
18642 return this._private.styleEnabled;
18643 },
18644 addToPool: function addToPool(eles) {
18645 this._private.elements.merge(eles);
18646
18647 return this; // chaining
18648 },
18649 removeFromPool: function removeFromPool(eles) {
18650 this._private.elements.unmerge(eles);
18651
18652 return this;
18653 },
18654 container: function container() {
18655 return this._private.container || null;
18656 },
18657 mount: function mount(container) {
18658 if (container == null) {
18659 return;
18660 }
18661
18662 var cy = this;
18663 var _p = cy._private;
18664 var options = _p.options;
18665
18666 if (!htmlElement(container) && htmlElement(container[0])) {
18667 container = container[0];
18668 }
18669
18670 cy.stopAnimationLoop();
18671 cy.destroyRenderer();
18672 _p.container = container;
18673 _p.styleEnabled = true;
18674 cy.invalidateSize();
18675 cy.initRenderer(extend({}, options, options.renderer, {
18676 // allow custom renderer name to be re-used, otherwise use canvas
18677 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18678 }));
18679 cy.startAnimationLoop();
18680 cy.style(options.style);
18681 cy.emit('mount');
18682 return cy;
18683 },
18684 unmount: function unmount() {
18685 var cy = this;
18686 cy.stopAnimationLoop();
18687 cy.destroyRenderer();
18688 cy.initRenderer({
18689 name: 'null'
18690 });
18691 cy.emit('unmount');
18692 return cy;
18693 },
18694 options: function options() {
18695 return copy(this._private.options);
18696 },
18697 json: function json(obj) {
18698 var cy = this;
18699 var _p = cy._private;
18700 var eles = cy.mutableElements();
18701
18702 var getFreshRef = function getFreshRef(ele) {
18703 return cy.getElementById(ele.id());
18704 };
18705
18706 if (plainObject(obj)) {
18707 // set
18708 cy.startBatch();
18709
18710 if (obj.elements) {
18711 var idInJson = {};
18712
18713 var updateEles = function updateEles(jsons, gr) {
18714 var toAdd = [];
18715 var toMod = [];
18716
18717 for (var i = 0; i < jsons.length; i++) {
18718 var json = jsons[i];
18719
18720 if (!json.data.id) {
18721 warn('cy.json() cannot handle elements without an ID attribute');
18722 continue;
18723 }
18724
18725 var id = '' + json.data.id; // id must be string
18726
18727 var ele = cy.getElementById(id);
18728 idInJson[id] = true;
18729
18730 if (ele.length !== 0) {
18731 // existing element should be updated
18732 toMod.push({
18733 ele: ele,
18734 json: json
18735 });
18736 } else {
18737 // otherwise should be added
18738 if (gr) {
18739 json.group = gr;
18740 toAdd.push(json);
18741 } else {
18742 toAdd.push(json);
18743 }
18744 }
18745 }
18746
18747 cy.add(toAdd);
18748
18749 for (var _i = 0; _i < toMod.length; _i++) {
18750 var _toMod$_i = toMod[_i],
18751 _ele = _toMod$_i.ele,
18752 _json = _toMod$_i.json;
18753
18754 _ele.json(_json);
18755 }
18756 };
18757
18758 if (array(obj.elements)) {
18759 // elements: []
18760 updateEles(obj.elements);
18761 } else {
18762 // elements: { nodes: [], edges: [] }
18763 var grs = ['nodes', 'edges'];
18764
18765 for (var i = 0; i < grs.length; i++) {
18766 var gr = grs[i];
18767 var elements = obj.elements[gr];
18768
18769 if (array(elements)) {
18770 updateEles(elements, gr);
18771 }
18772 }
18773 }
18774
18775 var parentsToRemove = cy.collection();
18776 eles.filter(function (ele) {
18777 return !idInJson[ele.id()];
18778 }).forEach(function (ele) {
18779 if (ele.isParent()) {
18780 parentsToRemove.merge(ele);
18781 } else {
18782 ele.remove();
18783 }
18784 }); // so that children are not removed w/parent
18785
18786 parentsToRemove.forEach(function (ele) {
18787 return ele.children().move({
18788 parent: null
18789 });
18790 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18791
18792 parentsToRemove.forEach(function (ele) {
18793 return getFreshRef(ele).remove();
18794 });
18795 }
18796
18797 if (obj.style) {
18798 cy.style(obj.style);
18799 }
18800
18801 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18802 cy.zoom(obj.zoom);
18803 }
18804
18805 if (obj.pan) {
18806 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18807 cy.pan(obj.pan);
18808 }
18809 }
18810
18811 if (obj.data) {
18812 cy.data(obj.data);
18813 }
18814
18815 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
18816
18817 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18818 var f = fields[_i2];
18819
18820 if (obj[f] != null) {
18821 cy[f](obj[f]);
18822 }
18823 }
18824
18825 cy.endBatch();
18826 return this; // chaining
18827 } else {
18828 // get
18829 var flat = !!obj;
18830 var json = {};
18831
18832 if (flat) {
18833 json.elements = this.elements().map(function (ele) {
18834 return ele.json();
18835 });
18836 } else {
18837 json.elements = {};
18838 eles.forEach(function (ele) {
18839 var group = ele.group();
18840
18841 if (!json.elements[group]) {
18842 json.elements[group] = [];
18843 }
18844
18845 json.elements[group].push(ele.json());
18846 });
18847 }
18848
18849 if (this._private.styleEnabled) {
18850 json.style = cy.style().json();
18851 }
18852
18853 json.data = copy(cy.data());
18854 var options = _p.options;
18855 json.zoomingEnabled = _p.zoomingEnabled;
18856 json.userZoomingEnabled = _p.userZoomingEnabled;
18857 json.zoom = _p.zoom;
18858 json.minZoom = _p.minZoom;
18859 json.maxZoom = _p.maxZoom;
18860 json.panningEnabled = _p.panningEnabled;
18861 json.userPanningEnabled = _p.userPanningEnabled;
18862 json.pan = copy(_p.pan);
18863 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18864 json.renderer = copy(options.renderer);
18865 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18866 json.textureOnViewport = options.textureOnViewport;
18867 json.wheelSensitivity = options.wheelSensitivity;
18868 json.motionBlur = options.motionBlur;
18869 return json;
18870 }
18871 }
18872});
18873corefn$9.$id = corefn$9.getElementById;
18874[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18875 extend(corefn$9, props);
18876});
18877
18878/* eslint-disable no-unused-vars */
18879
18880var defaults$9 = {
18881 fit: true,
18882 // whether to fit the viewport to the graph
18883 directed: false,
18884 // whether the tree is directed downwards (or edges can point in any direction if false)
18885 padding: 30,
18886 // padding on fit
18887 circle: false,
18888 // put depths in concentric circles if true, put depths top down if false
18889 grid: false,
18890 // whether to create an even grid into which the DAG is placed (circle:false only)
18891 spacingFactor: 1.75,
18892 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
18893 boundingBox: undefined,
18894 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
18895 avoidOverlap: true,
18896 // prevents node overlap, may overflow boundingBox if not enough space
18897 nodeDimensionsIncludeLabels: false,
18898 // Excludes the label when calculating node bounding boxes for the layout algorithm
18899 roots: undefined,
18900 // the roots of the trees
18901 maximal: false,
18902 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
18903 animate: false,
18904 // whether to transition the node positions
18905 animationDuration: 500,
18906 // duration of animation in ms if enabled
18907 animationEasing: undefined,
18908 // easing of animation if enabled,
18909 animateFilter: function animateFilter(node, i) {
18910 return true;
18911 },
18912 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
18913 ready: undefined,
18914 // callback on layoutready
18915 stop: undefined,
18916 // callback on layoutstop
18917 transform: function transform(node, position) {
18918 return position;
18919 } // transform a given node position. Useful for changing flow direction in discrete layouts
18920
18921};
18922/* eslint-enable */
18923
18924var getInfo = function getInfo(ele) {
18925 return ele.scratch('breadthfirst');
18926};
18927
18928var setInfo = function setInfo(ele, obj) {
18929 return ele.scratch('breadthfirst', obj);
18930};
18931
18932function BreadthFirstLayout(options) {
18933 this.options = extend({}, defaults$9, options);
18934}
18935
18936BreadthFirstLayout.prototype.run = function () {
18937 var params = this.options;
18938 var options = params;
18939 var cy = params.cy;
18940 var eles = options.eles;
18941 var nodes = eles.nodes().filter(function (n) {
18942 return !n.isParent();
18943 });
18944 var graph = eles;
18945 var directed = options.directed;
18946 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
18947
18948 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
18949 x1: 0,
18950 y1: 0,
18951 w: cy.width(),
18952 h: cy.height()
18953 });
18954 var roots;
18955
18956 if (elementOrCollection(options.roots)) {
18957 roots = options.roots;
18958 } else if (array(options.roots)) {
18959 var rootsArray = [];
18960
18961 for (var i = 0; i < options.roots.length; i++) {
18962 var id = options.roots[i];
18963 var ele = cy.getElementById(id);
18964 rootsArray.push(ele);
18965 }
18966
18967 roots = cy.collection(rootsArray);
18968 } else if (string(options.roots)) {
18969 roots = cy.$(options.roots);
18970 } else {
18971 if (directed) {
18972 roots = nodes.roots();
18973 } else {
18974 var components = eles.components();
18975 roots = cy.collection();
18976
18977 var _loop = function _loop(_i) {
18978 var comp = components[_i];
18979 var maxDegree = comp.maxDegree(false);
18980 var compRoots = comp.filter(function (ele) {
18981 return ele.degree(false) === maxDegree;
18982 });
18983 roots = roots.add(compRoots);
18984 };
18985
18986 for (var _i = 0; _i < components.length; _i++) {
18987 _loop(_i);
18988 }
18989 }
18990 }
18991
18992 var depths = [];
18993 var foundByBfs = {};
18994
18995 var addToDepth = function addToDepth(ele, d) {
18996 if (depths[d] == null) {
18997 depths[d] = [];
18998 }
18999
19000 var i = depths[d].length;
19001 depths[d].push(ele);
19002 setInfo(ele, {
19003 index: i,
19004 depth: d
19005 });
19006 };
19007
19008 var changeDepth = function changeDepth(ele, newDepth) {
19009 var _getInfo = getInfo(ele),
19010 depth = _getInfo.depth,
19011 index = _getInfo.index;
19012
19013 depths[depth][index] = null;
19014 addToDepth(ele, newDepth);
19015 }; // find the depths of the nodes
19016
19017
19018 graph.bfs({
19019 roots: roots,
19020 directed: options.directed,
19021 visit: function visit(node, edge, pNode, i, depth) {
19022 var ele = node[0];
19023 var id = ele.id();
19024 addToDepth(ele, depth);
19025 foundByBfs[id] = true;
19026 }
19027 }); // check for nodes not found by bfs
19028
19029 var orphanNodes = [];
19030
19031 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19032 var _ele = nodes[_i2];
19033
19034 if (foundByBfs[_ele.id()]) {
19035 continue;
19036 } else {
19037 orphanNodes.push(_ele);
19038 }
19039 } // assign the nodes a depth and index
19040
19041
19042 var assignDepthsAt = function assignDepthsAt(i) {
19043 var eles = depths[i];
19044
19045 for (var j = 0; j < eles.length; j++) {
19046 var _ele2 = eles[j];
19047
19048 if (_ele2 == null) {
19049 eles.splice(j, 1);
19050 j--;
19051 continue;
19052 }
19053
19054 setInfo(_ele2, {
19055 depth: i,
19056 index: j
19057 });
19058 }
19059 };
19060
19061 var assignDepths = function assignDepths() {
19062 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19063 assignDepthsAt(_i3);
19064 }
19065 };
19066
19067 var adjustMaximally = function adjustMaximally(ele, shifted) {
19068 var eInfo = getInfo(ele);
19069 var incomers = ele.incomers().filter(function (el) {
19070 return el.isNode() && eles.has(el);
19071 });
19072 var maxDepth = -1;
19073 var id = ele.id();
19074
19075 for (var k = 0; k < incomers.length; k++) {
19076 var incmr = incomers[k];
19077 var iInfo = getInfo(incmr);
19078 maxDepth = Math.max(maxDepth, iInfo.depth);
19079 }
19080
19081 if (eInfo.depth <= maxDepth) {
19082 if (shifted[id]) {
19083 return null;
19084 }
19085
19086 changeDepth(ele, maxDepth + 1);
19087 shifted[id] = true;
19088 return true;
19089 }
19090
19091 return false;
19092 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19093
19094
19095 if (directed && maximal) {
19096 var Q = [];
19097 var shifted = {};
19098
19099 var enqueue = function enqueue(n) {
19100 return Q.push(n);
19101 };
19102
19103 var dequeue = function dequeue() {
19104 return Q.shift();
19105 };
19106
19107 nodes.forEach(function (n) {
19108 return Q.push(n);
19109 });
19110
19111 while (Q.length > 0) {
19112 var _ele3 = dequeue();
19113
19114 var didShift = adjustMaximally(_ele3, shifted);
19115
19116 if (didShift) {
19117 _ele3.outgoers().filter(function (el) {
19118 return el.isNode() && eles.has(el);
19119 }).forEach(enqueue);
19120 } else if (didShift === null) {
19121 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19122 break; // exit on failure
19123 }
19124 }
19125 }
19126
19127 assignDepths(); // clear holes
19128 // find min distance we need to leave between nodes
19129
19130 var minDistance = 0;
19131
19132 if (options.avoidOverlap) {
19133 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19134 var n = nodes[_i4];
19135 var nbb = n.layoutDimensions(options);
19136 var w = nbb.w;
19137 var h = nbb.h;
19138 minDistance = Math.max(minDistance, w, h);
19139 }
19140 } // get the weighted percent for an element based on its connectivity to other levels
19141
19142
19143 var cachedWeightedPercent = {};
19144
19145 var getWeightedPercent = function getWeightedPercent(ele) {
19146 if (cachedWeightedPercent[ele.id()]) {
19147 return cachedWeightedPercent[ele.id()];
19148 }
19149
19150 var eleDepth = getInfo(ele).depth;
19151 var neighbors = ele.neighborhood();
19152 var percent = 0;
19153 var samples = 0;
19154
19155 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19156 var neighbor = neighbors[_i5];
19157
19158 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19159 continue;
19160 }
19161
19162 var bf = getInfo(neighbor);
19163
19164 if (bf == null) {
19165 continue;
19166 }
19167
19168 var index = bf.index;
19169 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19170
19171 if (index == null || depth == null) {
19172 continue;
19173 }
19174
19175 var nDepth = depths[depth].length;
19176
19177 if (depth < eleDepth) {
19178 // only get influenced by elements above
19179 percent += index / nDepth;
19180 samples++;
19181 }
19182 }
19183
19184 samples = Math.max(1, samples);
19185 percent = percent / samples;
19186
19187 if (samples === 0) {
19188 // put lone nodes at the start
19189 percent = 0;
19190 }
19191
19192 cachedWeightedPercent[ele.id()] = percent;
19193 return percent;
19194 }; // rearrange the indices in each depth level based on connectivity
19195
19196
19197 var sortFn = function sortFn(a, b) {
19198 var apct = getWeightedPercent(a);
19199 var bpct = getWeightedPercent(b);
19200 var diff = apct - bpct;
19201
19202 if (diff === 0) {
19203 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19204 } else {
19205 return diff;
19206 }
19207 }; // sort each level to make connected nodes closer
19208
19209
19210 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19211 depths[_i6].sort(sortFn);
19212
19213 assignDepthsAt(_i6);
19214 } // assign orphan nodes to a new top-level depth
19215
19216
19217 var orphanDepth = [];
19218
19219 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19220 orphanDepth.push(orphanNodes[_i7]);
19221 }
19222
19223 depths.unshift(orphanDepth);
19224 assignDepths();
19225 var biggestDepthSize = 0;
19226
19227 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19228 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19229 }
19230
19231 var center = {
19232 x: bb.x1 + bb.w / 2,
19233 y: bb.x1 + bb.h / 2
19234 };
19235 var maxDepthSize = depths.reduce(function (max, eles) {
19236 return Math.max(max, eles.length);
19237 }, 0);
19238
19239 var getPosition = function getPosition(ele) {
19240 var _getInfo2 = getInfo(ele),
19241 depth = _getInfo2.depth,
19242 index = _getInfo2.index;
19243
19244 var depthSize = depths[depth].length;
19245 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19246 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19247 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19248 radiusStepSize = Math.max(radiusStepSize, minDistance);
19249
19250 if (!options.circle) {
19251 var epos = {
19252 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19253 y: (depth + 1) * distanceY
19254 };
19255 return epos;
19256 } else {
19257 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19258 var theta = 2 * Math.PI / depths[depth].length * index;
19259
19260 if (depth === 0 && depths[0].length === 1) {
19261 radius = 1;
19262 }
19263
19264 return {
19265 x: center.x + radius * Math.cos(theta),
19266 y: center.y + radius * Math.sin(theta)
19267 };
19268 }
19269 };
19270
19271 eles.nodes().layoutPositions(this, options, getPosition);
19272 return this; // chaining
19273};
19274
19275var defaults$a = {
19276 fit: true,
19277 // whether to fit the viewport to the graph
19278 padding: 30,
19279 // the padding on fit
19280 boundingBox: undefined,
19281 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19282 avoidOverlap: true,
19283 // prevents node overlap, may overflow boundingBox and radius if not enough space
19284 nodeDimensionsIncludeLabels: false,
19285 // Excludes the label when calculating node bounding boxes for the layout algorithm
19286 spacingFactor: undefined,
19287 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19288 radius: undefined,
19289 // the radius of the circle
19290 startAngle: 3 / 2 * Math.PI,
19291 // where nodes start in radians
19292 sweep: undefined,
19293 // how many radians should be between the first and last node (defaults to full circle)
19294 clockwise: true,
19295 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19296 sort: undefined,
19297 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19298 animate: false,
19299 // whether to transition the node positions
19300 animationDuration: 500,
19301 // duration of animation in ms if enabled
19302 animationEasing: undefined,
19303 // easing of animation if enabled
19304 animateFilter: function animateFilter(node, i) {
19305 return true;
19306 },
19307 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
19308 ready: undefined,
19309 // callback on layoutready
19310 stop: undefined,
19311 // callback on layoutstop
19312 transform: function transform(node, position) {
19313 return position;
19314 } // transform a given node position. Useful for changing flow direction in discrete layouts
19315
19316};
19317
19318function CircleLayout(options) {
19319 this.options = extend({}, defaults$a, options);
19320}
19321
19322CircleLayout.prototype.run = function () {
19323 var params = this.options;
19324 var options = params;
19325 var cy = params.cy;
19326 var eles = options.eles;
19327 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19328 var nodes = eles.nodes().not(':parent');
19329
19330 if (options.sort) {
19331 nodes = nodes.sort(options.sort);
19332 }
19333
19334 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19335 x1: 0,
19336 y1: 0,
19337 w: cy.width(),
19338 h: cy.height()
19339 });
19340 var center = {
19341 x: bb.x1 + bb.w / 2,
19342 y: bb.y1 + bb.h / 2
19343 };
19344 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19345 var dTheta = sweep / Math.max(1, nodes.length - 1);
19346 var r;
19347 var minDistance = 0;
19348
19349 for (var i = 0; i < nodes.length; i++) {
19350 var n = nodes[i];
19351 var nbb = n.layoutDimensions(options);
19352 var w = nbb.w;
19353 var h = nbb.h;
19354 minDistance = Math.max(minDistance, w, h);
19355 }
19356
19357 if (number(options.radius)) {
19358 r = options.radius;
19359 } else if (nodes.length <= 1) {
19360 r = 0;
19361 } else {
19362 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19363 } // calculate the radius
19364
19365
19366 if (nodes.length > 1 && options.avoidOverlap) {
19367 // but only if more than one node (can't overlap)
19368 minDistance *= 1.75; // just to have some nice spacing
19369
19370 var dcos = Math.cos(dTheta) - Math.cos(0);
19371 var dsin = Math.sin(dTheta) - Math.sin(0);
19372 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19373
19374 r = Math.max(rMin, r);
19375 }
19376
19377 var getPos = function getPos(ele, i) {
19378 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19379 var rx = r * Math.cos(theta);
19380 var ry = r * Math.sin(theta);
19381 var pos = {
19382 x: center.x + rx,
19383 y: center.y + ry
19384 };
19385 return pos;
19386 };
19387
19388 eles.nodes().layoutPositions(this, options, getPos);
19389 return this; // chaining
19390};
19391
19392var defaults$b = {
19393 fit: true,
19394 // whether to fit the viewport to the graph
19395 padding: 30,
19396 // the padding on fit
19397 startAngle: 3 / 2 * Math.PI,
19398 // where nodes start in radians
19399 sweep: undefined,
19400 // how many radians should be between the first and last node (defaults to full circle)
19401 clockwise: true,
19402 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19403 equidistant: false,
19404 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19405 minNodeSpacing: 10,
19406 // min spacing between outside of nodes (used for radius adjustment)
19407 boundingBox: undefined,
19408 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19409 avoidOverlap: true,
19410 // prevents node overlap, may overflow boundingBox if not enough space
19411 nodeDimensionsIncludeLabels: false,
19412 // Excludes the label when calculating node bounding boxes for the layout algorithm
19413 height: undefined,
19414 // height of layout area (overrides container height)
19415 width: undefined,
19416 // width of layout area (overrides container width)
19417 spacingFactor: undefined,
19418 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19419 concentric: function concentric(node) {
19420 // returns numeric value for each node, placing higher nodes in levels towards the centre
19421 return node.degree();
19422 },
19423 levelWidth: function levelWidth(nodes) {
19424 // the variation of concentric values in each level
19425 return nodes.maxDegree() / 4;
19426 },
19427 animate: false,
19428 // whether to transition the node positions
19429 animationDuration: 500,
19430 // duration of animation in ms if enabled
19431 animationEasing: undefined,
19432 // easing of animation if enabled
19433 animateFilter: function animateFilter(node, i) {
19434 return true;
19435 },
19436 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
19437 ready: undefined,
19438 // callback on layoutready
19439 stop: undefined,
19440 // callback on layoutstop
19441 transform: function transform(node, position) {
19442 return position;
19443 } // transform a given node position. Useful for changing flow direction in discrete layouts
19444
19445};
19446
19447function ConcentricLayout(options) {
19448 this.options = extend({}, defaults$b, options);
19449}
19450
19451ConcentricLayout.prototype.run = function () {
19452 var params = this.options;
19453 var options = params;
19454 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19455 var cy = params.cy;
19456 var eles = options.eles;
19457 var nodes = eles.nodes().not(':parent');
19458 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19459 x1: 0,
19460 y1: 0,
19461 w: cy.width(),
19462 h: cy.height()
19463 });
19464 var center = {
19465 x: bb.x1 + bb.w / 2,
19466 y: bb.y1 + bb.h / 2
19467 };
19468 var nodeValues = []; // { node, value }
19469
19470 var maxNodeSize = 0;
19471
19472 for (var i = 0; i < nodes.length; i++) {
19473 var node = nodes[i];
19474 var value = void 0; // calculate the node value
19475
19476 value = options.concentric(node);
19477 nodeValues.push({
19478 value: value,
19479 node: node
19480 }); // for style mapping
19481
19482 node._private.scratch.concentric = value;
19483 } // in case we used the `concentric` in style
19484
19485
19486 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19487
19488 for (var _i = 0; _i < nodes.length; _i++) {
19489 var _node = nodes[_i];
19490
19491 var nbb = _node.layoutDimensions(options);
19492
19493 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19494 } // sort node values in descreasing order
19495
19496
19497 nodeValues.sort(function (a, b) {
19498 return b.value - a.value;
19499 });
19500 var levelWidth = options.levelWidth(nodes); // put the values into levels
19501
19502 var levels = [[]];
19503 var currentLevel = levels[0];
19504
19505 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19506 var val = nodeValues[_i2];
19507
19508 if (currentLevel.length > 0) {
19509 var diff = Math.abs(currentLevel[0].value - val.value);
19510
19511 if (diff >= levelWidth) {
19512 currentLevel = [];
19513 levels.push(currentLevel);
19514 }
19515 }
19516
19517 currentLevel.push(val);
19518 } // create positions from levels
19519
19520
19521 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19522
19523 if (!options.avoidOverlap) {
19524 // then strictly constrain to bb
19525 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19526 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19527 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19528 minDist = Math.min(minDist, rStep);
19529 } // find the metrics for each level
19530
19531
19532 var r = 0;
19533
19534 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19535 var level = levels[_i3];
19536 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19537 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19538
19539 if (level.length > 1 && options.avoidOverlap) {
19540 // but only if more than one node (can't overlap)
19541 var dcos = Math.cos(dTheta) - Math.cos(0);
19542 var dsin = Math.sin(dTheta) - Math.sin(0);
19543 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19544
19545 r = Math.max(rMin, r);
19546 }
19547
19548 level.r = r;
19549 r += minDist;
19550 }
19551
19552 if (options.equidistant) {
19553 var rDeltaMax = 0;
19554 var _r = 0;
19555
19556 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19557 var _level = levels[_i4];
19558 var rDelta = _level.r - _r;
19559 rDeltaMax = Math.max(rDeltaMax, rDelta);
19560 }
19561
19562 _r = 0;
19563
19564 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19565 var _level2 = levels[_i5];
19566
19567 if (_i5 === 0) {
19568 _r = _level2.r;
19569 }
19570
19571 _level2.r = _r;
19572 _r += rDeltaMax;
19573 }
19574 } // calculate the node positions
19575
19576
19577 var pos = {}; // id => position
19578
19579 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19580 var _level3 = levels[_i6];
19581 var _dTheta = _level3.dTheta;
19582 var _r2 = _level3.r;
19583
19584 for (var j = 0; j < _level3.length; j++) {
19585 var _val = _level3[j];
19586 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19587 var p = {
19588 x: center.x + _r2 * Math.cos(theta),
19589 y: center.y + _r2 * Math.sin(theta)
19590 };
19591 pos[_val.node.id()] = p;
19592 }
19593 } // position the nodes
19594
19595
19596 eles.nodes().layoutPositions(this, options, function (ele) {
19597 var id = ele.id();
19598 return pos[id];
19599 });
19600 return this; // chaining
19601};
19602
19603/*
19604The CoSE layout was written by Gerardo Huck.
19605https://www.linkedin.com/in/gerardohuck/
19606
19607Based on the following article:
19608http://dl.acm.org/citation.cfm?id=1498047
19609
19610Modifications tracked on Github.
19611*/
19612var DEBUG;
19613/**
19614 * @brief : default layout options
19615 */
19616
19617var defaults$c = {
19618 // Called on `layoutready`
19619 ready: function ready() {},
19620 // Called on `layoutstop`
19621 stop: function stop() {},
19622 // Whether to animate while running the layout
19623 // true : Animate continuously as the layout is running
19624 // false : Just show the end result
19625 // 'end' : Animate with the end result, from the initial positions to the end positions
19626 animate: true,
19627 // Easing of the animation for animate:'end'
19628 animationEasing: undefined,
19629 // The duration of the animation for animate:'end'
19630 animationDuration: undefined,
19631 // A function that determines whether the node should be animated
19632 // All nodes animated by default on animate enabled
19633 // Non-animated nodes are positioned immediately when the layout starts
19634 animateFilter: function animateFilter(node, i) {
19635 return true;
19636 },
19637 // The layout animates only after this many milliseconds for animate:true
19638 // (prevents flashing on fast runs)
19639 animationThreshold: 250,
19640 // Number of iterations between consecutive screen positions update
19641 refresh: 20,
19642 // Whether to fit the network view after when done
19643 fit: true,
19644 // Padding on fit
19645 padding: 30,
19646 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19647 boundingBox: undefined,
19648 // Excludes the label when calculating node bounding boxes for the layout algorithm
19649 nodeDimensionsIncludeLabels: false,
19650 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19651 randomize: false,
19652 // Extra spacing between components in non-compound graphs
19653 componentSpacing: 40,
19654 // Node repulsion (non overlapping) multiplier
19655 nodeRepulsion: function nodeRepulsion(node) {
19656 return 2048;
19657 },
19658 // Node repulsion (overlapping) multiplier
19659 nodeOverlap: 4,
19660 // Ideal edge (non nested) length
19661 idealEdgeLength: function idealEdgeLength(edge) {
19662 return 32;
19663 },
19664 // Divisor to compute edge forces
19665 edgeElasticity: function edgeElasticity(edge) {
19666 return 32;
19667 },
19668 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19669 nestingFactor: 1.2,
19670 // Gravity force (constant)
19671 gravity: 1,
19672 // Maximum number of iterations to perform
19673 numIter: 1000,
19674 // Initial temperature (maximum node displacement)
19675 initialTemp: 1000,
19676 // Cooling factor (how the temperature is reduced between consecutive iterations
19677 coolingFactor: 0.99,
19678 // Lower temperature threshold (below this point the layout will end)
19679 minTemp: 1.0
19680};
19681/**
19682 * @brief : constructor
19683 * @arg options : object containing layout options
19684 */
19685
19686function CoseLayout(options) {
19687 this.options = extend({}, defaults$c, options);
19688 this.options.layout = this;
19689}
19690/**
19691 * @brief : runs the layout
19692 */
19693
19694
19695CoseLayout.prototype.run = function () {
19696 var options = this.options;
19697 var cy = options.cy;
19698 var layout = this;
19699 layout.stopped = false;
19700
19701 if (options.animate === true || options.animate === false) {
19702 layout.emit({
19703 type: 'layoutstart',
19704 layout: layout
19705 });
19706 } // Set DEBUG - Global variable
19707
19708
19709 if (true === options.debug) {
19710 DEBUG = true;
19711 } else {
19712 DEBUG = false;
19713 } // Initialize layout info
19714
19715
19716 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19717
19718 if (DEBUG) {
19719 printLayoutInfo(layoutInfo);
19720 } // If required, randomize node positions
19721
19722
19723 if (options.randomize) {
19724 randomizePositions(layoutInfo);
19725 }
19726
19727 var startTime = performanceNow();
19728
19729 var refresh = function refresh() {
19730 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19731
19732 if (true === options.fit) {
19733 cy.fit(options.padding);
19734 }
19735 };
19736
19737 var mainLoop = function mainLoop(i) {
19738 if (layout.stopped || i >= options.numIter) {
19739 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19740 return false;
19741 } // Do one step in the phisical simulation
19742
19743
19744 step$1(layoutInfo, options); // Update temperature
19745
19746 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19747
19748 if (layoutInfo.temperature < options.minTemp) {
19749 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19750 return false;
19751 }
19752
19753 return true;
19754 };
19755
19756 var done = function done() {
19757 if (options.animate === true || options.animate === false) {
19758 refresh(); // Layout has finished
19759
19760 layout.one('layoutstop', options.stop);
19761 layout.emit({
19762 type: 'layoutstop',
19763 layout: layout
19764 });
19765 } else {
19766 var nodes = options.eles.nodes();
19767 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19768 nodes.layoutPositions(layout, options, getScaledPos);
19769 }
19770 };
19771
19772 var i = 0;
19773 var loopRet = true;
19774
19775 if (options.animate === true) {
19776 var frame = function frame() {
19777 var f = 0;
19778
19779 while (loopRet && f < options.refresh) {
19780 loopRet = mainLoop(i);
19781 i++;
19782 f++;
19783 }
19784
19785 if (!loopRet) {
19786 // it's done
19787 separateComponents(layoutInfo, options);
19788 done();
19789 } else {
19790 var now = performanceNow();
19791
19792 if (now - startTime >= options.animationThreshold) {
19793 refresh();
19794 }
19795
19796 requestAnimationFrame(frame);
19797 }
19798 };
19799
19800 frame();
19801 } else {
19802 while (loopRet) {
19803 loopRet = mainLoop(i);
19804 i++;
19805 }
19806
19807 separateComponents(layoutInfo, options);
19808 done();
19809 }
19810
19811 return this; // chaining
19812};
19813/**
19814 * @brief : called on continuous layouts to stop them before they finish
19815 */
19816
19817
19818CoseLayout.prototype.stop = function () {
19819 this.stopped = true;
19820
19821 if (this.thread) {
19822 this.thread.stop();
19823 }
19824
19825 this.emit('layoutstop');
19826 return this; // chaining
19827};
19828
19829CoseLayout.prototype.destroy = function () {
19830 if (this.thread) {
19831 this.thread.stop();
19832 }
19833
19834 return this; // chaining
19835};
19836/**
19837 * @brief : Creates an object which is contains all the data
19838 * used in the layout process
19839 * @arg cy : cytoscape.js object
19840 * @return : layoutInfo object initialized
19841 */
19842
19843
19844var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19845 // Shortcut
19846 var edges = options.eles.edges();
19847 var nodes = options.eles.nodes();
19848 var layoutInfo = {
19849 isCompound: cy.hasCompoundNodes(),
19850 layoutNodes: [],
19851 idToIndex: {},
19852 nodeSize: nodes.size(),
19853 graphSet: [],
19854 indexToGraph: [],
19855 layoutEdges: [],
19856 edgeSize: edges.size(),
19857 temperature: options.initialTemp,
19858 clientWidth: cy.width(),
19859 clientHeight: cy.width(),
19860 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19861 x1: 0,
19862 y1: 0,
19863 w: cy.width(),
19864 h: cy.height()
19865 })
19866 };
19867 var components = options.eles.components();
19868 var id2cmptId = {};
19869
19870 for (var i = 0; i < components.length; i++) {
19871 var component = components[i];
19872
19873 for (var j = 0; j < component.length; j++) {
19874 var node = component[j];
19875 id2cmptId[node.id()] = i;
19876 }
19877 } // Iterate over all nodes, creating layout nodes
19878
19879
19880 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19881 var n = nodes[i];
19882 var nbb = n.layoutDimensions(options);
19883 var tempNode = {};
19884 tempNode.isLocked = n.locked();
19885 tempNode.id = n.data('id');
19886 tempNode.parentId = n.data('parent');
19887 tempNode.cmptId = id2cmptId[n.id()];
19888 tempNode.children = [];
19889 tempNode.positionX = n.position('x');
19890 tempNode.positionY = n.position('y');
19891 tempNode.offsetX = 0;
19892 tempNode.offsetY = 0;
19893 tempNode.height = nbb.w;
19894 tempNode.width = nbb.h;
19895 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
19896 tempNode.minX = tempNode.positionX - tempNode.width / 2;
19897 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
19898 tempNode.minY = tempNode.positionY - tempNode.height / 2;
19899 tempNode.padLeft = parseFloat(n.style('padding'));
19900 tempNode.padRight = parseFloat(n.style('padding'));
19901 tempNode.padTop = parseFloat(n.style('padding'));
19902 tempNode.padBottom = parseFloat(n.style('padding')); // forces
19903
19904 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
19905
19906 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
19907
19908 layoutInfo.idToIndex[tempNode.id] = i;
19909 } // Inline implementation of a queue, used for traversing the graph in BFS order
19910
19911
19912 var queue = [];
19913 var start = 0; // Points to the start the queue
19914
19915 var end = -1; // Points to the end of the queue
19916
19917 var tempGraph = []; // Second pass to add child information and
19918 // initialize queue for hierarchical traversal
19919
19920 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19921 var n = layoutInfo.layoutNodes[i];
19922 var p_id = n.parentId; // Check if node n has a parent node
19923
19924 if (null != p_id) {
19925 // Add node Id to parent's list of children
19926 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
19927 } else {
19928 // If a node doesn't have a parent, then it's in the root graph
19929 queue[++end] = n.id;
19930 tempGraph.push(n.id);
19931 }
19932 } // Add root graph to graphSet
19933
19934
19935 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
19936
19937 while (start <= end) {
19938 // Get the node to visit and remove it from queue
19939 var node_id = queue[start++];
19940 var node_ix = layoutInfo.idToIndex[node_id];
19941 var node = layoutInfo.layoutNodes[node_ix];
19942 var children = node.children;
19943
19944 if (children.length > 0) {
19945 // Add children nodes as a new graph to graph set
19946 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
19947
19948 for (var i = 0; i < children.length; i++) {
19949 queue[++end] = children[i];
19950 }
19951 }
19952 } // Create indexToGraph map
19953
19954
19955 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19956 var graph = layoutInfo.graphSet[i];
19957
19958 for (var j = 0; j < graph.length; j++) {
19959 var index = layoutInfo.idToIndex[graph[j]];
19960 layoutInfo.indexToGraph[index] = i;
19961 }
19962 } // Iterate over all edges, creating Layout Edges
19963
19964
19965 for (var i = 0; i < layoutInfo.edgeSize; i++) {
19966 var e = edges[i];
19967 var tempEdge = {};
19968 tempEdge.id = e.data('id');
19969 tempEdge.sourceId = e.data('source');
19970 tempEdge.targetId = e.data('target'); // Compute ideal length
19971
19972 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
19973 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
19974
19975 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
19976 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
19977 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
19978 var targetGraph = layoutInfo.indexToGraph[targetIx];
19979
19980 if (sourceGraph != targetGraph) {
19981 // Find lowest common graph ancestor
19982 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
19983
19984 var lcaGraph = layoutInfo.graphSet[lca];
19985 var depth = 0; // Source depth
19986
19987 var tempNode = layoutInfo.layoutNodes[sourceIx];
19988
19989 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19990 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19991 depth++;
19992 } // Target depth
19993
19994
19995 tempNode = layoutInfo.layoutNodes[targetIx];
19996
19997 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19998 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19999 depth++;
20000 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20001 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20002 // ". Depth: " + depth);
20003 // Update idealLength
20004
20005
20006 idealLength *= depth * options.nestingFactor;
20007 }
20008
20009 tempEdge.idealLength = idealLength;
20010 tempEdge.elasticity = elasticity;
20011 layoutInfo.layoutEdges.push(tempEdge);
20012 } // Finally, return layoutInfo object
20013
20014
20015 return layoutInfo;
20016};
20017/**
20018 * @brief : This function finds the index of the lowest common
20019 * graph ancestor between 2 nodes in the subtree
20020 * (from the graph hierarchy induced tree) whose
20021 * root is graphIx
20022 *
20023 * @arg node1: node1's ID
20024 * @arg node2: node2's ID
20025 * @arg layoutInfo: layoutInfo object
20026 *
20027 */
20028
20029
20030var findLCA = function findLCA(node1, node2, layoutInfo) {
20031 // Find their common ancester, starting from the root graph
20032 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20033
20034 if (2 > res.count) {
20035 // If aux function couldn't find the common ancester,
20036 // then it is the root graph
20037 return 0;
20038 } else {
20039 return res.graph;
20040 }
20041};
20042/**
20043 * @brief : Auxiliary function used for LCA computation
20044 *
20045 * @arg node1 : node1's ID
20046 * @arg node2 : node2's ID
20047 * @arg graphIx : subgraph index
20048 * @arg layoutInfo : layoutInfo object
20049 *
20050 * @return : object of the form {count: X, graph: Y}, where:
20051 * X is the number of ancesters (max: 2) found in
20052 * graphIx (and it's subgraphs),
20053 * Y is the graph index of the lowest graph containing
20054 * all X nodes
20055 */
20056
20057
20058var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20059 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20060
20061 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20062 return {
20063 count: 2,
20064 graph: graphIx
20065 };
20066 } // Make recursive calls for all subgraphs
20067
20068
20069 var c = 0;
20070
20071 for (var i = 0; i < graph.length; i++) {
20072 var nodeId = graph[i];
20073 var nodeIx = layoutInfo.idToIndex[nodeId];
20074 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20075
20076 if (0 === children.length) {
20077 continue;
20078 }
20079
20080 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20081 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20082
20083 if (0 === result.count) {
20084 // Neither node1 nor node2 are present in this subgraph
20085 continue;
20086 } else if (1 === result.count) {
20087 // One of (node1, node2) is present in this subgraph
20088 c++;
20089
20090 if (2 === c) {
20091 // We've already found both nodes, no need to keep searching
20092 break;
20093 }
20094 } else {
20095 // Both nodes are present in this subgraph
20096 return result;
20097 }
20098 }
20099
20100 return {
20101 count: c,
20102 graph: graphIx
20103 };
20104};
20105/**
20106 * @brief: printsLayoutInfo into js console
20107 * Only used for debbuging
20108 */
20109
20110
20111if (false) {
20112 var printLayoutInfo;
20113}
20114/**
20115 * @brief : Randomizes the position of all nodes
20116 */
20117
20118
20119var randomizePositions = function randomizePositions(layoutInfo, cy) {
20120 var width = layoutInfo.clientWidth;
20121 var height = layoutInfo.clientHeight;
20122
20123 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20124 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20125
20126 if (0 === n.children.length && !n.isLocked) {
20127 n.positionX = Math.random() * width;
20128 n.positionY = Math.random() * height;
20129 }
20130 }
20131};
20132
20133var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20134 var bb = layoutInfo.boundingBox;
20135 var coseBB = {
20136 x1: Infinity,
20137 x2: -Infinity,
20138 y1: Infinity,
20139 y2: -Infinity
20140 };
20141
20142 if (options.boundingBox) {
20143 nodes.forEach(function (node) {
20144 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20145 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20146 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20147 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20148 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20149 });
20150 coseBB.w = coseBB.x2 - coseBB.x1;
20151 coseBB.h = coseBB.y2 - coseBB.y1;
20152 }
20153
20154 return function (ele, i) {
20155 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20156
20157 if (options.boundingBox) {
20158 // then add extra bounding box constraint
20159 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20160 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20161 return {
20162 x: bb.x1 + pctX * bb.w,
20163 y: bb.y1 + pctY * bb.h
20164 };
20165 } else {
20166 return {
20167 x: lnode.positionX,
20168 y: lnode.positionY
20169 };
20170 }
20171 };
20172};
20173/**
20174 * @brief : Updates the positions of nodes in the network
20175 * @arg layoutInfo : LayoutInfo object
20176 * @arg cy : Cytoscape object
20177 * @arg options : Layout options
20178 */
20179
20180
20181var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20182 // var s = 'Refreshing positions';
20183 // logDebug(s);
20184 var layout = options.layout;
20185 var nodes = options.eles.nodes();
20186 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20187 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20188
20189 if (true !== layoutInfo.ready) {
20190 // s = 'Triggering layoutready';
20191 // logDebug(s);
20192 layoutInfo.ready = true;
20193 layout.one('layoutready', options.ready);
20194 layout.emit({
20195 type: 'layoutready',
20196 layout: this
20197 });
20198 }
20199};
20200/**
20201 * @brief : Logs a debug message in JS console, if DEBUG is ON
20202 */
20203// var logDebug = function(text) {
20204// if (DEBUG) {
20205// console.debug(text);
20206// }
20207// };
20208
20209/**
20210 * @brief : Performs one iteration of the physical simulation
20211 * @arg layoutInfo : LayoutInfo object already initialized
20212 * @arg cy : Cytoscape object
20213 * @arg options : Layout options
20214 */
20215
20216
20217var step$1 = function step(layoutInfo, options, _step) {
20218 // var s = "\n\n###############################";
20219 // s += "\nSTEP: " + step;
20220 // s += "\n###############################\n";
20221 // logDebug(s);
20222 // Calculate node repulsions
20223 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20224
20225 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20226
20227 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20228
20229 propagateForces(layoutInfo); // Update positions based on calculated forces
20230
20231 updatePositions(layoutInfo);
20232};
20233/**
20234 * @brief : Computes the node repulsion forces
20235 */
20236
20237
20238var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20239 // Go through each of the graphs in graphSet
20240 // Nodes only repel each other if they belong to the same graph
20241 // var s = 'calculateNodeForces';
20242 // logDebug(s);
20243 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20244 var graph = layoutInfo.graphSet[i];
20245 var numNodes = graph.length; // s = "Set: " + graph.toString();
20246 // logDebug(s);
20247 // Now get all the pairs of nodes
20248 // Only get each pair once, (A, B) = (B, A)
20249
20250 for (var j = 0; j < numNodes; j++) {
20251 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20252
20253 for (var k = j + 1; k < numNodes; k++) {
20254 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20255 nodeRepulsion(node1, node2, layoutInfo, options);
20256 }
20257 }
20258 }
20259};
20260
20261var randomDistance = function randomDistance(max) {
20262 return -max + 2 * max * Math.random();
20263};
20264/**
20265 * @brief : Compute the node repulsion forces between a pair of nodes
20266 */
20267
20268
20269var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20270 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20271 var cmptId1 = node1.cmptId;
20272 var cmptId2 = node2.cmptId;
20273
20274 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20275 return;
20276 } // Get direction of line connecting both node centers
20277
20278
20279 var directionX = node2.positionX - node1.positionX;
20280 var directionY = node2.positionY - node1.positionY;
20281 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20282 // If both centers are the same, apply a random force
20283
20284 if (0 === directionX && 0 === directionY) {
20285 directionX = randomDistance(maxRandDist);
20286 directionY = randomDistance(maxRandDist);
20287 }
20288
20289 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20290
20291 if (overlap > 0) {
20292 // s += "\nNodes DO overlap.";
20293 // s += "\nOverlap: " + overlap;
20294 // If nodes overlap, repulsion force is proportional
20295 // to the overlap
20296 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20297
20298 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20299
20300 var forceX = force * directionX / distance;
20301 var forceY = force * directionY / distance;
20302 } else {
20303 // s += "\nNodes do NOT overlap.";
20304 // If there's no overlap, force is inversely proportional
20305 // to squared distance
20306 // Get clipping points for both nodes
20307 var point1 = findClippingPoint(node1, directionX, directionY);
20308 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20309
20310 var distanceX = point2.x - point1.x;
20311 var distanceY = point2.y - point1.y;
20312 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20313 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20314 // Compute the module and components of the force vector
20315
20316 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20317 var forceX = force * distanceX / distance;
20318 var forceY = force * distanceY / distance;
20319 } // Apply force
20320
20321
20322 if (!node1.isLocked) {
20323 node1.offsetX -= forceX;
20324 node1.offsetY -= forceY;
20325 }
20326
20327 if (!node2.isLocked) {
20328 node2.offsetX += forceX;
20329 node2.offsetY += forceY;
20330 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20331 // logDebug(s);
20332
20333
20334 return;
20335};
20336/**
20337 * @brief : Determines whether two nodes overlap or not
20338 * @return : Amount of overlapping (0 => no overlap)
20339 */
20340
20341
20342var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20343 if (dX > 0) {
20344 var overlapX = node1.maxX - node2.minX;
20345 } else {
20346 var overlapX = node2.maxX - node1.minX;
20347 }
20348
20349 if (dY > 0) {
20350 var overlapY = node1.maxY - node2.minY;
20351 } else {
20352 var overlapY = node2.maxY - node1.minY;
20353 }
20354
20355 if (overlapX >= 0 && overlapY >= 0) {
20356 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20357 } else {
20358 return 0;
20359 }
20360};
20361/**
20362 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20363 * the rectangular bounding box of it's source/target node
20364 */
20365
20366
20367var findClippingPoint = function findClippingPoint(node, dX, dY) {
20368 // Shorcuts
20369 var X = node.positionX;
20370 var Y = node.positionY;
20371 var H = node.height || 1;
20372 var W = node.width || 1;
20373 var dirSlope = dY / dX;
20374 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20375 // " . Height: " + H + ", Width: " + W +
20376 // "\nDirection " + dX + ", " + dY;
20377 //
20378 // Compute intersection
20379
20380 var res = {}; // Case: Vertical direction (up)
20381
20382 if (0 === dX && 0 < dY) {
20383 res.x = X; // s += "\nUp direction";
20384
20385 res.y = Y + H / 2;
20386 return res;
20387 } // Case: Vertical direction (down)
20388
20389
20390 if (0 === dX && 0 > dY) {
20391 res.x = X;
20392 res.y = Y + H / 2; // s += "\nDown direction";
20393
20394 return res;
20395 } // Case: Intersects the right border
20396
20397
20398 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20399 res.x = X + W / 2;
20400 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20401
20402 return res;
20403 } // Case: Intersects the left border
20404
20405
20406 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20407 res.x = X - W / 2;
20408 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20409
20410 return res;
20411 } // Case: Intersects the top border
20412
20413
20414 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20415 res.x = X + H * dX / 2 / dY;
20416 res.y = Y + H / 2; // s += "\nTop border";
20417
20418 return res;
20419 } // Case: Intersects the bottom border
20420
20421
20422 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20423 res.x = X - H * dX / 2 / dY;
20424 res.y = Y - H / 2; // s += "\nBottom border";
20425
20426 return res;
20427 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20428 // logDebug(s);
20429
20430
20431 return res;
20432};
20433/**
20434 * @brief : Calculates all edge forces
20435 */
20436
20437
20438var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20439 // Iterate over all edges
20440 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20441 // Get edge, source & target nodes
20442 var edge = layoutInfo.layoutEdges[i];
20443 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20444 var source = layoutInfo.layoutNodes[sourceIx];
20445 var targetIx = layoutInfo.idToIndex[edge.targetId];
20446 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20447
20448 var directionX = target.positionX - source.positionX;
20449 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20450 // A random force has already been applied as node repulsion
20451
20452 if (0 === directionX && 0 === directionY) {
20453 continue;
20454 } // Get clipping points for both nodes
20455
20456
20457 var point1 = findClippingPoint(source, directionX, directionY);
20458 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20459 var lx = point2.x - point1.x;
20460 var ly = point2.y - point1.y;
20461 var l = Math.sqrt(lx * lx + ly * ly);
20462 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20463
20464 if (0 !== l) {
20465 var forceX = force * lx / l;
20466 var forceY = force * ly / l;
20467 } else {
20468 var forceX = 0;
20469 var forceY = 0;
20470 } // Add this force to target and source nodes
20471
20472
20473 if (!source.isLocked) {
20474 source.offsetX += forceX;
20475 source.offsetY += forceY;
20476 }
20477
20478 if (!target.isLocked) {
20479 target.offsetX -= forceX;
20480 target.offsetY -= forceY;
20481 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20482 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20483 // logDebug(s);
20484
20485 }
20486};
20487/**
20488 * @brief : Computes gravity forces for all nodes
20489 */
20490
20491
20492var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20493 var distThreshold = 1; // var s = 'calculateGravityForces';
20494 // logDebug(s);
20495
20496 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20497 var graph = layoutInfo.graphSet[i];
20498 var numNodes = graph.length; // s = "Set: " + graph.toString();
20499 // logDebug(s);
20500 // Compute graph center
20501
20502 if (0 === i) {
20503 var centerX = layoutInfo.clientHeight / 2;
20504 var centerY = layoutInfo.clientWidth / 2;
20505 } else {
20506 // Get Parent node for this graph, and use its position as center
20507 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20508 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20509 var centerX = parent.positionX;
20510 var centerY = parent.positionY;
20511 } // s = "Center found at: " + centerX + ", " + centerY;
20512 // logDebug(s);
20513 // Apply force to all nodes in graph
20514
20515
20516 for (var j = 0; j < numNodes; j++) {
20517 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20518
20519 if (node.isLocked) {
20520 continue;
20521 }
20522
20523 var dx = centerX - node.positionX;
20524 var dy = centerY - node.positionY;
20525 var d = Math.sqrt(dx * dx + dy * dy);
20526
20527 if (d > distThreshold) {
20528 var fx = options.gravity * dx / d;
20529 var fy = options.gravity * dy / d;
20530 node.offsetX += fx;
20531 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20532 } // s += ": skypped since it's too close to center";
20533 // logDebug(s);
20534
20535 }
20536 }
20537};
20538/**
20539 * @brief : This function propagates the existing offsets from
20540 * parent nodes to its descendents.
20541 * @arg layoutInfo : layoutInfo Object
20542 * @arg cy : cytoscape Object
20543 * @arg options : Layout options
20544 */
20545
20546
20547var propagateForces = function propagateForces(layoutInfo, options) {
20548 // Inline implementation of a queue, used for traversing the graph in BFS order
20549 var queue = [];
20550 var start = 0; // Points to the start the queue
20551
20552 var end = -1; // Points to the end of the queue
20553 // logDebug('propagateForces');
20554 // Start by visiting the nodes in the root graph
20555
20556 queue.push.apply(queue, layoutInfo.graphSet[0]);
20557 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20558
20559 while (start <= end) {
20560 // Get the node to visit and remove it from queue
20561 var nodeId = queue[start++];
20562 var nodeIndex = layoutInfo.idToIndex[nodeId];
20563 var node = layoutInfo.layoutNodes[nodeIndex];
20564 var children = node.children; // We only need to process the node if it's compound
20565
20566 if (0 < children.length && !node.isLocked) {
20567 var offX = node.offsetX;
20568 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20569 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20570 // s += "\n Children: " + children.toString();
20571 // logDebug(s);
20572
20573 for (var i = 0; i < children.length; i++) {
20574 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20575
20576 childNode.offsetX += offX;
20577 childNode.offsetY += offY; // Add children to queue to be visited
20578
20579 queue[++end] = children[i];
20580 } // Reset parent offsets
20581
20582
20583 node.offsetX = 0;
20584 node.offsetY = 0;
20585 }
20586 }
20587};
20588/**
20589 * @brief : Updates the layout model positions, based on
20590 * the accumulated forces
20591 */
20592
20593
20594var updatePositions = function updatePositions(layoutInfo, options) {
20595 // var s = 'Updating positions';
20596 // logDebug(s);
20597 // Reset boundaries for compound nodes
20598 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20599 var n = layoutInfo.layoutNodes[i];
20600
20601 if (0 < n.children.length) {
20602 // logDebug("Resetting boundaries of compound node: " + n.id);
20603 n.maxX = undefined;
20604 n.minX = undefined;
20605 n.maxY = undefined;
20606 n.minY = undefined;
20607 }
20608 }
20609
20610 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20611 var n = layoutInfo.layoutNodes[i];
20612
20613 if (0 < n.children.length || n.isLocked) {
20614 // No need to set compound or locked node position
20615 // logDebug("Skipping position update of node: " + n.id);
20616 continue;
20617 } // s = "Node: " + n.id + " Previous position: (" +
20618 // n.positionX + ", " + n.positionY + ").";
20619 // Limit displacement in order to improve stability
20620
20621
20622 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20623 n.positionX += tempForce.x;
20624 n.positionY += tempForce.y;
20625 n.offsetX = 0;
20626 n.offsetY = 0;
20627 n.minX = n.positionX - n.width;
20628 n.maxX = n.positionX + n.width;
20629 n.minY = n.positionY - n.height;
20630 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20631 // logDebug(s);
20632 // Update ancestry boudaries
20633
20634 updateAncestryBoundaries(n, layoutInfo);
20635 } // Update size, position of compund nodes
20636
20637
20638 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20639 var n = layoutInfo.layoutNodes[i];
20640
20641 if (0 < n.children.length && !n.isLocked) {
20642 n.positionX = (n.maxX + n.minX) / 2;
20643 n.positionY = (n.maxY + n.minY) / 2;
20644 n.width = n.maxX - n.minX;
20645 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20646 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20647 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20648 // logDebug(s);
20649 }
20650 }
20651};
20652/**
20653 * @brief : Limits a force (forceX, forceY) to be not
20654 * greater (in modulo) than max.
20655 8 Preserves force direction.
20656 */
20657
20658
20659var limitForce = function limitForce(forceX, forceY, max) {
20660 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20661 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20662
20663 if (force > max) {
20664 var res = {
20665 x: max * forceX / force,
20666 y: max * forceY / force
20667 };
20668 } else {
20669 var res = {
20670 x: forceX,
20671 y: forceY
20672 };
20673 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20674 // logDebug(s);
20675
20676
20677 return res;
20678};
20679/**
20680 * @brief : Function used for keeping track of compound node
20681 * sizes, since they should bound all their subnodes.
20682 */
20683
20684
20685var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20686 // var s = "Propagating new position/size of node " + node.id;
20687 var parentId = node.parentId;
20688
20689 if (null == parentId) {
20690 // If there's no parent, we are done
20691 // s += ". No parent node.";
20692 // logDebug(s);
20693 return;
20694 } // Get Parent Node
20695
20696
20697 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20698 var flag = false; // MaxX
20699
20700 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20701 p.maxX = node.maxX + p.padRight;
20702 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20703 } // MinX
20704
20705
20706 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20707 p.minX = node.minX - p.padLeft;
20708 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20709 } // MaxY
20710
20711
20712 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20713 p.maxY = node.maxY + p.padBottom;
20714 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20715 } // MinY
20716
20717
20718 if (null == p.minY || node.minY - p.padTop < p.minY) {
20719 p.minY = node.minY - p.padTop;
20720 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20721 } // If updated boundaries, propagate changes upward
20722
20723
20724 if (flag) {
20725 // logDebug(s);
20726 return updateAncestryBoundaries(p, layoutInfo);
20727 } // s += ". No changes in boundaries/position of parent node " + p.id;
20728 // logDebug(s);
20729
20730
20731 return;
20732};
20733
20734var separateComponents = function separateComponents(layoutInfo, options) {
20735 var nodes = layoutInfo.layoutNodes;
20736 var components = [];
20737
20738 for (var i = 0; i < nodes.length; i++) {
20739 var node = nodes[i];
20740 var cid = node.cmptId;
20741 var component = components[cid] = components[cid] || [];
20742 component.push(node);
20743 }
20744
20745 var totalA = 0;
20746
20747 for (var i = 0; i < components.length; i++) {
20748 var c = components[i];
20749
20750 if (!c) {
20751 continue;
20752 }
20753
20754 c.x1 = Infinity;
20755 c.x2 = -Infinity;
20756 c.y1 = Infinity;
20757 c.y2 = -Infinity;
20758
20759 for (var j = 0; j < c.length; j++) {
20760 var n = c[j];
20761 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20762 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20763 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20764 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20765 }
20766
20767 c.w = c.x2 - c.x1;
20768 c.h = c.y2 - c.y1;
20769 totalA += c.w * c.h;
20770 }
20771
20772 components.sort(function (c1, c2) {
20773 return c2.w * c2.h - c1.w * c1.h;
20774 });
20775 var x = 0;
20776 var y = 0;
20777 var usedW = 0;
20778 var rowH = 0;
20779 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20780
20781 for (var i = 0; i < components.length; i++) {
20782 var c = components[i];
20783
20784 if (!c) {
20785 continue;
20786 }
20787
20788 for (var j = 0; j < c.length; j++) {
20789 var n = c[j];
20790
20791 if (!n.isLocked) {
20792 n.positionX += x - c.x1;
20793 n.positionY += y - c.y1;
20794 }
20795 }
20796
20797 x += c.w + options.componentSpacing;
20798 usedW += c.w + options.componentSpacing;
20799 rowH = Math.max(rowH, c.h);
20800
20801 if (usedW > maxRowW) {
20802 y += rowH + options.componentSpacing;
20803 x = 0;
20804 usedW = 0;
20805 rowH = 0;
20806 }
20807 }
20808};
20809
20810var defaults$d = {
20811 fit: true,
20812 // whether to fit the viewport to the graph
20813 padding: 30,
20814 // padding used on fit
20815 boundingBox: undefined,
20816 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20817 avoidOverlap: true,
20818 // prevents node overlap, may overflow boundingBox if not enough space
20819 avoidOverlapPadding: 10,
20820 // extra spacing around nodes when avoidOverlap: true
20821 nodeDimensionsIncludeLabels: false,
20822 // Excludes the label when calculating node bounding boxes for the layout algorithm
20823 spacingFactor: undefined,
20824 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20825 condense: false,
20826 // uses all available space on false, uses minimal space on true
20827 rows: undefined,
20828 // force num of rows in the grid
20829 cols: undefined,
20830 // force num of columns in the grid
20831 position: function position(node) {},
20832 // returns { row, col } for element
20833 sort: undefined,
20834 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20835 animate: false,
20836 // whether to transition the node positions
20837 animationDuration: 500,
20838 // duration of animation in ms if enabled
20839 animationEasing: undefined,
20840 // easing of animation if enabled
20841 animateFilter: function animateFilter(node, i) {
20842 return true;
20843 },
20844 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
20845 ready: undefined,
20846 // callback on layoutready
20847 stop: undefined,
20848 // callback on layoutstop
20849 transform: function transform(node, position) {
20850 return position;
20851 } // transform a given node position. Useful for changing flow direction in discrete layouts
20852
20853};
20854
20855function GridLayout(options) {
20856 this.options = extend({}, defaults$d, options);
20857}
20858
20859GridLayout.prototype.run = function () {
20860 var params = this.options;
20861 var options = params;
20862 var cy = params.cy;
20863 var eles = options.eles;
20864 var nodes = eles.nodes().not(':parent');
20865
20866 if (options.sort) {
20867 nodes = nodes.sort(options.sort);
20868 }
20869
20870 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20871 x1: 0,
20872 y1: 0,
20873 w: cy.width(),
20874 h: cy.height()
20875 });
20876
20877 if (bb.h === 0 || bb.w === 0) {
20878 eles.nodes().layoutPositions(this, options, function (ele) {
20879 return {
20880 x: bb.x1,
20881 y: bb.y1
20882 };
20883 });
20884 } else {
20885 // width/height * splits^2 = cells where splits is number of times to split width
20886 var cells = nodes.size();
20887 var splits = Math.sqrt(cells * bb.h / bb.w);
20888 var rows = Math.round(splits);
20889 var cols = Math.round(bb.w / bb.h * splits);
20890
20891 var small = function small(val) {
20892 if (val == null) {
20893 return Math.min(rows, cols);
20894 } else {
20895 var min = Math.min(rows, cols);
20896
20897 if (min == rows) {
20898 rows = val;
20899 } else {
20900 cols = val;
20901 }
20902 }
20903 };
20904
20905 var large = function large(val) {
20906 if (val == null) {
20907 return Math.max(rows, cols);
20908 } else {
20909 var max = Math.max(rows, cols);
20910
20911 if (max == rows) {
20912 rows = val;
20913 } else {
20914 cols = val;
20915 }
20916 }
20917 };
20918
20919 var oRows = options.rows;
20920 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
20921
20922 if (oRows != null && oCols != null) {
20923 rows = oRows;
20924 cols = oCols;
20925 } else if (oRows != null && oCols == null) {
20926 rows = oRows;
20927 cols = Math.ceil(cells / rows);
20928 } else if (oRows == null && oCols != null) {
20929 cols = oCols;
20930 rows = Math.ceil(cells / cols);
20931 } // otherwise use the automatic values and adjust accordingly
20932 // if rounding was up, see if we can reduce rows or columns
20933 else if (cols * rows > cells) {
20934 var sm = small();
20935 var lg = large(); // reducing the small side takes away the most cells, so try it first
20936
20937 if ((sm - 1) * lg >= cells) {
20938 small(sm - 1);
20939 } else if ((lg - 1) * sm >= cells) {
20940 large(lg - 1);
20941 }
20942 } else {
20943 // if rounding was too low, add rows or columns
20944 while (cols * rows < cells) {
20945 var _sm = small();
20946
20947 var _lg = large(); // try to add to larger side first (adds less in multiplication)
20948
20949
20950 if ((_lg + 1) * _sm >= cells) {
20951 large(_lg + 1);
20952 } else {
20953 small(_sm + 1);
20954 }
20955 }
20956 }
20957
20958 var cellWidth = bb.w / cols;
20959 var cellHeight = bb.h / rows;
20960
20961 if (options.condense) {
20962 cellWidth = 0;
20963 cellHeight = 0;
20964 }
20965
20966 if (options.avoidOverlap) {
20967 for (var i = 0; i < nodes.length; i++) {
20968 var node = nodes[i];
20969 var pos = node._private.position;
20970
20971 if (pos.x == null || pos.y == null) {
20972 // for bb
20973 pos.x = 0;
20974 pos.y = 0;
20975 }
20976
20977 var nbb = node.layoutDimensions(options);
20978 var p = options.avoidOverlapPadding;
20979 var w = nbb.w + p;
20980 var h = nbb.h + p;
20981 cellWidth = Math.max(cellWidth, w);
20982 cellHeight = Math.max(cellHeight, h);
20983 }
20984 }
20985
20986 var cellUsed = {}; // e.g. 'c-0-2' => true
20987
20988 var used = function used(row, col) {
20989 return cellUsed['c-' + row + '-' + col] ? true : false;
20990 };
20991
20992 var use = function use(row, col) {
20993 cellUsed['c-' + row + '-' + col] = true;
20994 }; // to keep track of current cell position
20995
20996
20997 var row = 0;
20998 var col = 0;
20999
21000 var moveToNextCell = function moveToNextCell() {
21001 col++;
21002
21003 if (col >= cols) {
21004 col = 0;
21005 row++;
21006 }
21007 }; // get a cache of all the manual positions
21008
21009
21010 var id2manPos = {};
21011
21012 for (var _i = 0; _i < nodes.length; _i++) {
21013 var _node = nodes[_i];
21014 var rcPos = options.position(_node);
21015
21016 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21017 // must have at least row or col def'd
21018 var _pos = {
21019 row: rcPos.row,
21020 col: rcPos.col
21021 };
21022
21023 if (_pos.col === undefined) {
21024 // find unused col
21025 _pos.col = 0;
21026
21027 while (used(_pos.row, _pos.col)) {
21028 _pos.col++;
21029 }
21030 } else if (_pos.row === undefined) {
21031 // find unused row
21032 _pos.row = 0;
21033
21034 while (used(_pos.row, _pos.col)) {
21035 _pos.row++;
21036 }
21037 }
21038
21039 id2manPos[_node.id()] = _pos;
21040 use(_pos.row, _pos.col);
21041 }
21042 }
21043
21044 var getPos = function getPos(element, i) {
21045 var x, y;
21046
21047 if (element.locked() || element.isParent()) {
21048 return false;
21049 } // see if we have a manual position set
21050
21051
21052 var rcPos = id2manPos[element.id()];
21053
21054 if (rcPos) {
21055 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21056 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21057 } else {
21058 // otherwise set automatically
21059 while (used(row, col)) {
21060 moveToNextCell();
21061 }
21062
21063 x = col * cellWidth + cellWidth / 2 + bb.x1;
21064 y = row * cellHeight + cellHeight / 2 + bb.y1;
21065 use(row, col);
21066 moveToNextCell();
21067 }
21068
21069 return {
21070 x: x,
21071 y: y
21072 };
21073 };
21074
21075 nodes.layoutPositions(this, options, getPos);
21076 }
21077
21078 return this; // chaining
21079};
21080
21081var defaults$e = {
21082 ready: function ready() {},
21083 // on layoutready
21084 stop: function stop() {} // on layoutstop
21085
21086}; // constructor
21087// options : object containing layout options
21088
21089function NullLayout(options) {
21090 this.options = extend({}, defaults$e, options);
21091} // runs the layout
21092
21093
21094NullLayout.prototype.run = function () {
21095 var options = this.options;
21096 var eles = options.eles; // elements to consider in the layout
21097
21098 var layout = this; // cy is automatically populated for us in the constructor
21099 // (disable eslint for next line as this serves as example layout code to external developers)
21100 // eslint-disable-next-line no-unused-vars
21101
21102 var cy = options.cy;
21103 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21104 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21105
21106 eles.nodes().positions(function () {
21107 return {
21108 x: 0,
21109 y: 0
21110 };
21111 }); // trigger layoutready when each node has had its position set at least once
21112
21113 layout.one('layoutready', options.ready);
21114 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21115
21116 layout.one('layoutstop', options.stop);
21117 layout.emit('layoutstop');
21118 return this; // chaining
21119}; // called on continuous layouts to stop them before they finish
21120
21121
21122NullLayout.prototype.stop = function () {
21123 return this; // chaining
21124};
21125
21126var defaults$f = {
21127 positions: undefined,
21128 // map of (node id) => (position obj); or function(node){ return somPos; }
21129 zoom: undefined,
21130 // the zoom level to set (prob want fit = false if set)
21131 pan: undefined,
21132 // the pan level to set (prob want fit = false if set)
21133 fit: true,
21134 // whether to fit to viewport
21135 padding: 30,
21136 // padding on fit
21137 animate: false,
21138 // whether to transition the node positions
21139 animationDuration: 500,
21140 // duration of animation in ms if enabled
21141 animationEasing: undefined,
21142 // easing of animation if enabled
21143 animateFilter: function animateFilter(node, i) {
21144 return true;
21145 },
21146 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
21147 ready: undefined,
21148 // callback on layoutready
21149 stop: undefined,
21150 // callback on layoutstop
21151 transform: function transform(node, position) {
21152 return position;
21153 } // transform a given node position. Useful for changing flow direction in discrete layouts
21154
21155};
21156
21157function PresetLayout(options) {
21158 this.options = extend({}, defaults$f, options);
21159}
21160
21161PresetLayout.prototype.run = function () {
21162 var options = this.options;
21163 var eles = options.eles;
21164 var nodes = eles.nodes();
21165 var posIsFn = fn(options.positions);
21166
21167 function getPosition(node) {
21168 if (options.positions == null) {
21169 return copyPosition(node.position());
21170 }
21171
21172 if (posIsFn) {
21173 return options.positions(node);
21174 }
21175
21176 var pos = options.positions[node._private.data.id];
21177
21178 if (pos == null) {
21179 return null;
21180 }
21181
21182 return pos;
21183 }
21184
21185 nodes.layoutPositions(this, options, function (node, i) {
21186 var position = getPosition(node);
21187
21188 if (node.locked() || position == null) {
21189 return false;
21190 }
21191
21192 return position;
21193 });
21194 return this; // chaining
21195};
21196
21197var defaults$g = {
21198 fit: true,
21199 // whether to fit to viewport
21200 padding: 30,
21201 // fit padding
21202 boundingBox: undefined,
21203 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21204 animate: false,
21205 // whether to transition the node positions
21206 animationDuration: 500,
21207 // duration of animation in ms if enabled
21208 animationEasing: undefined,
21209 // easing of animation if enabled
21210 animateFilter: function animateFilter(node, i) {
21211 return true;
21212 },
21213 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
21214 ready: undefined,
21215 // callback on layoutready
21216 stop: undefined,
21217 // callback on layoutstop
21218 transform: function transform(node, position) {
21219 return position;
21220 } // transform a given node position. Useful for changing flow direction in discrete layouts
21221
21222};
21223
21224function RandomLayout(options) {
21225 this.options = extend({}, defaults$g, options);
21226}
21227
21228RandomLayout.prototype.run = function () {
21229 var options = this.options;
21230 var cy = options.cy;
21231 var eles = options.eles;
21232 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21233 x1: 0,
21234 y1: 0,
21235 w: cy.width(),
21236 h: cy.height()
21237 });
21238
21239 var getPos = function getPos(node, i) {
21240 return {
21241 x: bb.x1 + Math.round(Math.random() * bb.w),
21242 y: bb.y1 + Math.round(Math.random() * bb.h)
21243 };
21244 };
21245
21246 eles.nodes().layoutPositions(this, options, getPos);
21247 return this; // chaining
21248};
21249
21250var layout = [{
21251 name: 'breadthfirst',
21252 impl: BreadthFirstLayout
21253}, {
21254 name: 'circle',
21255 impl: CircleLayout
21256}, {
21257 name: 'concentric',
21258 impl: ConcentricLayout
21259}, {
21260 name: 'cose',
21261 impl: CoseLayout
21262}, {
21263 name: 'grid',
21264 impl: GridLayout
21265}, {
21266 name: 'null',
21267 impl: NullLayout
21268}, {
21269 name: 'preset',
21270 impl: PresetLayout
21271}, {
21272 name: 'random',
21273 impl: RandomLayout
21274}];
21275
21276function NullRenderer(options) {
21277 this.options = options;
21278 this.notifications = 0; // for testing
21279}
21280
21281var noop$1 = function noop() {};
21282
21283var throwImgErr = function throwImgErr() {
21284 throw new Error('A headless instance can not render images');
21285};
21286
21287NullRenderer.prototype = {
21288 recalculateRenderedStyle: noop$1,
21289 notify: function notify() {
21290 this.notifications++;
21291 },
21292 init: noop$1,
21293 isHeadless: function isHeadless() {
21294 return true;
21295 },
21296 png: throwImgErr,
21297 jpg: throwImgErr
21298};
21299
21300var BRp = {};
21301BRp.arrowShapeWidth = 0.3;
21302
21303BRp.registerArrowShapes = function () {
21304 var arrowShapes = this.arrowShapes = {};
21305 var renderer = this; // Contract for arrow shapes:
21306 // 0, 0 is arrow tip
21307 // (0, 1) is direction towards node
21308 // (1, 0) is right
21309 //
21310 // functional api:
21311 // collide: check x, y in shape
21312 // roughCollide: called before collide, no false negatives
21313 // draw: draw
21314 // spacing: dist(arrowTip, nodeBoundary)
21315 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21316
21317 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21318 var x1 = translation.x - size / 2 - padding;
21319 var x2 = translation.x + size / 2 + padding;
21320 var y1 = translation.y - size / 2 - padding;
21321 var y2 = translation.y + size / 2 + padding;
21322 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21323 return inside;
21324 };
21325
21326 var transform = function transform(x, y, size, angle, translation) {
21327 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21328 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21329 var xScaled = xRotated * size;
21330 var yScaled = yRotated * size;
21331 var xTranslated = xScaled + translation.x;
21332 var yTranslated = yScaled + translation.y;
21333 return {
21334 x: xTranslated,
21335 y: yTranslated
21336 };
21337 };
21338
21339 var transformPoints = function transformPoints(pts, size, angle, translation) {
21340 var retPts = [];
21341
21342 for (var i = 0; i < pts.length; i += 2) {
21343 var x = pts[i];
21344 var y = pts[i + 1];
21345 retPts.push(transform(x, y, size, angle, translation));
21346 }
21347
21348 return retPts;
21349 };
21350
21351 var pointsToArr = function pointsToArr(pts) {
21352 var ret = [];
21353
21354 for (var i = 0; i < pts.length; i++) {
21355 var p = pts[i];
21356 ret.push(p.x, p.y);
21357 }
21358
21359 return ret;
21360 };
21361
21362 var standardGap = function standardGap(edge) {
21363 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21364 };
21365
21366 var defineArrowShape = function defineArrowShape(name, defn) {
21367 if (string(defn)) {
21368 defn = arrowShapes[defn];
21369 }
21370
21371 arrowShapes[name] = extend({
21372 name: name,
21373 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21374 collide: function collide(x, y, size, angle, translation, padding) {
21375 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21376 var inside = pointInsidePolygonPoints(x, y, points);
21377 return inside;
21378 },
21379 roughCollide: bbCollide,
21380 draw: function draw(context, size, angle, translation) {
21381 var points = transformPoints(this.points, size, angle, translation);
21382 renderer.arrowShapeImpl('polygon')(context, points);
21383 },
21384 spacing: function spacing(edge) {
21385 return 0;
21386 },
21387 gap: standardGap
21388 }, defn);
21389 };
21390
21391 defineArrowShape('none', {
21392 collide: falsify,
21393 roughCollide: falsify,
21394 draw: noop,
21395 spacing: zeroify,
21396 gap: zeroify
21397 });
21398 defineArrowShape('triangle', {
21399 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21400 });
21401 defineArrowShape('arrow', 'triangle');
21402 defineArrowShape('triangle-backcurve', {
21403 points: arrowShapes['triangle'].points,
21404 controlPoint: [0, -0.15],
21405 roughCollide: bbCollide,
21406 draw: function draw(context, size, angle, translation, edgeWidth) {
21407 var ptsTrans = transformPoints(this.points, size, angle, translation);
21408 var ctrlPt = this.controlPoint;
21409 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21410 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21411 },
21412 gap: function gap(edge) {
21413 return standardGap(edge) * 0.8;
21414 }
21415 });
21416 defineArrowShape('triangle-tee', {
21417 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21418 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21419 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21420 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21421 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21422 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21423 return inside;
21424 },
21425 draw: function draw(context, size, angle, translation, edgeWidth) {
21426 var triPts = transformPoints(this.points, size, angle, translation);
21427 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21428 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21429 }
21430 });
21431 defineArrowShape('circle-triangle', {
21432 radius: 0.15,
21433 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21434 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21435 var t = translation;
21436 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21437 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21438 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21439 },
21440 draw: function draw(context, size, angle, translation, edgeWidth) {
21441 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21442 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21443 },
21444 spacing: function spacing(edge) {
21445 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21446 }
21447 });
21448 defineArrowShape('triangle-cross', {
21449 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21450 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21451 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21452 0.15, -0.4],
21453 crossLinePts: function crossLinePts(size, edgeWidth) {
21454 // shift points so that the distance between the cross points matches edge width
21455 var p = this.baseCrossLinePts.slice();
21456 var shiftFactor = edgeWidth / size;
21457 var y0 = 3;
21458 var y1 = 5;
21459 p[y0] = p[y0] - shiftFactor;
21460 p[y1] = p[y1] - shiftFactor;
21461 return p;
21462 },
21463 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21464 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21465 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21466 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21467 return inside;
21468 },
21469 draw: function draw(context, size, angle, translation, edgeWidth) {
21470 var triPts = transformPoints(this.points, size, angle, translation);
21471 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21472 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21473 }
21474 });
21475 defineArrowShape('vee', {
21476 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21477 gap: function gap(edge) {
21478 return standardGap(edge) * 0.525;
21479 }
21480 });
21481 defineArrowShape('circle', {
21482 radius: 0.15,
21483 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21484 var t = translation;
21485 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21486 return inside;
21487 },
21488 draw: function draw(context, size, angle, translation, edgeWidth) {
21489 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21490 },
21491 spacing: function spacing(edge) {
21492 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21493 }
21494 });
21495 defineArrowShape('tee', {
21496 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21497 spacing: function spacing(edge) {
21498 return 1;
21499 },
21500 gap: function gap(edge) {
21501 return 1;
21502 }
21503 });
21504 defineArrowShape('square', {
21505 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21506 });
21507 defineArrowShape('diamond', {
21508 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21509 gap: function gap(edge) {
21510 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21511 }
21512 });
21513 defineArrowShape('chevron', {
21514 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21515 gap: function gap(edge) {
21516 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21517 }
21518 });
21519};
21520
21521var BRp$1 = {}; // Project mouse
21522
21523BRp$1.projectIntoViewport = function (clientX, clientY) {
21524 var cy = this.cy;
21525 var offsets = this.findContainerClientCoords();
21526 var offsetLeft = offsets[0];
21527 var offsetTop = offsets[1];
21528 var scale = offsets[4];
21529 var pan = cy.pan();
21530 var zoom = cy.zoom();
21531 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21532 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21533 return [x, y];
21534};
21535
21536BRp$1.findContainerClientCoords = function () {
21537 if (this.containerBB) {
21538 return this.containerBB;
21539 }
21540
21541 var container = this.container;
21542 var rect = container.getBoundingClientRect();
21543 var style = window$1.getComputedStyle(container);
21544
21545 var styleValue = function styleValue(name) {
21546 return parseFloat(style.getPropertyValue(name));
21547 };
21548
21549 var padding = {
21550 left: styleValue('padding-left'),
21551 right: styleValue('padding-right'),
21552 top: styleValue('padding-top'),
21553 bottom: styleValue('padding-bottom')
21554 };
21555 var border = {
21556 left: styleValue('border-left-width'),
21557 right: styleValue('border-right-width'),
21558 top: styleValue('border-top-width'),
21559 bottom: styleValue('border-bottom-width')
21560 };
21561 var clientWidth = container.clientWidth;
21562 var clientHeight = container.clientHeight;
21563 var paddingHor = padding.left + padding.right;
21564 var paddingVer = padding.top + padding.bottom;
21565 var borderHor = border.left + border.right;
21566 var scale = rect.width / (clientWidth + borderHor);
21567 var unscaledW = clientWidth - paddingHor;
21568 var unscaledH = clientHeight - paddingVer;
21569 var left = rect.left + padding.left + border.left;
21570 var top = rect.top + padding.top + border.top;
21571 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21572};
21573
21574BRp$1.invalidateContainerClientCoordsCache = function () {
21575 this.containerBB = null;
21576};
21577
21578BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21579 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21580};
21581
21582BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21583 var self = this;
21584 var r = this;
21585 var eles = r.getCachedZSortedEles();
21586 var near = []; // 1 node max, 1 edge max
21587
21588 var zoom = r.cy.zoom();
21589 var hasCompounds = r.cy.hasCompoundNodes();
21590 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21591 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21592 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21593 var minSqDist = Infinity;
21594 var nearEdge;
21595 var nearNode;
21596
21597 if (interactiveElementsOnly) {
21598 eles = eles.interactive;
21599 }
21600
21601 function addEle(ele, sqDist) {
21602 if (ele.isNode()) {
21603 if (nearNode) {
21604 return; // can't replace node
21605 } else {
21606 nearNode = ele;
21607 near.push(ele);
21608 }
21609 }
21610
21611 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21612 if (nearEdge) {
21613 // then replace existing edge
21614 // can replace only if same z-index
21615 if (nearEdge.pstyle('z-compound-depth').value === ele.pstyle('z-compound-depth').value && nearEdge.pstyle('z-compound-depth').value === ele.pstyle('z-compound-depth').value) {
21616 for (var i = 0; i < near.length; i++) {
21617 if (near[i].isEdge()) {
21618 near[i] = ele;
21619 nearEdge = ele;
21620 minSqDist = sqDist != null ? sqDist : minSqDist;
21621 break;
21622 }
21623 }
21624 }
21625 } else {
21626 near.push(ele);
21627 nearEdge = ele;
21628 minSqDist = sqDist != null ? sqDist : minSqDist;
21629 }
21630 }
21631 }
21632
21633 function checkNode(node) {
21634 var width = node.outerWidth() + 2 * nodeThreshold;
21635 var height = node.outerHeight() + 2 * nodeThreshold;
21636 var hw = width / 2;
21637 var hh = height / 2;
21638 var pos = node.position();
21639
21640 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21641 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21642 ) {
21643 var shape = r.nodeShapes[self.getNodeShape(node)];
21644
21645 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21646 addEle(node, 0);
21647 return true;
21648 }
21649 }
21650 }
21651
21652 function checkEdge(edge) {
21653 var _p = edge._private;
21654 var rs = _p.rscratch;
21655 var styleWidth = edge.pstyle('width').pfValue;
21656 var scale = edge.pstyle('arrow-scale').value;
21657 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21658
21659 var widthSq = width * width;
21660 var width2 = width * 2;
21661 var src = _p.source;
21662 var tgt = _p.target;
21663 var sqDist;
21664
21665 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21666 var pts = rs.allpts;
21667
21668 for (var i = 0; i + 3 < pts.length; i += 2) {
21669 if (inLineVicinity(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], width2) && widthSq > (sqDist = sqdistToFiniteLine(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3]))) {
21670 addEle(edge, sqDist);
21671 return true;
21672 }
21673 }
21674 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21675 var pts = rs.allpts;
21676
21677 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21678 if (inBezierVicinity(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5], width2) && widthSq > (sqDist = sqdistToQuadraticBezier(x, y, pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5]))) {
21679 addEle(edge, sqDist);
21680 return true;
21681 }
21682 }
21683 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21684
21685
21686 var src = src || _p.source;
21687 var tgt = tgt || _p.target;
21688 var arSize = self.getArrowWidth(styleWidth, scale);
21689 var arrows = [{
21690 name: 'source',
21691 x: rs.arrowStartX,
21692 y: rs.arrowStartY,
21693 angle: rs.srcArrowAngle
21694 }, {
21695 name: 'target',
21696 x: rs.arrowEndX,
21697 y: rs.arrowEndY,
21698 angle: rs.tgtArrowAngle
21699 }, {
21700 name: 'mid-source',
21701 x: rs.midX,
21702 y: rs.midY,
21703 angle: rs.midsrcArrowAngle
21704 }, {
21705 name: 'mid-target',
21706 x: rs.midX,
21707 y: rs.midY,
21708 angle: rs.midtgtArrowAngle
21709 }];
21710
21711 for (var i = 0; i < arrows.length; i++) {
21712 var ar = arrows[i];
21713 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21714 var edgeWidth = edge.pstyle('width').pfValue;
21715
21716 if (shape.roughCollide(x, y, arSize, ar.angle, {
21717 x: ar.x,
21718 y: ar.y
21719 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21720 x: ar.x,
21721 y: ar.y
21722 }, edgeWidth, edgeThreshold)) {
21723 addEle(edge);
21724 return true;
21725 }
21726 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21727
21728
21729 if (hasCompounds && near.length > 0) {
21730 checkNode(src);
21731 checkNode(tgt);
21732 }
21733 }
21734
21735 function preprop(obj, name, pre) {
21736 return getPrefixedProperty(obj, name, pre);
21737 }
21738
21739 function checkLabel(ele, prefix) {
21740 var _p = ele._private;
21741 var th = labelThreshold;
21742 var prefixDash;
21743
21744 if (prefix) {
21745 prefixDash = prefix + '-';
21746 } else {
21747 prefixDash = '';
21748 }
21749
21750 ele.boundingBox();
21751 var bb = _p.labelBounds[prefix || 'main'];
21752 var text = ele.pstyle(prefixDash + 'label').value;
21753 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21754
21755 if (!eventsEnabled || !text) {
21756 return;
21757 }
21758
21759 var lx = preprop(_p.rscratch, 'labelX', prefix);
21760 var ly = preprop(_p.rscratch, 'labelY', prefix);
21761 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21762 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21763 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21764 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21765
21766 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21767
21768 var ly1 = bb.y1 - th - oy;
21769 var ly2 = bb.y2 + th - oy;
21770
21771 if (theta) {
21772 var cos = Math.cos(theta);
21773 var sin = Math.sin(theta);
21774
21775 var rotate = function rotate(x, y) {
21776 x = x - lx;
21777 y = y - ly;
21778 return {
21779 x: x * cos - y * sin + lx,
21780 y: x * sin + y * cos + ly
21781 };
21782 };
21783
21784 var px1y1 = rotate(lx1, ly1);
21785 var px1y2 = rotate(lx1, ly2);
21786 var px2y1 = rotate(lx2, ly1);
21787 var px2y2 = rotate(lx2, ly2);
21788 var points = [// with the margin added after the rotation is applied
21789 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21790
21791 if (pointInsidePolygonPoints(x, y, points)) {
21792 addEle(ele);
21793 return true;
21794 }
21795 } else {
21796 // do a cheaper bb check
21797 if (inBoundingBox(bb, x, y)) {
21798 addEle(ele);
21799 return true;
21800 }
21801 }
21802 }
21803
21804 for (var i = eles.length - 1; i >= 0; i--) {
21805 // reverse order for precedence
21806 var ele = eles[i];
21807
21808 if (ele.isNode()) {
21809 checkNode(ele) || checkLabel(ele);
21810 } else {
21811 // then edge
21812 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21813 }
21814 }
21815
21816 return near;
21817}; // 'Give me everything from this box'
21818
21819
21820BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21821 var eles = this.getCachedZSortedEles().interactive;
21822 var box = [];
21823 var x1c = Math.min(x1, x2);
21824 var x2c = Math.max(x1, x2);
21825 var y1c = Math.min(y1, y2);
21826 var y2c = Math.max(y1, y2);
21827 x1 = x1c;
21828 x2 = x2c;
21829 y1 = y1c;
21830 y2 = y2c;
21831 var boxBb = makeBoundingBox({
21832 x1: x1,
21833 y1: y1,
21834 x2: x2,
21835 y2: y2
21836 });
21837
21838 for (var e = 0; e < eles.length; e++) {
21839 var ele = eles[e];
21840
21841 if (ele.isNode()) {
21842 var node = ele;
21843 var nodeBb = node.boundingBox({
21844 includeNodes: true,
21845 includeEdges: false,
21846 includeLabels: false
21847 });
21848
21849 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21850 box.push(node);
21851 }
21852 } else {
21853 var edge = ele;
21854 var _p = edge._private;
21855 var rs = _p.rscratch;
21856
21857 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21858 continue;
21859 }
21860
21861 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21862 continue;
21863 }
21864
21865 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21866 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21867 var allInside = true;
21868
21869 for (var i = 0; i < pts.length; i++) {
21870 if (!pointInBoundingBox(boxBb, pts[i])) {
21871 allInside = false;
21872 break;
21873 }
21874 }
21875
21876 if (allInside) {
21877 box.push(edge);
21878 }
21879 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
21880 box.push(edge);
21881 }
21882 }
21883 }
21884
21885 return box;
21886};
21887
21888var BRp$2 = {};
21889
21890BRp$2.calculateArrowAngles = function (edge) {
21891 var rs = edge._private.rscratch;
21892 var isHaystack = rs.edgeType === 'haystack';
21893 var isBezier = rs.edgeType === 'bezier';
21894 var isMultibezier = rs.edgeType === 'multibezier';
21895 var isSegments = rs.edgeType === 'segments';
21896 var isCompound = rs.edgeType === 'compound';
21897 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
21898
21899 var dispX, dispY;
21900 var startX, startY, endX, endY, midX, midY;
21901
21902 if (isHaystack) {
21903 startX = rs.haystackPts[0];
21904 startY = rs.haystackPts[1];
21905 endX = rs.haystackPts[2];
21906 endY = rs.haystackPts[3];
21907 } else {
21908 startX = rs.arrowStartX;
21909 startY = rs.arrowStartY;
21910 endX = rs.arrowEndX;
21911 endY = rs.arrowEndY;
21912 }
21913
21914 midX = rs.midX;
21915 midY = rs.midY; // source
21916 //
21917
21918 if (isSegments) {
21919 dispX = startX - rs.segpts[0];
21920 dispY = startY - rs.segpts[1];
21921 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21922 var pts = rs.allpts;
21923 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
21924 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
21925 dispX = startX - bX;
21926 dispY = startY - bY;
21927 } else {
21928 dispX = startX - midX;
21929 dispY = startY - midY;
21930 }
21931
21932 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
21933 //
21934
21935 var midX = rs.midX;
21936 var midY = rs.midY;
21937
21938 if (isHaystack) {
21939 midX = (startX + endX) / 2;
21940 midY = (startY + endY) / 2;
21941 }
21942
21943 dispX = endX - startX;
21944 dispY = endY - startY;
21945
21946 if (isSegments) {
21947 var pts = rs.allpts;
21948
21949 if (pts.length / 2 % 2 === 0) {
21950 var i2 = pts.length / 2;
21951 var i1 = i2 - 2;
21952 dispX = pts[i2] - pts[i1];
21953 dispY = pts[i2 + 1] - pts[i1 + 1];
21954 } else {
21955 var i2 = pts.length / 2 - 1;
21956 var i1 = i2 - 2;
21957 var i3 = i2 + 2;
21958 dispX = pts[i2] - pts[i1];
21959 dispY = pts[i2 + 1] - pts[i1 + 1];
21960 }
21961 } else if (isMultibezier || isCompound || isSelf) {
21962 var pts = rs.allpts;
21963 var cpts = rs.ctrlpts;
21964 var bp0x, bp0y;
21965 var bp1x, bp1y;
21966
21967 if (cpts.length / 2 % 2 === 0) {
21968 var p0 = pts.length / 2 - 1; // startpt
21969
21970 var ic = p0 + 2;
21971 var p1 = ic + 2;
21972 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
21973 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
21974 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
21975 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
21976 } else {
21977 var ic = pts.length / 2 - 1; // ctrpt
21978
21979 var p0 = ic - 2; // startpt
21980
21981 var p1 = ic + 2; // endpt
21982
21983 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
21984 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
21985 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
21986 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
21987 }
21988
21989 dispX = bp1x - bp0x;
21990 dispY = bp1y - bp0y;
21991 }
21992
21993 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
21994 rs.midDispX = dispX;
21995 rs.midDispY = dispY; // mid source
21996 //
21997
21998 dispX *= -1;
21999 dispY *= -1;
22000
22001 if (isSegments) {
22002 var pts = rs.allpts;
22003
22004 if (pts.length / 2 % 2 === 0) ; else {
22005 var i2 = pts.length / 2 - 1;
22006 var i3 = i2 + 2;
22007 dispX = -(pts[i3] - pts[i2]);
22008 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22009 }
22010 }
22011
22012 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22013 //
22014
22015 if (isSegments) {
22016 dispX = endX - rs.segpts[rs.segpts.length - 2];
22017 dispY = endY - rs.segpts[rs.segpts.length - 1];
22018 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22019 var pts = rs.allpts;
22020 var l = pts.length;
22021 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22022 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22023 dispX = endX - bX;
22024 dispY = endY - bY;
22025 } else {
22026 dispX = endX - midX;
22027 dispY = endY - midY;
22028 }
22029
22030 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22031};
22032
22033BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22034 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22035 var cachedVal = cache[edgeWidth + ', ' + scale];
22036
22037 if (cachedVal) {
22038 return cachedVal;
22039 }
22040
22041 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22042 cache[edgeWidth + ', ' + scale] = cachedVal;
22043 return cachedVal;
22044};
22045
22046var BRp$3 = {};
22047
22048BRp$3.findHaystackPoints = function (edges) {
22049 for (var i = 0; i < edges.length; i++) {
22050 var edge = edges[i];
22051 var _p = edge._private;
22052 var rs = _p.rscratch;
22053
22054 if (!rs.haystack) {
22055 var angle = Math.random() * 2 * Math.PI;
22056 rs.source = {
22057 x: Math.cos(angle),
22058 y: Math.sin(angle)
22059 };
22060 angle = Math.random() * 2 * Math.PI;
22061 rs.target = {
22062 x: Math.cos(angle),
22063 y: Math.sin(angle)
22064 };
22065 }
22066
22067 var src = _p.source;
22068 var tgt = _p.target;
22069 var srcPos = src.position();
22070 var tgtPos = tgt.position();
22071 var srcW = src.width();
22072 var tgtW = tgt.width();
22073 var srcH = src.height();
22074 var tgtH = tgt.height();
22075 var radius = edge.pstyle('haystack-radius').value;
22076 var halfRadius = radius / 2; // b/c have to half width/height
22077
22078 rs.haystackPts = rs.allpts = [rs.source.x * srcW * halfRadius + srcPos.x, rs.source.y * srcH * halfRadius + srcPos.y, rs.target.x * tgtW * halfRadius + tgtPos.x, rs.target.y * tgtH * halfRadius + tgtPos.y];
22079 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22080 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22081
22082 rs.edgeType = 'haystack';
22083 rs.haystack = true;
22084 this.storeEdgeProjections(edge);
22085 this.calculateArrowAngles(edge);
22086 this.recalculateEdgeLabelProjections(edge);
22087 this.calculateLabelAngles(edge);
22088 }
22089};
22090
22091BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22092 // Segments (multiple straight lines)
22093 var rs = edge._private.rscratch;
22094 var posPts = pairInfo.posPts,
22095 intersectionPts = pairInfo.intersectionPts,
22096 vectorNormInverse = pairInfo.vectorNormInverse;
22097 var edgeDistances = edge.pstyle('edge-distances').value;
22098 var segmentWs = edge.pstyle('segment-weights');
22099 var segmentDs = edge.pstyle('segment-distances');
22100 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22101 rs.edgeType = 'segments';
22102 rs.segpts = [];
22103
22104 for (var s = 0; s < segmentsN; s++) {
22105 var w = segmentWs.pfValue[s];
22106 var d = segmentDs.pfValue[s];
22107 var w1 = 1 - w;
22108 var w2 = w;
22109 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22110 var adjustedMidpt = {
22111 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22112 y: midptPts.y1 * w1 + midptPts.y2 * w2
22113 };
22114 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22115 }
22116};
22117
22118BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22119 // Self-edge
22120 var rs = edge._private.rscratch;
22121 var dirCounts = pairInfo.dirCounts,
22122 srcPos = pairInfo.srcPos;
22123 var ctrlptDists = edge.pstyle('control-point-distances');
22124 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22125 var loopDir = edge.pstyle('loop-direction').pfValue;
22126 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22127 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22128 rs.edgeType = 'self';
22129 var j = i;
22130 var loopDist = stepSize;
22131
22132 if (edgeIsUnbundled) {
22133 j = 0;
22134 loopDist = ctrlptDist;
22135 }
22136
22137 var loopAngle = loopDir - Math.PI / 2;
22138 var outAngle = loopAngle - loopSwp / 2;
22139 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22140
22141 var dc = String(loopDir + '_' + loopSwp);
22142 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22143 rs.ctrlpts = [srcPos.x + Math.cos(outAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.y + Math.sin(outAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.x + Math.cos(inAngle) * 1.4 * loopDist * (j / 3 + 1), srcPos.y + Math.sin(inAngle) * 1.4 * loopDist * (j / 3 + 1)];
22144};
22145
22146BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22147 // Compound edge
22148 var rs = edge._private.rscratch;
22149 rs.edgeType = 'compound';
22150 var srcPos = pairInfo.srcPos,
22151 tgtPos = pairInfo.tgtPos,
22152 srcW = pairInfo.srcW,
22153 srcH = pairInfo.srcH,
22154 tgtW = pairInfo.tgtW,
22155 tgtH = pairInfo.tgtH;
22156 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22157 var ctrlptDists = edge.pstyle('control-point-distances');
22158 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22159 var j = i;
22160 var loopDist = stepSize;
22161
22162 if (edgeIsUnbundled) {
22163 j = 0;
22164 loopDist = ctrlptDist;
22165 }
22166
22167 var loopW = 50;
22168 var loopaPos = {
22169 x: srcPos.x - srcW / 2,
22170 y: srcPos.y - srcH / 2
22171 };
22172 var loopbPos = {
22173 x: tgtPos.x - tgtW / 2,
22174 y: tgtPos.y - tgtH / 2
22175 };
22176 var loopPos = {
22177 x: Math.min(loopaPos.x, loopbPos.x),
22178 y: Math.min(loopaPos.y, loopbPos.y)
22179 }; // avoids cases with impossible beziers
22180
22181 var minCompoundStretch = 0.5;
22182 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22183 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22184 rs.ctrlpts = [loopPos.x, loopPos.y - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchA, loopPos.x - (1 + Math.pow(loopW, 1.12) / 100) * loopDist * (j / 3 + 1) * compoundStretchB, loopPos.y];
22185};
22186
22187BRp$3.findStraightEdgePoints = function (edge) {
22188 // Straight edge within bundle
22189 edge._private.rscratch.edgeType = 'straight';
22190};
22191
22192BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22193 var rs = edge._private.rscratch;
22194 var vectorNormInverse = pairInfo.vectorNormInverse,
22195 posPts = pairInfo.posPts,
22196 intersectionPts = pairInfo.intersectionPts;
22197 var edgeDistances = edge.pstyle('edge-distances').value;
22198 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22199 var ctrlptDists = edge.pstyle('control-point-distances');
22200 var ctrlptWs = edge.pstyle('control-point-weights');
22201 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22202 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22203 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22204
22205 var multi = edgeIsUnbundled;
22206 rs.edgeType = multi ? 'multibezier' : 'bezier';
22207 rs.ctrlpts = [];
22208
22209 for (var b = 0; b < bezierN; b++) {
22210 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22211 var manctrlptDist = void 0;
22212 var sign = signum(normctrlptDist);
22213
22214 if (multi) {
22215 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22216
22217 ctrlptWeight = ctrlptWs.value[b];
22218 }
22219
22220 if (edgeIsUnbundled) {
22221 // multi or single unbundled
22222 manctrlptDist = ctrlptDist;
22223 } else {
22224 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22225 }
22226
22227 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22228 var w1 = 1 - ctrlptWeight;
22229 var w2 = ctrlptWeight;
22230 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22231 var adjustedMidpt = {
22232 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22233 y: midptPts.y1 * w1 + midptPts.y2 * w2
22234 };
22235 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22236 }
22237};
22238
22239BRp$3.findTaxiPoints = function (edge, pairInfo) {
22240 // Taxicab geometry with two turns maximum
22241 var rs = edge._private.rscratch;
22242 rs.edgeType = 'segments';
22243 var VERTICAL = 'vertical';
22244 var HORIZONTAL = 'horizontal';
22245 var LEFTWARD = 'leftward';
22246 var RIGHTWARD = 'rightward';
22247 var DOWNWARD = 'downward';
22248 var UPWARD = 'upward';
22249 var AUTO = 'auto';
22250 var posPts = pairInfo.posPts,
22251 srcW = pairInfo.srcW,
22252 srcH = pairInfo.srcH,
22253 tgtW = pairInfo.tgtW,
22254 tgtH = pairInfo.tgtH;
22255 var edgeDistances = edge.pstyle('edge-distances').value;
22256 var dIncludesNodeBody = edgeDistances !== 'node-position';
22257 var taxiDir = edge.pstyle('taxi-direction').value;
22258 var rawTaxiDir = taxiDir; // unprocessed value
22259
22260 var taxiTurn = edge.pstyle('taxi-turn');
22261 var turnIsPercent = taxiTurn.units === '%';
22262 var taxiTurnPfVal = taxiTurn.pfValue;
22263 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22264
22265 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22266 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22267 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22268 var pdx = posPts.x2 - posPts.x1;
22269 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22270
22271 var subDWH = function subDWH(dxy, dwh) {
22272 if (dxy > 0) {
22273 return Math.max(dxy - dwh, 0);
22274 } else {
22275 return Math.min(dxy + dwh, 0);
22276 }
22277 };
22278
22279 var dx = subDWH(pdx, dw);
22280 var dy = subDWH(pdy, dh);
22281 var isExplicitDir = false;
22282
22283 if (rawTaxiDir === AUTO) {
22284 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22285 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22286 taxiDir = VERTICAL;
22287 isExplicitDir = true;
22288 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22289 taxiDir = HORIZONTAL;
22290 isExplicitDir = true;
22291 }
22292
22293 var isVert = taxiDir === VERTICAL;
22294 var l = isVert ? dy : dx;
22295 var pl = isVert ? pdy : pdx;
22296 var sgnL = signum(pl);
22297 var forcedDir = false;
22298
22299 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22300 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22301 sgnL *= -1;
22302 l = sgnL * Math.abs(l);
22303 forcedDir = true;
22304 }
22305
22306 var d;
22307
22308 if (turnIsPercent) {
22309 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22310 d = p * l;
22311 } else {
22312 var k = taxiTurnPfVal < 0 ? l : 0;
22313 d = k + taxiTurnPfVal * sgnL;
22314 }
22315
22316 var getIsTooClose = function getIsTooClose(d) {
22317 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22318 };
22319
22320 var isTooCloseSrc = getIsTooClose(d);
22321 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22322 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22323
22324 if (isTooClose && !forcedDir) {
22325 // non-ideal routing
22326 if (isVert) {
22327 // vertical fallbacks
22328 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22329 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22330
22331 if (lShapeInsideSrc) {
22332 // horizontal Z-shape (direction not respected)
22333 var x = (posPts.x1 + posPts.x2) / 2;
22334 var y1 = posPts.y1,
22335 y2 = posPts.y2;
22336 rs.segpts = [x, y1, x, y2];
22337 } else if (lShapeInsideTgt) {
22338 // vertical Z-shape (distance not respected)
22339 var y = (posPts.y1 + posPts.y2) / 2;
22340 var x1 = posPts.x1,
22341 x2 = posPts.x2;
22342 rs.segpts = [x1, y, x2, y];
22343 } else {
22344 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22345 rs.segpts = [posPts.x1, posPts.y2];
22346 }
22347 } else {
22348 // horizontal fallbacks
22349 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22350
22351 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22352
22353 if (_lShapeInsideSrc) {
22354 // vertical Z-shape (direction not respected)
22355 var _y = (posPts.y1 + posPts.y2) / 2;
22356
22357 var _x = posPts.x1,
22358 _x2 = posPts.x2;
22359 rs.segpts = [_x, _y, _x2, _y];
22360 } else if (_lShapeInsideTgt) {
22361 // horizontal Z-shape (turn distance not respected)
22362 var _x3 = (posPts.x1 + posPts.x2) / 2;
22363
22364 var _y2 = posPts.y1,
22365 _y3 = posPts.y2;
22366 rs.segpts = [_x3, _y2, _x3, _y3];
22367 } else {
22368 // L-shape (turn distance not respected, but works well for tree siblings)
22369 rs.segpts = [posPts.x2, posPts.y1];
22370 }
22371 }
22372 } else {
22373 // ideal routing
22374 if (isVert) {
22375 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22376
22377 var _x4 = posPts.x1,
22378 _x5 = posPts.x2;
22379 rs.segpts = [_x4, _y4, _x5, _y4];
22380 } else {
22381 // horizontal
22382 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22383
22384 var _y5 = posPts.y1,
22385 _y6 = posPts.y2;
22386 rs.segpts = [_x6, _y5, _x6, _y6];
22387 }
22388 }
22389};
22390
22391BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22392 var rs = edge._private.rscratch; // can only correct beziers for now...
22393
22394 if (rs.edgeType === 'bezier') {
22395 var srcPos = pairInfo.srcPos,
22396 tgtPos = pairInfo.tgtPos,
22397 srcW = pairInfo.srcW,
22398 srcH = pairInfo.srcH,
22399 tgtW = pairInfo.tgtW,
22400 tgtH = pairInfo.tgtH,
22401 srcShape = pairInfo.srcShape,
22402 tgtShape = pairInfo.tgtShape;
22403 var badStart = !number(rs.startX) || !number(rs.startY);
22404 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22405 var badEnd = !number(rs.endX) || !number(rs.endY);
22406 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22407 var minCpADistFactor = 3;
22408 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22409 var minCpADist = minCpADistFactor * arrowW;
22410 var startACpDist = dist({
22411 x: rs.ctrlpts[0],
22412 y: rs.ctrlpts[1]
22413 }, {
22414 x: rs.startX,
22415 y: rs.startY
22416 });
22417 var closeStartACp = startACpDist < minCpADist;
22418 var endACpDist = dist({
22419 x: rs.ctrlpts[0],
22420 y: rs.ctrlpts[1]
22421 }, {
22422 x: rs.endX,
22423 y: rs.endY
22424 });
22425 var closeEndACp = endACpDist < minCpADist;
22426 var overlapping = false;
22427
22428 if (badStart || badAStart || closeStartACp) {
22429 overlapping = true; // project control point along line from src centre to outside the src shape
22430 // (otherwise intersection will yield nothing)
22431
22432 var cpD = {
22433 // delta
22434 x: rs.ctrlpts[0] - srcPos.x,
22435 y: rs.ctrlpts[1] - srcPos.y
22436 };
22437 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22438
22439 var cpM = {
22440 // normalised delta
22441 x: cpD.x / cpL,
22442 y: cpD.y / cpL
22443 };
22444 var radius = Math.max(srcW, srcH);
22445 var cpProj = {
22446 // *2 radius guarantees outside shape
22447 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22448 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22449 };
22450 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22451
22452 if (closeStartACp) {
22453 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22454 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22455 } else {
22456 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22457 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22458 }
22459 }
22460
22461 if (badEnd || badAEnd || closeEndACp) {
22462 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22463 // (otherwise intersection will yield nothing)
22464
22465 var _cpD = {
22466 // delta
22467 x: rs.ctrlpts[0] - tgtPos.x,
22468 y: rs.ctrlpts[1] - tgtPos.y
22469 };
22470
22471 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22472
22473
22474 var _cpM = {
22475 // normalised delta
22476 x: _cpD.x / _cpL,
22477 y: _cpD.y / _cpL
22478 };
22479
22480 var _radius = Math.max(srcW, srcH);
22481
22482 var _cpProj = {
22483 // *2 radius guarantees outside shape
22484 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22485 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22486 };
22487 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22488
22489 if (closeEndACp) {
22490 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22491 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22492 } else {
22493 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22494 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22495 }
22496 }
22497
22498 if (overlapping) {
22499 // recalc endpts
22500 this.findEndpoints(edge);
22501 }
22502 }
22503};
22504
22505BRp$3.storeAllpts = function (edge) {
22506 var rs = edge._private.rscratch;
22507
22508 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22509 rs.allpts = [];
22510 rs.allpts.push(rs.startX, rs.startY);
22511
22512 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22513 // ctrl pt itself
22514 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22515
22516 if (b + 3 < rs.ctrlpts.length) {
22517 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22518 }
22519 }
22520
22521 rs.allpts.push(rs.endX, rs.endY);
22522 var m, mt;
22523
22524 if (rs.ctrlpts.length / 2 % 2 === 0) {
22525 m = rs.allpts.length / 2 - 1;
22526 rs.midX = rs.allpts[m];
22527 rs.midY = rs.allpts[m + 1];
22528 } else {
22529 m = rs.allpts.length / 2 - 3;
22530 mt = 0.5;
22531 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22532 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22533 }
22534 } else if (rs.edgeType === 'straight') {
22535 // need to calc these after endpts
22536 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22537
22538 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22539 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22540 } else if (rs.edgeType === 'segments') {
22541 rs.allpts = [];
22542 rs.allpts.push(rs.startX, rs.startY);
22543 rs.allpts.push.apply(rs.allpts, rs.segpts);
22544 rs.allpts.push(rs.endX, rs.endY);
22545
22546 if (rs.segpts.length % 4 === 0) {
22547 var i2 = rs.segpts.length / 2;
22548 var i1 = i2 - 2;
22549 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22550 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22551 } else {
22552 var _i = rs.segpts.length / 2 - 1;
22553
22554 rs.midX = rs.segpts[_i];
22555 rs.midY = rs.segpts[_i + 1];
22556 }
22557 }
22558};
22559
22560BRp$3.checkForInvalidEdgeWarning = function (edge) {
22561 var rs = edge[0]._private.rscratch;
22562
22563 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22564 rs.loggedErr = false;
22565 } else {
22566 if (!rs.loggedErr) {
22567 rs.loggedErr = true;
22568 warn('Edge `' + edge.id() + '` has invalid endpoints and so it is impossible to draw. Adjust your edge style (e.g. control points) accordingly or use an alternative edge type. This is expected behaviour when the source node and the target node overlap.');
22569 }
22570 }
22571};
22572
22573BRp$3.findEdgeControlPoints = function (edges) {
22574 var _this = this;
22575
22576 if (!edges || edges.length === 0) {
22577 return;
22578 }
22579
22580 var r = this;
22581 var cy = r.cy;
22582 var hasCompounds = cy.hasCompoundNodes();
22583 var hashTable = {
22584 map: new Map$1(),
22585 get: function get(pairId) {
22586 var map2 = this.map.get(pairId[0]);
22587
22588 if (map2 != null) {
22589 return map2.get(pairId[1]);
22590 } else {
22591 return null;
22592 }
22593 },
22594 set: function set(pairId, val) {
22595 var map2 = this.map.get(pairId[0]);
22596
22597 if (map2 == null) {
22598 map2 = new Map$1();
22599 this.map.set(pairId[0], map2);
22600 }
22601
22602 map2.set(pairId[1], val);
22603 }
22604 };
22605 var pairIds = [];
22606 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22607
22608 for (var i = 0; i < edges.length; i++) {
22609 var edge = edges[i];
22610 var _p = edge._private;
22611 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22612 // they shouldn't take up space
22613
22614 if (edge.removed() || !edge.takesUpSpace()) {
22615 continue;
22616 }
22617
22618 if (curveStyle === 'haystack') {
22619 haystackEdges.push(edge);
22620 continue;
22621 }
22622
22623 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
22624 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22625 var src = _p.source;
22626 var tgt = _p.target;
22627 var srcIndex = src.poolIndex();
22628 var tgtIndex = tgt.poolIndex();
22629 var pairId = [srcIndex, tgtIndex].sort();
22630 var tableEntry = hashTable.get(pairId);
22631
22632 if (tableEntry == null) {
22633 tableEntry = {
22634 eles: []
22635 };
22636 hashTable.set(pairId, tableEntry);
22637 pairIds.push(pairId);
22638 }
22639
22640 tableEntry.eles.push(edge);
22641
22642 if (edgeIsUnbundled) {
22643 tableEntry.hasUnbundled = true;
22644 }
22645
22646 if (edgeIsBezier) {
22647 tableEntry.hasBezier = true;
22648 }
22649 } // for each pair (src, tgt), create the ctrl pts
22650 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22651
22652
22653 var _loop = function _loop(p) {
22654 var pairId = pairIds[p];
22655 var pairInfo = hashTable.get(pairId);
22656 var swappedpairInfo = void 0;
22657
22658 if (!pairInfo.hasUnbundled) {
22659 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22660 return e.isBundledBezier();
22661 });
22662 clearArray(pairInfo.eles);
22663 pllEdges.forEach(function (edge) {
22664 return pairInfo.eles.push(edge);
22665 }); // for each pair id, the edges should be sorted by index
22666
22667 pairInfo.eles.sort(function (edge1, edge2) {
22668 return edge1.poolIndex() - edge2.poolIndex();
22669 });
22670 }
22671
22672 var firstEdge = pairInfo.eles[0];
22673 var src = firstEdge.source();
22674 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22675
22676 if (src.poolIndex() > tgt.poolIndex()) {
22677 var temp = src;
22678 src = tgt;
22679 tgt = temp;
22680 }
22681
22682 var srcPos = pairInfo.srcPos = src.position();
22683 var tgtPos = pairInfo.tgtPos = tgt.position();
22684 var srcW = pairInfo.srcW = src.outerWidth();
22685 var srcH = pairInfo.srcH = src.outerHeight();
22686 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22687 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22688
22689 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22690
22691 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22692
22693 pairInfo.dirCounts = {
22694 'north': 0,
22695 'west': 0,
22696 'south': 0,
22697 'east': 0,
22698 'northwest': 0,
22699 'southwest': 0,
22700 'northeast': 0,
22701 'southeast': 0
22702 };
22703
22704 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22705 var _edge = pairInfo.eles[_i2];
22706 var rs = _edge[0]._private.rscratch;
22707
22708 var _curveStyle = _edge.pstyle('curve-style').value;
22709
22710 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22711
22712
22713 var edgeIsSwapped = !src.same(_edge.source());
22714
22715 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22716 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22717
22718 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22719 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22720
22721 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22722 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22723 var intersectionPts = pairInfo.intersectionPts = {
22724 x1: srcOutside[0],
22725 x2: tgtOutside[0],
22726 y1: srcOutside[1],
22727 y2: tgtOutside[1]
22728 };
22729 var posPts = pairInfo.posPts = {
22730 x1: srcPos.x,
22731 x2: tgtPos.x,
22732 y1: srcPos.y,
22733 y2: tgtPos.y
22734 };
22735 var dy = tgtOutside[1] - srcOutside[1];
22736 var dx = tgtOutside[0] - srcOutside[0];
22737 var l = Math.sqrt(dx * dx + dy * dy);
22738 var vector = pairInfo.vector = {
22739 x: dx,
22740 y: dy
22741 };
22742 var vectorNorm = pairInfo.vectorNorm = {
22743 x: vector.x / l,
22744 y: vector.y / l
22745 };
22746 var vectorNormInverse = {
22747 x: -vectorNorm.y,
22748 y: vectorNorm.x
22749 }; // if node shapes overlap, then no ctrl pts to draw
22750
22751 pairInfo.nodesOverlap = !number(l) || tgtShape.checkPoint(srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y) || srcShape.checkPoint(tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y);
22752 pairInfo.vectorNormInverse = vectorNormInverse;
22753 swappedpairInfo = {
22754 nodesOverlap: pairInfo.nodesOverlap,
22755 dirCounts: pairInfo.dirCounts,
22756 calculatedIntersection: true,
22757 hasBezier: pairInfo.hasBezier,
22758 hasUnbundled: pairInfo.hasUnbundled,
22759 eles: pairInfo.eles,
22760 srcPos: tgtPos,
22761 tgtPos: srcPos,
22762 srcW: tgtW,
22763 srcH: tgtH,
22764 tgtW: srcW,
22765 tgtH: srcH,
22766 srcIntn: tgtIntn,
22767 tgtIntn: srcIntn,
22768 srcShape: tgtShape,
22769 tgtShape: srcShape,
22770 posPts: {
22771 x1: posPts.x2,
22772 y1: posPts.y2,
22773 x2: posPts.x1,
22774 y2: posPts.y1
22775 },
22776 intersectionPts: {
22777 x1: intersectionPts.x2,
22778 y1: intersectionPts.y2,
22779 x2: intersectionPts.x1,
22780 y2: intersectionPts.y1
22781 },
22782 vector: {
22783 x: -vector.x,
22784 y: -vector.y
22785 },
22786 vectorNorm: {
22787 x: -vectorNorm.x,
22788 y: -vectorNorm.y
22789 },
22790 vectorNormInverse: {
22791 x: -vectorNormInverse.x,
22792 y: -vectorNormInverse.y
22793 }
22794 };
22795 }
22796
22797 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22798 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22799 rs.srcIntn = passedPairInfo.srcIntn;
22800 rs.tgtIntn = passedPairInfo.tgtIntn;
22801
22802 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22803 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22804 } else if (src === tgt) {
22805 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22806 } else if (_curveStyle === 'segments') {
22807 _this.findSegmentsPoints(_edge, passedPairInfo);
22808 } else if (_curveStyle === 'taxi') {
22809 _this.findTaxiPoints(_edge, passedPairInfo);
22810 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22811 _this.findStraightEdgePoints(_edge);
22812 } else {
22813 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22814 }
22815
22816 _this.findEndpoints(_edge);
22817
22818 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22819
22820 _this.checkForInvalidEdgeWarning(_edge);
22821
22822 _this.storeAllpts(_edge);
22823
22824 _this.storeEdgeProjections(_edge);
22825
22826 _this.calculateArrowAngles(_edge);
22827
22828 _this.recalculateEdgeLabelProjections(_edge);
22829
22830 _this.calculateLabelAngles(_edge);
22831 } // for pair edges
22832
22833 };
22834
22835 for (var p = 0; p < pairIds.length; p++) {
22836 _loop(p);
22837 } // for pair ids
22838 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22839
22840
22841 this.findHaystackPoints(haystackEdges);
22842};
22843
22844function getPts(pts) {
22845 var retPts = [];
22846
22847 if (pts == null) {
22848 return;
22849 }
22850
22851 for (var i = 0; i < pts.length; i += 2) {
22852 var x = pts[i];
22853 var y = pts[i + 1];
22854 retPts.push({
22855 x: x,
22856 y: y
22857 });
22858 }
22859
22860 return retPts;
22861}
22862
22863BRp$3.getSegmentPoints = function (edge) {
22864 var rs = edge[0]._private.rscratch;
22865 var type = rs.edgeType;
22866
22867 if (type === 'segments') {
22868 this.recalculateRenderedStyle(edge);
22869 return getPts(rs.segpts);
22870 }
22871};
22872
22873BRp$3.getControlPoints = function (edge) {
22874 var rs = edge[0]._private.rscratch;
22875 var type = rs.edgeType;
22876
22877 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
22878 this.recalculateRenderedStyle(edge);
22879 return getPts(rs.ctrlpts);
22880 }
22881};
22882
22883BRp$3.getEdgeMidpoint = function (edge) {
22884 var rs = edge[0]._private.rscratch;
22885 this.recalculateRenderedStyle(edge);
22886 return {
22887 x: rs.midX,
22888 y: rs.midY
22889 };
22890};
22891
22892var BRp$4 = {};
22893
22894BRp$4.manualEndptToPx = function (node, prop) {
22895 var r = this;
22896 var npos = node.position();
22897 var w = node.outerWidth();
22898 var h = node.outerHeight();
22899
22900 if (prop.value.length === 2) {
22901 var p = [prop.pfValue[0], prop.pfValue[1]];
22902
22903 if (prop.units[0] === '%') {
22904 p[0] = p[0] * w;
22905 }
22906
22907 if (prop.units[1] === '%') {
22908 p[1] = p[1] * h;
22909 }
22910
22911 p[0] += npos.x;
22912 p[1] += npos.y;
22913 return p;
22914 } else {
22915 var angle = prop.pfValue[0];
22916 angle = -Math.PI / 2 + angle; // start at 12 o'clock
22917
22918 var l = 2 * Math.max(w, h);
22919 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
22920 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
22921 }
22922};
22923
22924BRp$4.findEndpoints = function (edge) {
22925 var r = this;
22926 var intersect;
22927 var source = edge.source()[0];
22928 var target = edge.target()[0];
22929 var srcPos = source.position();
22930 var tgtPos = target.position();
22931 var tgtArShape = edge.pstyle('target-arrow-shape').value;
22932 var srcArShape = edge.pstyle('source-arrow-shape').value;
22933 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
22934 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
22935 var curveStyle = edge.pstyle('curve-style').value;
22936 var rs = edge._private.rscratch;
22937 var et = rs.edgeType;
22938 var taxi = curveStyle === 'taxi';
22939 var self = et === 'self' || et === 'compound';
22940 var bezier = et === 'bezier' || et === 'multibezier' || self;
22941 var multi = et !== 'bezier';
22942 var lines = et === 'straight' || et === 'segments';
22943 var segments = et === 'segments';
22944 var hasEndpts = bezier || multi || lines;
22945 var overrideEndpts = self || taxi;
22946 var srcManEndpt = edge.pstyle('source-endpoint');
22947 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
22948 var tgtManEndpt = edge.pstyle('target-endpoint');
22949 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
22950 rs.srcManEndpt = srcManEndpt;
22951 rs.tgtManEndpt = tgtManEndpt;
22952 var p1; // last known point of edge on target side
22953
22954 var p2; // last known point of edge on source side
22955
22956 var p1_i; // point to intersect with target shape
22957
22958 var p2_i; // point to intersect with source shape
22959
22960 if (bezier) {
22961 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
22962 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
22963 p1 = cpEnd;
22964 p2 = cpStart;
22965 } else if (lines) {
22966 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
22967 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
22968 p1 = tgtArrowFromPt;
22969 p2 = srcArrowFromPt;
22970 }
22971
22972 if (tgtManEndptVal === 'inside-to-node') {
22973 intersect = [tgtPos.x, tgtPos.y];
22974 } else if (tgtManEndpt.units) {
22975 intersect = this.manualEndptToPx(target, tgtManEndpt);
22976 } else if (tgtManEndptVal === 'outside-to-line') {
22977 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
22978 } else {
22979 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
22980 p1_i = p1;
22981 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
22982 p1_i = [srcPos.x, srcPos.y];
22983 }
22984
22985 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
22986
22987 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
22988 var trs = target._private.rscratch;
22989 var lw = trs.labelWidth;
22990 var lh = trs.labelHeight;
22991 var lx = trs.labelX;
22992 var ly = trs.labelY;
22993 var lw2 = lw / 2;
22994 var lh2 = lh / 2;
22995 var va = target.pstyle('text-valign').value;
22996
22997 if (va === 'top') {
22998 ly -= lh2;
22999 } else if (va === 'bottom') {
23000 ly += lh2;
23001 }
23002
23003 var ha = target.pstyle('text-halign').value;
23004
23005 if (ha === 'left') {
23006 lx -= lw2;
23007 } else if (ha === 'right') {
23008 lx += lw2;
23009 }
23010
23011 var labelIntersect = polygonIntersectLine(p1_i[0], p1_i[1], [lx - lw2, ly - lh2, lx + lw2, ly - lh2, lx + lw2, ly + lh2, lx - lw2, ly + lh2], tgtPos.x, tgtPos.y);
23012
23013 if (labelIntersect.length > 0) {
23014 var refPt = srcPos;
23015 var intSqdist = sqdist(refPt, array2point(intersect));
23016 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23017 var minSqDist = intSqdist;
23018
23019 if (labIntSqdist < intSqdist) {
23020 intersect = labelIntersect;
23021 minSqDist = labIntSqdist;
23022 }
23023
23024 if (labelIntersect.length > 2) {
23025 var labInt2SqDist = sqdist(refPt, {
23026 x: labelIntersect[2],
23027 y: labelIntersect[3]
23028 });
23029
23030 if (labInt2SqDist < minSqDist) {
23031 intersect = [labelIntersect[2], labelIntersect[3]];
23032 }
23033 }
23034 }
23035 }
23036 }
23037
23038 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23039 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23040 rs.endX = edgeEnd[0];
23041 rs.endY = edgeEnd[1];
23042 rs.arrowEndX = arrowEnd[0];
23043 rs.arrowEndY = arrowEnd[1];
23044
23045 if (srcManEndptVal === 'inside-to-node') {
23046 intersect = [srcPos.x, srcPos.y];
23047 } else if (srcManEndpt.units) {
23048 intersect = this.manualEndptToPx(source, srcManEndpt);
23049 } else if (srcManEndptVal === 'outside-to-line') {
23050 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23051 } else {
23052 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23053 p2_i = p2;
23054 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23055 p2_i = [tgtPos.x, tgtPos.y];
23056 }
23057
23058 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23059
23060 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23061 var srs = source._private.rscratch;
23062 var _lw = srs.labelWidth;
23063 var _lh = srs.labelHeight;
23064 var _lx = srs.labelX;
23065 var _ly = srs.labelY;
23066
23067 var _lw2 = _lw / 2;
23068
23069 var _lh2 = _lh / 2;
23070
23071 var _va = source.pstyle('text-valign').value;
23072
23073 if (_va === 'top') {
23074 _ly -= _lh2;
23075 } else if (_va === 'bottom') {
23076 _ly += _lh2;
23077 }
23078
23079 var _ha = source.pstyle('text-halign').value;
23080
23081 if (_ha === 'left') {
23082 _lx -= _lw2;
23083 } else if (_ha === 'right') {
23084 _lx += _lw2;
23085 }
23086
23087 var _labelIntersect = polygonIntersectLine(p2_i[0], p2_i[1], [_lx - _lw2, _ly - _lh2, _lx + _lw2, _ly - _lh2, _lx + _lw2, _ly + _lh2, _lx - _lw2, _ly + _lh2], srcPos.x, srcPos.y);
23088
23089 if (_labelIntersect.length > 0) {
23090 var _refPt = tgtPos;
23091
23092 var _intSqdist = sqdist(_refPt, array2point(intersect));
23093
23094 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23095
23096 var _minSqDist = _intSqdist;
23097
23098 if (_labIntSqdist < _intSqdist) {
23099 intersect = [_labelIntersect[0], _labelIntersect[1]];
23100 _minSqDist = _labIntSqdist;
23101 }
23102
23103 if (_labelIntersect.length > 2) {
23104 var _labInt2SqDist = sqdist(_refPt, {
23105 x: _labelIntersect[2],
23106 y: _labelIntersect[3]
23107 });
23108
23109 if (_labInt2SqDist < _minSqDist) {
23110 intersect = [_labelIntersect[2], _labelIntersect[3]];
23111 }
23112 }
23113 }
23114 }
23115 }
23116
23117 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23118 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23119 rs.startX = edgeStart[0];
23120 rs.startY = edgeStart[1];
23121 rs.arrowStartX = arrowStart[0];
23122 rs.arrowStartY = arrowStart[1];
23123
23124 if (hasEndpts) {
23125 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23126 rs.badLine = true;
23127 } else {
23128 rs.badLine = false;
23129 }
23130 }
23131};
23132
23133BRp$4.getSourceEndpoint = function (edge) {
23134 var rs = edge[0]._private.rscratch;
23135 this.recalculateRenderedStyle(edge);
23136
23137 switch (rs.edgeType) {
23138 case 'haystack':
23139 return {
23140 x: rs.haystackPts[0],
23141 y: rs.haystackPts[1]
23142 };
23143
23144 default:
23145 return {
23146 x: rs.arrowStartX,
23147 y: rs.arrowStartY
23148 };
23149 }
23150};
23151
23152BRp$4.getTargetEndpoint = function (edge) {
23153 var rs = edge[0]._private.rscratch;
23154 this.recalculateRenderedStyle(edge);
23155
23156 switch (rs.edgeType) {
23157 case 'haystack':
23158 return {
23159 x: rs.haystackPts[2],
23160 y: rs.haystackPts[3]
23161 };
23162
23163 default:
23164 return {
23165 x: rs.arrowEndX,
23166 y: rs.arrowEndY
23167 };
23168 }
23169};
23170
23171var BRp$5 = {};
23172
23173function pushBezierPts(r, edge, pts) {
23174 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23175 return qbezierAt(p1, p2, p3, t);
23176 };
23177
23178 var _p = edge._private;
23179 var bpts = _p.rstyle.bezierPts;
23180
23181 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23182 var p = r.bezierProjPcts[i];
23183 bpts.push({
23184 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23185 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23186 });
23187 }
23188}
23189
23190BRp$5.storeEdgeProjections = function (edge) {
23191 var _p = edge._private;
23192 var rs = _p.rscratch;
23193 var et = rs.edgeType; // clear the cached points state
23194
23195 _p.rstyle.bezierPts = null;
23196 _p.rstyle.linePts = null;
23197 _p.rstyle.haystackPts = null;
23198
23199 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23200 _p.rstyle.bezierPts = [];
23201
23202 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23203 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23204 }
23205 } else if (et === 'segments') {
23206 var lpts = _p.rstyle.linePts = [];
23207
23208 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23209 lpts.push({
23210 x: rs.allpts[i],
23211 y: rs.allpts[i + 1]
23212 });
23213 }
23214 } else if (et === 'haystack') {
23215 var hpts = rs.haystackPts;
23216 _p.rstyle.haystackPts = [{
23217 x: hpts[0],
23218 y: hpts[1]
23219 }, {
23220 x: hpts[2],
23221 y: hpts[3]
23222 }];
23223 }
23224
23225 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23226};
23227
23228BRp$5.recalculateEdgeProjections = function (edges) {
23229 this.findEdgeControlPoints(edges);
23230};
23231
23232/* global document */
23233
23234var BRp$6 = {};
23235
23236BRp$6.recalculateNodeLabelProjection = function (node) {
23237 var content = node.pstyle('label').strValue;
23238
23239 if (emptyString(content)) {
23240 return;
23241 }
23242
23243 var textX, textY;
23244 var _p = node._private;
23245 var nodeWidth = node.width();
23246 var nodeHeight = node.height();
23247 var padding = node.padding();
23248 var nodePos = node.position();
23249 var textHalign = node.pstyle('text-halign').strValue;
23250 var textValign = node.pstyle('text-valign').strValue;
23251 var rs = _p.rscratch;
23252 var rstyle = _p.rstyle;
23253
23254 switch (textHalign) {
23255 case 'left':
23256 textX = nodePos.x - nodeWidth / 2 - padding;
23257 break;
23258
23259 case 'right':
23260 textX = nodePos.x + nodeWidth / 2 + padding;
23261 break;
23262
23263 default:
23264 // e.g. center
23265 textX = nodePos.x;
23266 }
23267
23268 switch (textValign) {
23269 case 'top':
23270 textY = nodePos.y - nodeHeight / 2 - padding;
23271 break;
23272
23273 case 'bottom':
23274 textY = nodePos.y + nodeHeight / 2 + padding;
23275 break;
23276
23277 default:
23278 // e.g. middle
23279 textY = nodePos.y;
23280 }
23281
23282 rs.labelX = textX;
23283 rs.labelY = textY;
23284 rstyle.labelX = textX;
23285 rstyle.labelY = textY;
23286 this.calculateLabelAngles(node);
23287 this.applyLabelDimensions(node);
23288};
23289
23290var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23291 var angle = Math.atan(dy / dx);
23292
23293 if (dx === 0 && angle < 0) {
23294 angle = angle * -1;
23295 }
23296
23297 return angle;
23298};
23299
23300var lineAngle = function lineAngle(p0, p1) {
23301 var dx = p1.x - p0.x;
23302 var dy = p1.y - p0.y;
23303 return lineAngleFromDelta(dx, dy);
23304};
23305
23306var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23307 var t0 = bound(0, t - 0.001, 1);
23308 var t1 = bound(0, t + 0.001, 1);
23309 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23310 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23311 return lineAngle(lp0, lp1);
23312};
23313
23314BRp$6.recalculateEdgeLabelProjections = function (edge) {
23315 var p;
23316 var _p = edge._private;
23317 var rs = _p.rscratch;
23318 var r = this;
23319 var content = {
23320 mid: edge.pstyle('label').strValue,
23321 source: edge.pstyle('source-label').strValue,
23322 target: edge.pstyle('target-label').strValue
23323 };
23324
23325 if (content.mid || content.source || content.target) ; else {
23326 return; // no labels => no calcs
23327 } // add center point to style so bounding box calculations can use it
23328 //
23329
23330
23331 p = {
23332 x: rs.midX,
23333 y: rs.midY
23334 };
23335
23336 var setRs = function setRs(propName, prefix, value) {
23337 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23338 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23339 };
23340
23341 setRs('labelX', null, p.x);
23342 setRs('labelY', null, p.y);
23343 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23344 setRs('labelAutoAngle', null, midAngle);
23345
23346 var createControlPointInfo = function createControlPointInfo() {
23347 if (createControlPointInfo.cache) {
23348 return createControlPointInfo.cache;
23349 } // use cache so only 1x per edge
23350
23351
23352 var ctrlpts = []; // store each ctrlpt info init
23353
23354 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23355 var p0 = {
23356 x: rs.allpts[i],
23357 y: rs.allpts[i + 1]
23358 };
23359 var p1 = {
23360 x: rs.allpts[i + 2],
23361 y: rs.allpts[i + 3]
23362 }; // ctrlpt
23363
23364 var p2 = {
23365 x: rs.allpts[i + 4],
23366 y: rs.allpts[i + 5]
23367 };
23368 ctrlpts.push({
23369 p0: p0,
23370 p1: p1,
23371 p2: p2,
23372 startDist: 0,
23373 length: 0,
23374 segments: []
23375 });
23376 }
23377
23378 var bpts = _p.rstyle.bezierPts;
23379 var nProjs = r.bezierProjPcts.length;
23380
23381 function addSegment(cp, p0, p1, t0, t1) {
23382 var length = dist(p0, p1);
23383 var prevSegment = cp.segments[cp.segments.length - 1];
23384 var segment = {
23385 p0: p0,
23386 p1: p1,
23387 t0: t0,
23388 t1: t1,
23389 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23390 length: length
23391 };
23392 cp.segments.push(segment);
23393 cp.length += length;
23394 } // update each ctrlpt with segment info
23395
23396
23397 for (var _i = 0; _i < ctrlpts.length; _i++) {
23398 var cp = ctrlpts[_i];
23399 var prevCp = ctrlpts[_i - 1];
23400
23401 if (prevCp) {
23402 cp.startDist = prevCp.startDist + prevCp.length;
23403 }
23404
23405 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23406
23407 for (var j = 0; j < nProjs - 1; j++) {
23408 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23409 }
23410
23411 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23412 }
23413
23414 return createControlPointInfo.cache = ctrlpts;
23415 };
23416
23417 var calculateEndProjection = function calculateEndProjection(prefix) {
23418 var angle;
23419 var isSrc = prefix === 'source';
23420
23421 if (!content[prefix]) {
23422 return;
23423 }
23424
23425 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23426
23427 switch (rs.edgeType) {
23428 case 'self':
23429 case 'compound':
23430 case 'bezier':
23431 case 'multibezier':
23432 {
23433 var cps = createControlPointInfo();
23434 var selected;
23435 var startDist = 0;
23436 var totalDist = 0; // find the segment we're on
23437
23438 for (var i = 0; i < cps.length; i++) {
23439 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23440
23441 for (var j = 0; j < _cp.segments.length; j++) {
23442 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23443 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23444 startDist = totalDist;
23445 totalDist += _seg.length;
23446
23447 if (totalDist >= offset || lastSeg) {
23448 selected = {
23449 cp: _cp,
23450 segment: _seg
23451 };
23452 break;
23453 }
23454 }
23455
23456 if (selected) {
23457 break;
23458 }
23459 }
23460
23461 var cp = selected.cp;
23462 var seg = selected.segment;
23463 var tSegment = (offset - startDist) / seg.length;
23464 var segDt = seg.t1 - seg.t0;
23465 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23466 t = bound(0, t, 1);
23467 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23468 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23469 break;
23470 }
23471
23472 case 'straight':
23473 case 'segments':
23474 case 'haystack':
23475 {
23476 var d = 0,
23477 di,
23478 d0;
23479 var p0, p1;
23480 var l = rs.allpts.length;
23481
23482 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23483 if (isSrc) {
23484 p0 = {
23485 x: rs.allpts[_i2],
23486 y: rs.allpts[_i2 + 1]
23487 };
23488 p1 = {
23489 x: rs.allpts[_i2 + 2],
23490 y: rs.allpts[_i2 + 3]
23491 };
23492 } else {
23493 p0 = {
23494 x: rs.allpts[l - 2 - _i2],
23495 y: rs.allpts[l - 1 - _i2]
23496 };
23497 p1 = {
23498 x: rs.allpts[l - 4 - _i2],
23499 y: rs.allpts[l - 3 - _i2]
23500 };
23501 }
23502
23503 di = dist(p0, p1);
23504 d0 = d;
23505 d += di;
23506
23507 if (d >= offset) {
23508 break;
23509 }
23510 }
23511
23512 var pD = offset - d0;
23513
23514 var _t = pD / di;
23515
23516 _t = bound(0, _t, 1);
23517 p = lineAt(p0, p1, _t);
23518 angle = lineAngle(p0, p1);
23519 break;
23520 }
23521 }
23522
23523 setRs('labelX', prefix, p.x);
23524 setRs('labelY', prefix, p.y);
23525 setRs('labelAutoAngle', prefix, angle);
23526 };
23527
23528 calculateEndProjection('source');
23529 calculateEndProjection('target');
23530 this.applyLabelDimensions(edge);
23531};
23532
23533BRp$6.applyLabelDimensions = function (ele) {
23534 this.applyPrefixedLabelDimensions(ele);
23535
23536 if (ele.isEdge()) {
23537 this.applyPrefixedLabelDimensions(ele, 'source');
23538 this.applyPrefixedLabelDimensions(ele, 'target');
23539 }
23540};
23541
23542BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23543 var _p = ele._private;
23544 var text = this.getLabelText(ele, prefix);
23545 var labelDims = this.calculateLabelDimensions(ele, text);
23546 var lineHeight = ele.pstyle('line-height').pfValue;
23547 var textWrap = ele.pstyle('text-wrap').strValue;
23548 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23549 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23550 var normPerLineHeight = labelDims.height / numLines;
23551 var labelLineHeight = normPerLineHeight * lineHeight;
23552 var width = labelDims.width;
23553 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23554 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23555 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23556 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23557 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23558 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23559};
23560
23561BRp$6.getLabelText = function (ele, prefix) {
23562 var _p = ele._private;
23563 var pfd = prefix ? prefix + '-' : '';
23564 var text = ele.pstyle(pfd + 'label').strValue;
23565 var textTransform = ele.pstyle('text-transform').value;
23566
23567 var rscratch = function rscratch(propName, value) {
23568 if (value) {
23569 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23570 return value;
23571 } else {
23572 return getPrefixedProperty(_p.rscratch, propName, prefix);
23573 }
23574 }; // for empty text, skip all processing
23575
23576
23577 if (!text) {
23578 return '';
23579 }
23580
23581 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23582 text = text.toUpperCase();
23583 } else if (textTransform == 'lowercase') {
23584 text = text.toLowerCase();
23585 }
23586
23587 var wrapStyle = ele.pstyle('text-wrap').value;
23588
23589 if (wrapStyle === 'wrap') {
23590 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23591
23592 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23593 return rscratch('labelWrapCachedText');
23594 }
23595
23596 var zwsp = "\u200B";
23597 var lines = text.split('\n');
23598 var maxW = ele.pstyle('text-max-width').pfValue;
23599 var overflow = ele.pstyle('text-overflow-wrap').value;
23600 var overflowAny = overflow === 'anywhere';
23601 var wrappedLines = [];
23602 var wordsRegex = /[\s\u200b]+/;
23603 var wordSeparator = overflowAny ? '' : ' ';
23604
23605 for (var l = 0; l < lines.length; l++) {
23606 var line = lines[l];
23607 var lineDims = this.calculateLabelDimensions(ele, line);
23608 var lineW = lineDims.width;
23609
23610 if (overflowAny) {
23611 var processedLine = line.split('').join(zwsp);
23612 line = processedLine;
23613 }
23614
23615 if (lineW > maxW) {
23616 // line is too long
23617 var words = line.split(wordsRegex);
23618 var subline = '';
23619
23620 for (var w = 0; w < words.length; w++) {
23621 var word = words[w];
23622 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23623 var testDims = this.calculateLabelDimensions(ele, testLine);
23624 var testW = testDims.width;
23625
23626 if (testW <= maxW) {
23627 // word fits on current line
23628 subline += word + wordSeparator;
23629 } else {
23630 // word starts new line
23631 if (subline) {
23632 wrappedLines.push(subline);
23633 }
23634
23635 subline = word + wordSeparator;
23636 }
23637 } // if there's remaining text, put it in a wrapped line
23638
23639
23640 if (!subline.match(/^[\s\u200b]+$/)) {
23641 wrappedLines.push(subline);
23642 }
23643 } else {
23644 // line is already short enough
23645 wrappedLines.push(line);
23646 }
23647 } // for
23648
23649
23650 rscratch('labelWrapCachedLines', wrappedLines);
23651 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23652 rscratch('labelWrapKey', labelKey);
23653 } else if (wrapStyle === 'ellipsis') {
23654 var _maxW = ele.pstyle('text-max-width').pfValue;
23655 var ellipsized = '';
23656 var ellipsis = "\u2026";
23657 var incLastCh = false;
23658
23659 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23660 // the label already fits
23661 return text;
23662 }
23663
23664 for (var i = 0; i < text.length; i++) {
23665 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23666
23667 if (widthWithNextCh > _maxW) {
23668 break;
23669 }
23670
23671 ellipsized += text[i];
23672
23673 if (i === text.length - 1) {
23674 incLastCh = true;
23675 }
23676 }
23677
23678 if (!incLastCh) {
23679 ellipsized += ellipsis;
23680 }
23681
23682 return ellipsized;
23683 } // if ellipsize
23684
23685
23686 return text;
23687};
23688
23689BRp$6.getLabelJustification = function (ele) {
23690 var justification = ele.pstyle('text-justification').strValue;
23691 var textHalign = ele.pstyle('text-halign').strValue;
23692
23693 if (justification === 'auto') {
23694 if (ele.isNode()) {
23695 switch (textHalign) {
23696 case 'left':
23697 return 'right';
23698
23699 case 'right':
23700 return 'left';
23701
23702 default:
23703 return 'center';
23704 }
23705 } else {
23706 return 'center';
23707 }
23708 } else {
23709 return justification;
23710 }
23711};
23712
23713BRp$6.calculateLabelDimensions = function (ele, text) {
23714 var r = this;
23715 var cacheKey = hashString(text, ele._private.labelDimsKey);
23716 var cache = r.labelDimCache || (r.labelDimCache = []);
23717 var existingVal = cache[cacheKey];
23718
23719 if (existingVal != null) {
23720 return existingVal;
23721 }
23722
23723 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23724
23725 var fStyle = ele.pstyle('font-style').strValue;
23726 var size = ele.pstyle('font-size').pfValue;
23727 var family = ele.pstyle('font-family').strValue;
23728 var weight = ele.pstyle('font-weight').strValue;
23729 var canvas = this.labelCalcCanvas;
23730 var c2d = this.labelCalcCanvasContext;
23731
23732 if (!canvas) {
23733 canvas = this.labelCalcCanvas = document.createElement('canvas');
23734 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23735 var ds = canvas.style;
23736 ds.position = 'absolute';
23737 ds.left = '-9999px';
23738 ds.top = '-9999px';
23739 ds.zIndex = '-1';
23740 ds.visibility = 'hidden';
23741 ds.pointerEvents = 'none';
23742 }
23743
23744 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23745 var width = 0;
23746 var height = 0;
23747 var lines = text.split('\n');
23748
23749 for (var i = 0; i < lines.length; i++) {
23750 var line = lines[i];
23751 var metrics = c2d.measureText(line);
23752 var w = Math.ceil(metrics.width);
23753 var h = size;
23754 width = Math.max(w, width);
23755 height += h;
23756 }
23757
23758 width += padding;
23759 height += padding;
23760 return cache[cacheKey] = {
23761 width: width,
23762 height: height
23763 };
23764};
23765
23766BRp$6.calculateLabelAngle = function (ele, prefix) {
23767 var _p = ele._private;
23768 var rs = _p.rscratch;
23769 var isEdge = ele.isEdge();
23770 var prefixDash = prefix ? prefix + '-' : '';
23771 var rot = ele.pstyle(prefixDash + 'text-rotation');
23772 var rotStr = rot.strValue;
23773
23774 if (rotStr === 'none') {
23775 return 0;
23776 } else if (isEdge && rotStr === 'autorotate') {
23777 return rs.labelAutoAngle;
23778 } else if (rotStr === 'autorotate') {
23779 return 0;
23780 } else {
23781 return rot.pfValue;
23782 }
23783};
23784
23785BRp$6.calculateLabelAngles = function (ele) {
23786 var r = this;
23787 var isEdge = ele.isEdge();
23788 var _p = ele._private;
23789 var rs = _p.rscratch;
23790 rs.labelAngle = r.calculateLabelAngle(ele);
23791
23792 if (isEdge) {
23793 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23794 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23795 }
23796};
23797
23798var BRp$7 = {};
23799var TOO_SMALL_CUT_RECT = 28;
23800var warnedCutRect = false;
23801
23802BRp$7.getNodeShape = function (node) {
23803 var r = this;
23804 var shape = node.pstyle('shape').value;
23805
23806 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23807 if (!warnedCutRect) {
23808 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23809 warnedCutRect = true;
23810 }
23811
23812 return 'rectangle';
23813 }
23814
23815 if (node.isParent()) {
23816 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23817 return shape;
23818 } else {
23819 return 'rectangle';
23820 }
23821 }
23822
23823 if (shape === 'polygon') {
23824 var points = node.pstyle('shape-polygon-points').value;
23825 return r.nodeShapes.makePolygon(points).name;
23826 }
23827
23828 return shape;
23829};
23830
23831var BRp$8 = {};
23832
23833BRp$8.registerCalculationListeners = function () {
23834 var cy = this.cy;
23835 var elesToUpdate = cy.collection();
23836 var r = this;
23837
23838 var enqueue = function enqueue(eles) {
23839 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23840 elesToUpdate.merge(eles);
23841
23842 if (dirtyStyleCaches) {
23843 for (var i = 0; i < eles.length; i++) {
23844 var ele = eles[i];
23845 var _p = ele._private;
23846 var rstyle = _p.rstyle;
23847 rstyle.clean = false;
23848 rstyle.cleanConnected = false;
23849 }
23850 }
23851 };
23852
23853 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23854 var ele = e.target;
23855 enqueue(ele);
23856 }).on('style.* background.*', function onDirtyStyle(e) {
23857 var ele = e.target;
23858 enqueue(ele, false);
23859 });
23860
23861 var updateEleCalcs = function updateEleCalcs(willDraw) {
23862 if (willDraw) {
23863 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
23864 // before calculating rendered style (and pstyle might not be called yet)
23865
23866 elesToUpdate.cleanStyle();
23867
23868 for (var i = 0; i < elesToUpdate.length; i++) {
23869 var ele = elesToUpdate[i];
23870 var rstyle = ele._private.rstyle;
23871
23872 if (ele.isNode() && !rstyle.cleanConnected) {
23873 enqueue(ele.connectedEdges());
23874 rstyle.cleanConnected = true;
23875 }
23876 }
23877
23878 if (fns) {
23879 for (var _i = 0; _i < fns.length; _i++) {
23880 var fn = fns[_i];
23881 fn(willDraw, elesToUpdate);
23882 }
23883 }
23884
23885 r.recalculateRenderedStyle(elesToUpdate);
23886 elesToUpdate = cy.collection();
23887 }
23888 };
23889
23890 r.flushRenderedStyleQueue = function () {
23891 updateEleCalcs(true);
23892 };
23893
23894 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
23895};
23896
23897BRp$8.onUpdateEleCalcs = function (fn) {
23898 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
23899 fns.push(fn);
23900};
23901
23902BRp$8.recalculateRenderedStyle = function (eles, useCache) {
23903 var isCleanConnected = function isCleanConnected(ele) {
23904 return ele._private.rstyle.cleanConnected;
23905 };
23906
23907 var edges = [];
23908 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
23909
23910 if (this.destroyed) {
23911 return;
23912 } // use cache by default for perf
23913
23914
23915 if (useCache === undefined) {
23916 useCache = true;
23917 }
23918
23919 for (var i = 0; i < eles.length; i++) {
23920 var ele = eles[i];
23921 var _p = ele._private;
23922 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
23923 // (and a request for recalc may come in between frames)
23924
23925 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
23926 rstyle.clean = false;
23927 } // only update if dirty and in graph
23928
23929
23930 if (useCache && rstyle.clean || ele.removed()) {
23931 continue;
23932 } // only update if not display: none
23933
23934
23935 if (ele.pstyle('display').value === 'none') {
23936 continue;
23937 }
23938
23939 if (_p.group === 'nodes') {
23940 nodes.push(ele);
23941 } else {
23942 // edges
23943 edges.push(ele);
23944 }
23945
23946 rstyle.clean = true;
23947 } // update node data from projections
23948
23949
23950 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
23951 var _ele = nodes[_i2];
23952 var _p2 = _ele._private;
23953 var _rstyle = _p2.rstyle;
23954
23955 var pos = _ele.position();
23956
23957 this.recalculateNodeLabelProjection(_ele);
23958 _rstyle.nodeX = pos.x;
23959 _rstyle.nodeY = pos.y;
23960 _rstyle.nodeW = _ele.pstyle('width').pfValue;
23961 _rstyle.nodeH = _ele.pstyle('height').pfValue;
23962 }
23963
23964 this.recalculateEdgeProjections(edges); // update edge data from projections
23965
23966 for (var _i3 = 0; _i3 < edges.length; _i3++) {
23967 var _ele2 = edges[_i3];
23968 var _p3 = _ele2._private;
23969 var _rstyle2 = _p3.rstyle;
23970 var rs = _p3.rscratch; // update rstyle positions
23971
23972 _rstyle2.srcX = rs.arrowStartX;
23973 _rstyle2.srcY = rs.arrowStartY;
23974 _rstyle2.tgtX = rs.arrowEndX;
23975 _rstyle2.tgtY = rs.arrowEndY;
23976 _rstyle2.midX = rs.midX;
23977 _rstyle2.midY = rs.midY;
23978 _rstyle2.labelAngle = rs.labelAngle;
23979 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
23980 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
23981 }
23982};
23983
23984var BRp$9 = {};
23985
23986BRp$9.updateCachedGrabbedEles = function () {
23987 var eles = this.cachedZSortedEles;
23988
23989 if (!eles) {
23990 // just let this be recalculated on the next z sort tick
23991 return;
23992 }
23993
23994 eles.drag = [];
23995 eles.nondrag = [];
23996 var grabTargets = [];
23997
23998 for (var i = 0; i < eles.length; i++) {
23999 var ele = eles[i];
24000 var rs = ele._private.rscratch;
24001
24002 if (ele.grabbed() && !ele.isParent()) {
24003 grabTargets.push(ele);
24004 } else if (rs.inDragLayer) {
24005 eles.drag.push(ele);
24006 } else {
24007 eles.nondrag.push(ele);
24008 }
24009 } // put the grab target nodes last so it's on top of its neighbourhood
24010
24011
24012 for (var i = 0; i < grabTargets.length; i++) {
24013 var ele = grabTargets[i];
24014 eles.drag.push(ele);
24015 }
24016};
24017
24018BRp$9.invalidateCachedZSortedEles = function () {
24019 this.cachedZSortedEles = null;
24020};
24021
24022BRp$9.getCachedZSortedEles = function (forceRecalc) {
24023 if (forceRecalc || !this.cachedZSortedEles) {
24024 var eles = this.cy.mutableElements().toArray();
24025 eles.sort(zIndexSort);
24026 eles.interactive = eles.filter(function (ele) {
24027 return ele.interactive();
24028 });
24029 this.cachedZSortedEles = eles;
24030 this.updateCachedGrabbedEles();
24031 } else {
24032 eles = this.cachedZSortedEles;
24033 }
24034
24035 return eles;
24036};
24037
24038var BRp$a = {};
24039[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24040 extend(BRp$a, props);
24041});
24042
24043var BRp$b = {};
24044
24045BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24046 var r = this;
24047 var imageCache = r.imageCache = r.imageCache || {};
24048 var cache = imageCache[url];
24049
24050 if (cache) {
24051 if (!cache.image.complete) {
24052 cache.image.addEventListener('load', onLoad);
24053 }
24054
24055 return cache.image;
24056 } else {
24057 cache = imageCache[url] = imageCache[url] || {};
24058 var image = cache.image = new Image(); // eslint-disable-line no-undef
24059
24060 image.addEventListener('load', onLoad);
24061 image.addEventListener('error', function () {
24062 image.error = true;
24063 }); // #1582 safari doesn't load data uris with crossOrigin properly
24064 // https://bugs.webkit.org/show_bug.cgi?id=123978
24065
24066 var dataUriPrefix = 'data:';
24067 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24068
24069 if (!isDataUri) {
24070 image.crossOrigin = crossOrigin; // prevent tainted canvas
24071 }
24072
24073 image.src = url;
24074 return image;
24075 }
24076};
24077
24078var BRp$c = {};
24079/* global document, window, ResizeObserver, MutationObserver */
24080
24081BRp$c.registerBinding = function (target, event, handler, useCapture) {
24082 // eslint-disable-line no-unused-vars
24083 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24084
24085 var b = this.binder(target);
24086 return b.on.apply(b, args);
24087};
24088
24089BRp$c.binder = function (tgt) {
24090 var r = this;
24091 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24092
24093 if (r.supportsPassiveEvents == null) {
24094 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24095 var supportsPassive = false;
24096
24097 try {
24098 var opts = Object.defineProperty({}, 'passive', {
24099 get: function get() {
24100 supportsPassive = true;
24101 return true;
24102 }
24103 });
24104 window.addEventListener('test', null, opts);
24105 } catch (err) {// not supported
24106 }
24107
24108 r.supportsPassiveEvents = supportsPassive;
24109 }
24110
24111 var on = function on(event, handler, useCapture) {
24112 var args = Array.prototype.slice.call(arguments);
24113
24114 if (tgtIsDom && r.supportsPassiveEvents) {
24115 // replace useCapture w/ opts obj
24116 args[2] = {
24117 capture: useCapture != null ? useCapture : false,
24118 passive: false,
24119 once: false
24120 };
24121 }
24122
24123 r.bindings.push({
24124 target: tgt,
24125 args: args
24126 });
24127 (tgt.addEventListener || tgt.on).apply(tgt, args);
24128 return this;
24129 };
24130
24131 return {
24132 on: on,
24133 addEventListener: on,
24134 addListener: on,
24135 bind: on
24136 };
24137};
24138
24139BRp$c.nodeIsDraggable = function (node) {
24140 return node && node.isNode() && !node.locked() && node.grabbable();
24141};
24142
24143BRp$c.nodeIsGrabbable = function (node) {
24144 return this.nodeIsDraggable(node) && node.interactive();
24145};
24146
24147BRp$c.load = function () {
24148 var r = this;
24149
24150 var isSelected = function isSelected(ele) {
24151 return ele.selected();
24152 };
24153
24154 var triggerEvents = function triggerEvents(target, names, e, position) {
24155 if (target == null) {
24156 target = r.cy;
24157 }
24158
24159 for (var i = 0; i < names.length; i++) {
24160 var name = names[i];
24161 target.emit({
24162 originalEvent: e,
24163 type: name,
24164 position: position
24165 });
24166 }
24167 };
24168
24169 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24170 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24171 };
24172
24173 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24174 var allowPassthrough = true;
24175
24176 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24177 // a grabbable compound node below the ele => no passthrough panning
24178 for (var i = 0; downs && i < downs.length; i++) {
24179 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
24180
24181 if (down.isNode() && down.isParent() && !down.pannable()) {
24182 allowPassthrough = false;
24183 break;
24184 }
24185 }
24186 } else {
24187 allowPassthrough = true;
24188 }
24189
24190 return allowPassthrough;
24191 };
24192
24193 var setGrabbed = function setGrabbed(ele) {
24194 ele[0]._private.grabbed = true;
24195 };
24196
24197 var setFreed = function setFreed(ele) {
24198 ele[0]._private.grabbed = false;
24199 };
24200
24201 var setInDragLayer = function setInDragLayer(ele) {
24202 ele[0]._private.rscratch.inDragLayer = true;
24203 };
24204
24205 var setOutDragLayer = function setOutDragLayer(ele) {
24206 ele[0]._private.rscratch.inDragLayer = false;
24207 };
24208
24209 var setGrabTarget = function setGrabTarget(ele) {
24210 ele[0]._private.rscratch.isGrabTarget = true;
24211 };
24212
24213 var removeGrabTarget = function removeGrabTarget(ele) {
24214 ele[0]._private.rscratch.isGrabTarget = false;
24215 };
24216
24217 var addToDragList = function addToDragList(ele, opts) {
24218 var list = opts.addToList;
24219 var listHasEle = list.has(ele);
24220
24221 if (!listHasEle) {
24222 list.merge(ele);
24223 setGrabbed(ele);
24224 }
24225 }; // helper function to determine which child nodes and inner edges
24226 // of a compound node to be dragged as well as the grabbed and selected nodes
24227
24228
24229 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24230 if (!node.cy().hasCompoundNodes()) {
24231 return;
24232 }
24233
24234 if (opts.inDragLayer == null && opts.addToList == null) {
24235 return;
24236 } // nothing to do
24237
24238
24239 var innerNodes = node.descendants();
24240
24241 if (opts.inDragLayer) {
24242 innerNodes.forEach(setInDragLayer);
24243 innerNodes.connectedEdges().forEach(setInDragLayer);
24244 }
24245
24246 if (opts.addToList) {
24247 opts.addToList.unmerge(innerNodes);
24248 }
24249 }; // adds the given nodes and its neighbourhood to the drag layer
24250
24251
24252 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24253 opts = opts || {};
24254 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24255
24256 if (opts.inDragLayer) {
24257 nodes.forEach(setInDragLayer);
24258 nodes.neighborhood().stdFilter(function (ele) {
24259 return !hasCompoundNodes || ele.isEdge();
24260 }).forEach(setInDragLayer);
24261 }
24262
24263 if (opts.addToList) {
24264 nodes.forEach(function (ele) {
24265 addToDragList(ele, opts);
24266 });
24267 }
24268
24269 addDescendantsToDrag(nodes, opts); // always add to drag
24270 // also add nodes and edges related to the topmost ancestor
24271
24272 updateAncestorsInDragLayer(nodes, {
24273 inDragLayer: opts.inDragLayer
24274 });
24275 r.updateCachedGrabbedEles();
24276 };
24277
24278 var addNodeToDrag = addNodesToDrag;
24279
24280 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24281 if (!grabbedEles) {
24282 return;
24283 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24284
24285
24286 r.getCachedZSortedEles().forEach(function (ele) {
24287 setFreed(ele);
24288 setOutDragLayer(ele);
24289 removeGrabTarget(ele);
24290 });
24291 r.updateCachedGrabbedEles();
24292 }; // helper function to determine which ancestor nodes and edges should go
24293 // to the drag layer (or should be removed from drag layer).
24294
24295
24296 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24297 if (opts.inDragLayer == null && opts.addToList == null) {
24298 return;
24299 } // nothing to do
24300
24301
24302 if (!node.cy().hasCompoundNodes()) {
24303 return;
24304 } // find top-level parent
24305
24306
24307 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24308
24309 if (parent.same(node)) {
24310 return;
24311 }
24312
24313 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24314 var edges = nodes.connectedEdges();
24315
24316 if (opts.inDragLayer) {
24317 edges.forEach(setInDragLayer);
24318 nodes.forEach(setInDragLayer);
24319 }
24320
24321 if (opts.addToList) {
24322 nodes.forEach(function (ele) {
24323 addToDragList(ele, opts);
24324 });
24325 }
24326 };
24327
24328 var blurActiveDomElement = function blurActiveDomElement() {
24329 if (document.activeElement != null && document.activeElement.blur != null) {
24330 document.activeElement.blur();
24331 }
24332 };
24333
24334 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24335 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24336
24337 if (haveMutationsApi) {
24338 r.removeObserver = new MutationObserver(function (mutns) {
24339 // eslint-disable-line no-undef
24340 for (var i = 0; i < mutns.length; i++) {
24341 var mutn = mutns[i];
24342 var rNodes = mutn.removedNodes;
24343
24344 if (rNodes) {
24345 for (var j = 0; j < rNodes.length; j++) {
24346 var rNode = rNodes[j];
24347
24348 if (rNode === r.container) {
24349 r.destroy();
24350 break;
24351 }
24352 }
24353 }
24354 }
24355 });
24356
24357 if (r.container.parentNode) {
24358 r.removeObserver.observe(r.container.parentNode, {
24359 childList: true
24360 });
24361 }
24362 } else {
24363 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24364 // eslint-disable-line no-unused-vars
24365 r.destroy();
24366 });
24367 }
24368
24369 var onResize = util(function () {
24370 r.cy.resize();
24371 }, 100);
24372
24373 if (haveMutationsApi) {
24374 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24375
24376 r.styleObserver.observe(r.container, {
24377 attributes: true
24378 });
24379 } // auto resize
24380
24381
24382 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24383
24384 if (haveResizeObserverApi) {
24385 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24386
24387 r.resizeObserver.observe(r.container);
24388 }
24389
24390 var forEachUp = function forEachUp(domEle, fn) {
24391 while (domEle != null) {
24392 fn(domEle);
24393 domEle = domEle.parentNode;
24394 }
24395 };
24396
24397 var invalidateCoords = function invalidateCoords() {
24398 r.invalidateContainerClientCoordsCache();
24399 };
24400
24401 forEachUp(r.container, function (domEle) {
24402 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24403 r.registerBinding(domEle, 'animationend', invalidateCoords);
24404 r.registerBinding(domEle, 'scroll', invalidateCoords);
24405 }); // stop right click menu from appearing on cy
24406
24407 r.registerBinding(r.container, 'contextmenu', function (e) {
24408 e.preventDefault();
24409 });
24410
24411 var inBoxSelection = function inBoxSelection() {
24412 return r.selection[4] !== 0;
24413 };
24414
24415 var eventInContainer = function eventInContainer(e) {
24416 // save cycles if mouse events aren't to be captured
24417 var containerPageCoords = r.findContainerClientCoords();
24418 var x = containerPageCoords[0];
24419 var y = containerPageCoords[1];
24420 var width = containerPageCoords[2];
24421 var height = containerPageCoords[3];
24422 var positions = e.touches ? e.touches : [e];
24423 var atLeastOnePosInside = false;
24424
24425 for (var i = 0; i < positions.length; i++) {
24426 var p = positions[i];
24427
24428 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24429 atLeastOnePosInside = true;
24430 break;
24431 }
24432 }
24433
24434 if (!atLeastOnePosInside) {
24435 return false;
24436 }
24437
24438 var container = r.container;
24439 var target = e.target;
24440 var tParent = target.parentNode;
24441 var containerIsTarget = false;
24442
24443 while (tParent) {
24444 if (tParent === container) {
24445 containerIsTarget = true;
24446 break;
24447 }
24448
24449 tParent = tParent.parentNode;
24450 }
24451
24452 if (!containerIsTarget) {
24453 return false;
24454 } // if target is outisde cy container, then this event is not for us
24455
24456
24457 return true;
24458 }; // Primary key
24459
24460
24461 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24462 if (!eventInContainer(e)) {
24463 return;
24464 }
24465
24466 e.preventDefault();
24467 blurActiveDomElement();
24468 r.hoverData.capture = true;
24469 r.hoverData.which = e.which;
24470 var cy = r.cy;
24471 var gpos = [e.clientX, e.clientY];
24472 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24473 var select = r.selection;
24474 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24475 var near = nears[0];
24476 var draggedElements = r.dragData.possibleDragElements;
24477 r.hoverData.mdownPos = pos;
24478 r.hoverData.mdownGPos = gpos;
24479
24480 var checkForTaphold = function checkForTaphold() {
24481 r.hoverData.tapholdCancelled = false;
24482 clearTimeout(r.hoverData.tapholdTimeout);
24483 r.hoverData.tapholdTimeout = setTimeout(function () {
24484 if (r.hoverData.tapholdCancelled) {
24485 return;
24486 } else {
24487 var ele = r.hoverData.down;
24488
24489 if (ele) {
24490 ele.emit({
24491 originalEvent: e,
24492 type: 'taphold',
24493 position: {
24494 x: pos[0],
24495 y: pos[1]
24496 }
24497 });
24498 } else {
24499 cy.emit({
24500 originalEvent: e,
24501 type: 'taphold',
24502 position: {
24503 x: pos[0],
24504 y: pos[1]
24505 }
24506 });
24507 }
24508 }
24509 }, r.tapholdDuration);
24510 }; // Right click button
24511
24512
24513 if (e.which == 3) {
24514 r.hoverData.cxtStarted = true;
24515 var cxtEvt = {
24516 originalEvent: e,
24517 type: 'cxttapstart',
24518 position: {
24519 x: pos[0],
24520 y: pos[1]
24521 }
24522 };
24523
24524 if (near) {
24525 near.activate();
24526 near.emit(cxtEvt);
24527 r.hoverData.down = near;
24528 } else {
24529 cy.emit(cxtEvt);
24530 }
24531
24532 r.hoverData.downTime = new Date().getTime();
24533 r.hoverData.cxtDragged = false; // Primary button
24534 } else if (e.which == 1) {
24535 if (near) {
24536 near.activate();
24537 } // Element dragging
24538
24539
24540 {
24541 // If something is under the cursor and it is draggable, prepare to grab it
24542 if (near != null) {
24543 if (r.nodeIsGrabbable(near)) {
24544 var makeEvent = function makeEvent(type) {
24545 return {
24546 originalEvent: e,
24547 type: type,
24548 position: {
24549 x: pos[0],
24550 y: pos[1]
24551 }
24552 };
24553 };
24554
24555 var triggerGrab = function triggerGrab(ele) {
24556 ele.emit(makeEvent('grab'));
24557 };
24558
24559 setGrabTarget(near);
24560
24561 if (!near.selected()) {
24562 draggedElements = r.dragData.possibleDragElements = cy.collection();
24563 addNodeToDrag(near, {
24564 addToList: draggedElements
24565 });
24566 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24567 } else {
24568 draggedElements = r.dragData.possibleDragElements = cy.collection();
24569 var selectedNodes = cy.$(function (ele) {
24570 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24571 });
24572 addNodesToDrag(selectedNodes, {
24573 addToList: draggedElements
24574 });
24575 near.emit(makeEvent('grabon'));
24576 selectedNodes.forEach(triggerGrab);
24577 }
24578
24579 r.redrawHint('eles', true);
24580 r.redrawHint('drag', true);
24581 }
24582 }
24583
24584 r.hoverData.down = near;
24585 r.hoverData.downs = nears;
24586 r.hoverData.downTime = new Date().getTime();
24587 }
24588 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24589 x: pos[0],
24590 y: pos[1]
24591 });
24592
24593 if (near == null) {
24594 select[4] = 1;
24595 r.data.bgActivePosistion = {
24596 x: pos[0],
24597 y: pos[1]
24598 };
24599 r.redrawHint('select', true);
24600 r.redraw();
24601 } else if (near.pannable()) {
24602 select[4] = 1; // for future pan
24603 }
24604
24605 checkForTaphold();
24606 } // Initialize selection box coordinates
24607
24608
24609 select[0] = select[2] = pos[0];
24610 select[1] = select[3] = pos[1];
24611 }, false);
24612 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24613 // eslint-disable-line no-undef
24614 var capture = r.hoverData.capture;
24615
24616 if (!capture && !eventInContainer(e)) {
24617 return;
24618 }
24619
24620 var preventDefault = false;
24621 var cy = r.cy;
24622 var zoom = cy.zoom();
24623 var gpos = [e.clientX, e.clientY];
24624 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24625 var mdownPos = r.hoverData.mdownPos;
24626 var mdownGPos = r.hoverData.mdownGPos;
24627 var select = r.selection;
24628 var near = null;
24629
24630 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24631 near = r.findNearestElement(pos[0], pos[1], true, false);
24632 }
24633
24634 var last = r.hoverData.last;
24635 var down = r.hoverData.down;
24636 var disp = [pos[0] - select[2], pos[1] - select[3]];
24637 var draggedElements = r.dragData.possibleDragElements;
24638 var isOverThresholdDrag;
24639
24640 if (mdownGPos) {
24641 var dx = gpos[0] - mdownGPos[0];
24642 var dx2 = dx * dx;
24643 var dy = gpos[1] - mdownGPos[1];
24644 var dy2 = dy * dy;
24645 var dist2 = dx2 + dy2;
24646 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24647 }
24648
24649 var multSelKeyDown = isMultSelKeyDown(e);
24650
24651 if (isOverThresholdDrag) {
24652 r.hoverData.tapholdCancelled = true;
24653 }
24654
24655 var updateDragDelta = function updateDragDelta() {
24656 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24657
24658 if (dragDelta.length === 0) {
24659 dragDelta.push(disp[0]);
24660 dragDelta.push(disp[1]);
24661 } else {
24662 dragDelta[0] += disp[0];
24663 dragDelta[1] += disp[1];
24664 }
24665 };
24666
24667 preventDefault = true;
24668 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24669 x: pos[0],
24670 y: pos[1]
24671 });
24672
24673 var goIntoBoxMode = function goIntoBoxMode() {
24674 r.data.bgActivePosistion = undefined;
24675
24676 if (!r.hoverData.selecting) {
24677 cy.emit({
24678 originalEvent: e,
24679 type: 'boxstart',
24680 position: {
24681 x: pos[0],
24682 y: pos[1]
24683 }
24684 });
24685 }
24686
24687 select[4] = 1;
24688 r.hoverData.selecting = true;
24689 r.redrawHint('select', true);
24690 r.redraw();
24691 }; // trigger context drag if rmouse down
24692
24693
24694 if (r.hoverData.which === 3) {
24695 // but only if over threshold
24696 if (isOverThresholdDrag) {
24697 var cxtEvt = {
24698 originalEvent: e,
24699 type: 'cxtdrag',
24700 position: {
24701 x: pos[0],
24702 y: pos[1]
24703 }
24704 };
24705
24706 if (down) {
24707 down.emit(cxtEvt);
24708 } else {
24709 cy.emit(cxtEvt);
24710 }
24711
24712 r.hoverData.cxtDragged = true;
24713
24714 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24715 if (r.hoverData.cxtOver) {
24716 r.hoverData.cxtOver.emit({
24717 originalEvent: e,
24718 type: 'cxtdragout',
24719 position: {
24720 x: pos[0],
24721 y: pos[1]
24722 }
24723 });
24724 }
24725
24726 r.hoverData.cxtOver = near;
24727
24728 if (near) {
24729 near.emit({
24730 originalEvent: e,
24731 type: 'cxtdragover',
24732 position: {
24733 x: pos[0],
24734 y: pos[1]
24735 }
24736 });
24737 }
24738 }
24739 } // Check if we are drag panning the entire graph
24740
24741 } else if (r.hoverData.dragging) {
24742 preventDefault = true;
24743
24744 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24745 var deltaP;
24746
24747 if (r.hoverData.justStartedPan) {
24748 var mdPos = r.hoverData.mdownPos;
24749 deltaP = {
24750 x: (pos[0] - mdPos[0]) * zoom,
24751 y: (pos[1] - mdPos[1]) * zoom
24752 };
24753 r.hoverData.justStartedPan = false;
24754 } else {
24755 deltaP = {
24756 x: disp[0] * zoom,
24757 y: disp[1] * zoom
24758 };
24759 }
24760
24761 cy.panBy(deltaP);
24762 r.hoverData.dragged = true;
24763 } // Needs reproject due to pan changing viewport
24764
24765
24766 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24767 } else if (select[4] == 1 && (down == null || down.pannable())) {
24768 if (isOverThresholdDrag) {
24769 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24770 goIntoBoxMode();
24771 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24772 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24773
24774 if (allowPassthrough) {
24775 r.hoverData.dragging = true;
24776 r.hoverData.justStartedPan = true;
24777 select[4] = 0;
24778 r.data.bgActivePosistion = array2point(mdownPos);
24779 r.redrawHint('select', true);
24780 r.redraw();
24781 }
24782 }
24783
24784 if (down && down.pannable() && down.active()) {
24785 down.unactivate();
24786 }
24787 }
24788 } else {
24789 if (down && down.pannable() && down.active()) {
24790 down.unactivate();
24791 }
24792
24793 if ((!down || !down.grabbed()) && near != last) {
24794 if (last) {
24795 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24796 x: pos[0],
24797 y: pos[1]
24798 });
24799 }
24800
24801 if (near) {
24802 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24803 x: pos[0],
24804 y: pos[1]
24805 });
24806 }
24807
24808 r.hoverData.last = near;
24809 }
24810
24811 if (down) {
24812 if (isOverThresholdDrag) {
24813 // then we can take action
24814 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24815 // then selection overrides
24816 if (down && down.grabbed()) {
24817 freeDraggedElements(draggedElements);
24818 down.emit('freeon');
24819 draggedElements.emit('free');
24820
24821 if (r.dragData.didDrag) {
24822 down.emit('dragfreeon');
24823 draggedElements.emit('dragfree');
24824 }
24825 }
24826
24827 goIntoBoxMode();
24828 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24829 // drag node
24830 var justStartedDrag = !r.dragData.didDrag;
24831
24832 if (justStartedDrag) {
24833 r.redrawHint('eles', true);
24834 }
24835
24836 r.dragData.didDrag = true; // indicate that we actually did drag the node
24837
24838 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24839
24840 if (!r.hoverData.draggingEles) {
24841 addNodesToDrag(draggedElements, {
24842 inDragLayer: true
24843 });
24844 }
24845
24846 var totalShift = {
24847 x: 0,
24848 y: 0
24849 };
24850
24851 if (number(disp[0]) && number(disp[1])) {
24852 totalShift.x += disp[0];
24853 totalShift.y += disp[1];
24854
24855 if (justStartedDrag) {
24856 var dragDelta = r.hoverData.dragDelta;
24857
24858 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24859 totalShift.x += dragDelta[0];
24860 totalShift.y += dragDelta[1];
24861 }
24862 }
24863 }
24864
24865 for (var i = 0; i < draggedElements.length; i++) {
24866 var dEle = draggedElements[i];
24867
24868 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24869 toTrigger.push(dEle);
24870 }
24871 }
24872
24873 r.hoverData.draggingEles = true;
24874 toTrigger.silentShift(totalShift).emit('position drag');
24875 r.redrawHint('drag', true);
24876 r.redraw();
24877 }
24878 } else {
24879 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24880 updateDragDelta();
24881 }
24882 } // prevent the dragging from triggering text selection on the page
24883
24884
24885 preventDefault = true;
24886 }
24887
24888 select[2] = pos[0];
24889 select[3] = pos[1];
24890
24891 if (preventDefault) {
24892 if (e.stopPropagation) e.stopPropagation();
24893 if (e.preventDefault) e.preventDefault();
24894 return false;
24895 }
24896 }, false);
24897 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24898 // eslint-disable-line no-undef
24899 var capture = r.hoverData.capture;
24900
24901 if (!capture) {
24902 return;
24903 }
24904
24905 r.hoverData.capture = false;
24906 var cy = r.cy;
24907 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24908 var select = r.selection;
24909 var near = r.findNearestElement(pos[0], pos[1], true, false);
24910 var draggedElements = r.dragData.possibleDragElements;
24911 var down = r.hoverData.down;
24912 var multSelKeyDown = isMultSelKeyDown(e);
24913
24914 if (r.data.bgActivePosistion) {
24915 r.redrawHint('select', true);
24916 r.redraw();
24917 }
24918
24919 r.hoverData.tapholdCancelled = true;
24920 r.data.bgActivePosistion = undefined; // not active bg now
24921
24922 if (down) {
24923 down.unactivate();
24924 }
24925
24926 if (r.hoverData.which === 3) {
24927 var cxtEvt = {
24928 originalEvent: e,
24929 type: 'cxttapend',
24930 position: {
24931 x: pos[0],
24932 y: pos[1]
24933 }
24934 };
24935
24936 if (down) {
24937 down.emit(cxtEvt);
24938 } else {
24939 cy.emit(cxtEvt);
24940 }
24941
24942 if (!r.hoverData.cxtDragged) {
24943 var cxtTap = {
24944 originalEvent: e,
24945 type: 'cxttap',
24946 position: {
24947 x: pos[0],
24948 y: pos[1]
24949 }
24950 };
24951
24952 if (down) {
24953 down.emit(cxtTap);
24954 } else {
24955 cy.emit(cxtTap);
24956 }
24957 }
24958
24959 r.hoverData.cxtDragged = false;
24960 r.hoverData.which = null;
24961 } else if (r.hoverData.which === 1) {
24962 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24963 x: pos[0],
24964 y: pos[1]
24965 });
24966
24967 if (!r.dragData.didDrag // didn't move a node around
24968 && !r.hoverData.dragged // didn't pan
24969 && !r.hoverData.selecting // not box selection
24970 && !r.hoverData.isOverThresholdDrag // didn't move too much
24971 ) {
24972 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24973 x: pos[0],
24974 y: pos[1]
24975 });
24976 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24977
24978
24979 if (down == null && // not mousedown on node
24980 !r.dragData.didDrag // didn't move the node around
24981 && !r.hoverData.selecting // not box selection
24982 && !r.hoverData.dragged // didn't pan
24983 && !isMultSelKeyDown(e)) {
24984 cy.$(isSelected).unselect(['tapunselect']);
24985
24986 if (draggedElements.length > 0) {
24987 r.redrawHint('eles', true);
24988 }
24989
24990 r.dragData.possibleDragElements = draggedElements = cy.collection();
24991 } // Single selection
24992
24993
24994 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24995 if (near != null && near._private.selectable) {
24996 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24997 if (near.selected()) {
24998 near.unselect(['tapunselect']);
24999 } else {
25000 near.select(['tapselect']);
25001 }
25002 } else {
25003 if (!multSelKeyDown) {
25004 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25005 near.select(['tapselect']);
25006 }
25007 }
25008
25009 r.redrawHint('eles', true);
25010 }
25011 }
25012
25013 if (r.hoverData.selecting) {
25014 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25015 r.redrawHint('select', true);
25016
25017 if (box.length > 0) {
25018 r.redrawHint('eles', true);
25019 }
25020
25021 cy.emit({
25022 type: 'boxend',
25023 originalEvent: e,
25024 position: {
25025 x: pos[0],
25026 y: pos[1]
25027 }
25028 });
25029
25030 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25031 return ele.selectable() && !ele.selected();
25032 };
25033
25034 if (cy.selectionType() === 'additive') {
25035 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25036 } else {
25037 if (!multSelKeyDown) {
25038 cy.$(isSelected).unmerge(box).unselect();
25039 }
25040
25041 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25042 } // always need redraw in case eles unselectable
25043
25044
25045 r.redraw();
25046 } // Cancel drag pan
25047
25048
25049 if (r.hoverData.dragging) {
25050 r.hoverData.dragging = false;
25051 r.redrawHint('select', true);
25052 r.redrawHint('eles', true);
25053 r.redraw();
25054 }
25055
25056 if (!select[4]) {
25057 r.redrawHint('drag', true);
25058 r.redrawHint('eles', true);
25059 var downWasGrabbed = down && down.grabbed();
25060 freeDraggedElements(draggedElements);
25061
25062 if (downWasGrabbed) {
25063 down.emit('freeon');
25064 draggedElements.emit('free');
25065
25066 if (r.dragData.didDrag) {
25067 down.emit('dragfreeon');
25068 draggedElements.emit('dragfree');
25069 }
25070 }
25071 }
25072 } // else not right mouse
25073
25074
25075 select[4] = 0;
25076 r.hoverData.down = null;
25077 r.hoverData.cxtStarted = false;
25078 r.hoverData.draggingEles = false;
25079 r.hoverData.selecting = false;
25080 r.hoverData.isOverThresholdDrag = false;
25081 r.dragData.didDrag = false;
25082 r.hoverData.dragged = false;
25083 r.hoverData.dragDelta = [];
25084 r.hoverData.mdownPos = null;
25085 r.hoverData.mdownGPos = null;
25086 }, false);
25087
25088 var wheelHandler = function wheelHandler(e) {
25089 if (r.scrollingPage) {
25090 return;
25091 } // while scrolling, ignore wheel-to-zoom
25092
25093
25094 var cy = r.cy;
25095 var zoom = cy.zoom();
25096 var pan = cy.pan();
25097 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25098 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25099
25100 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25101 // if pan dragging or cxt dragging, wheel movements make no zoom
25102 e.preventDefault();
25103 return;
25104 }
25105
25106 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25107 e.preventDefault();
25108 r.data.wheelZooming = true;
25109 clearTimeout(r.data.wheelTimeout);
25110 r.data.wheelTimeout = setTimeout(function () {
25111 r.data.wheelZooming = false;
25112 r.redrawHint('eles', true);
25113 r.redraw();
25114 }, 150);
25115 var diff;
25116
25117 if (e.deltaY != null) {
25118 diff = e.deltaY / -250;
25119 } else if (e.wheelDeltaY != null) {
25120 diff = e.wheelDeltaY / 1000;
25121 } else {
25122 diff = e.wheelDelta / 1000;
25123 }
25124
25125 diff = diff * r.wheelSensitivity;
25126 var needsWheelFix = e.deltaMode === 1;
25127
25128 if (needsWheelFix) {
25129 // fixes slow wheel events on ff/linux and ff/windows
25130 diff *= 33;
25131 }
25132
25133 var newZoom = cy.zoom() * Math.pow(10, diff);
25134
25135 if (e.type === 'gesturechange') {
25136 newZoom = r.gestureStartZoom * e.scale;
25137 }
25138
25139 cy.zoom({
25140 level: newZoom,
25141 renderedPosition: {
25142 x: rpos[0],
25143 y: rpos[1]
25144 }
25145 });
25146 }
25147 }; // Functions to help with whether mouse wheel should trigger zooming
25148 // --
25149
25150
25151 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25152 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25153 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25154 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25155
25156 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25157 // eslint-disable-line no-unused-vars
25158 r.scrollingPage = true;
25159 clearTimeout(r.scrollingPageTimeout);
25160 r.scrollingPageTimeout = setTimeout(function () {
25161 r.scrollingPage = false;
25162 }, 250);
25163 }, true); // desktop safari pinch to zoom start
25164
25165 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25166 r.gestureStartZoom = r.cy.zoom();
25167
25168 if (!r.hasTouchStarted) {
25169 // don't affect touch devices like iphone
25170 e.preventDefault();
25171 }
25172 }, true);
25173 r.registerBinding(r.container, 'gesturechange', function (e) {
25174 if (!r.hasTouchStarted) {
25175 // don't affect touch devices like iphone
25176 wheelHandler(e);
25177 }
25178 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25179 // Handle mouseout on Cytoscape container
25180
25181 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25182 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25183 r.cy.emit({
25184 originalEvent: e,
25185 type: 'mouseout',
25186 position: {
25187 x: pos[0],
25188 y: pos[1]
25189 }
25190 });
25191 }, false);
25192 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25193 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25194 r.cy.emit({
25195 originalEvent: e,
25196 type: 'mouseover',
25197 position: {
25198 x: pos[0],
25199 y: pos[1]
25200 }
25201 });
25202 }, false);
25203 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25204
25205 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25206
25207 var center1, modelCenter1; // center point on start pinch to zoom
25208
25209 var offsetLeft, offsetTop;
25210 var containerWidth, containerHeight;
25211 var twoFingersStartInside;
25212
25213 var distance = function distance(x1, y1, x2, y2) {
25214 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25215 };
25216
25217 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25218 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25219 };
25220
25221 var touchstartHandler;
25222 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25223 r.hasTouchStarted = true;
25224
25225 if (!eventInContainer(e)) {
25226 return;
25227 }
25228
25229 blurActiveDomElement();
25230 r.touchData.capture = true;
25231 r.data.bgActivePosistion = undefined;
25232 var cy = r.cy;
25233 var now = r.touchData.now;
25234 var earlier = r.touchData.earlier;
25235
25236 if (e.touches[0]) {
25237 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25238 now[0] = pos[0];
25239 now[1] = pos[1];
25240 }
25241
25242 if (e.touches[1]) {
25243 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25244 now[2] = pos[0];
25245 now[3] = pos[1];
25246 }
25247
25248 if (e.touches[2]) {
25249 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25250 now[4] = pos[0];
25251 now[5] = pos[1];
25252 } // record starting points for pinch-to-zoom
25253
25254
25255 if (e.touches[1]) {
25256 r.touchData.singleTouchMoved = true;
25257 freeDraggedElements(r.dragData.touchDragEles);
25258 var offsets = r.findContainerClientCoords();
25259 offsetLeft = offsets[0];
25260 offsetTop = offsets[1];
25261 containerWidth = offsets[2];
25262 containerHeight = offsets[3];
25263 f1x1 = e.touches[0].clientX - offsetLeft;
25264 f1y1 = e.touches[0].clientY - offsetTop;
25265 f2x1 = e.touches[1].clientX - offsetLeft;
25266 f2y1 = e.touches[1].clientY - offsetTop;
25267 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25268 var pan = cy.pan();
25269 var zoom = cy.zoom();
25270 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25271 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25272 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25273 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25274
25275 var cxtDistThreshold = 200;
25276 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25277
25278 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25279 var near1 = r.findNearestElement(now[0], now[1], true, true);
25280 var near2 = r.findNearestElement(now[2], now[3], true, true);
25281
25282 if (near1 && near1.isNode()) {
25283 near1.activate().emit({
25284 originalEvent: e,
25285 type: 'cxttapstart',
25286 position: {
25287 x: now[0],
25288 y: now[1]
25289 }
25290 });
25291 r.touchData.start = near1;
25292 } else if (near2 && near2.isNode()) {
25293 near2.activate().emit({
25294 originalEvent: e,
25295 type: 'cxttapstart',
25296 position: {
25297 x: now[0],
25298 y: now[1]
25299 }
25300 });
25301 r.touchData.start = near2;
25302 } else {
25303 cy.emit({
25304 originalEvent: e,
25305 type: 'cxttapstart',
25306 position: {
25307 x: now[0],
25308 y: now[1]
25309 }
25310 });
25311 }
25312
25313 if (r.touchData.start) {
25314 r.touchData.start._private.grabbed = false;
25315 }
25316
25317 r.touchData.cxt = true;
25318 r.touchData.cxtDragged = false;
25319 r.data.bgActivePosistion = undefined;
25320 r.redraw();
25321 return;
25322 }
25323 }
25324
25325 if (e.touches[2]) {
25326 // ignore
25327 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25328 if (cy.boxSelectionEnabled()) {
25329 e.preventDefault();
25330 }
25331 } else if (e.touches[1]) ; else if (e.touches[0]) {
25332 var nears = r.findNearestElements(now[0], now[1], true, true);
25333 var near = nears[0];
25334
25335 if (near != null) {
25336 near.activate();
25337 r.touchData.start = near;
25338 r.touchData.starts = nears;
25339
25340 if (r.nodeIsGrabbable(near)) {
25341 var draggedEles = r.dragData.touchDragEles = cy.collection();
25342 var selectedNodes = null;
25343 r.redrawHint('eles', true);
25344 r.redrawHint('drag', true);
25345
25346 if (near.selected()) {
25347 // reset drag elements, since near will be added again
25348 selectedNodes = cy.$(function (ele) {
25349 return ele.selected() && r.nodeIsGrabbable(ele);
25350 });
25351 addNodesToDrag(selectedNodes, {
25352 addToList: draggedEles
25353 });
25354 } else {
25355 addNodeToDrag(near, {
25356 addToList: draggedEles
25357 });
25358 }
25359
25360 setGrabTarget(near);
25361
25362 var makeEvent = function makeEvent(type) {
25363 return {
25364 originalEvent: e,
25365 type: type,
25366 position: {
25367 x: now[0],
25368 y: now[1]
25369 }
25370 };
25371 };
25372
25373 near.emit(makeEvent('grabon'));
25374
25375 if (selectedNodes) {
25376 selectedNodes.forEach(function (n) {
25377 n.emit(makeEvent('grab'));
25378 });
25379 } else {
25380 near.emit(makeEvent('grab'));
25381 }
25382 }
25383 }
25384
25385 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25386 x: now[0],
25387 y: now[1]
25388 });
25389
25390 if (near == null) {
25391 r.data.bgActivePosistion = {
25392 x: pos[0],
25393 y: pos[1]
25394 };
25395 r.redrawHint('select', true);
25396 r.redraw();
25397 } // Tap, taphold
25398 // -----
25399
25400
25401 r.touchData.singleTouchMoved = false;
25402 r.touchData.singleTouchStartTime = +new Date();
25403 clearTimeout(r.touchData.tapholdTimeout);
25404 r.touchData.tapholdTimeout = setTimeout(function () {
25405 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25406 && !r.touchData.selecting // box selection shouldn't allow taphold through
25407 ) {
25408 triggerEvents(r.touchData.start, ['taphold'], e, {
25409 x: now[0],
25410 y: now[1]
25411 });
25412 }
25413 }, r.tapholdDuration);
25414 }
25415
25416 if (e.touches.length >= 1) {
25417 var sPos = r.touchData.startPosition = [];
25418
25419 for (var i = 0; i < now.length; i++) {
25420 sPos[i] = earlier[i] = now[i];
25421 }
25422
25423 var touch0 = e.touches[0];
25424 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25425 }
25426 }, false);
25427 var touchmoveHandler;
25428 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25429 // eslint-disable-line no-undef
25430 var capture = r.touchData.capture;
25431
25432 if (!capture && !eventInContainer(e)) {
25433 return;
25434 }
25435
25436 var select = r.selection;
25437 var cy = r.cy;
25438 var now = r.touchData.now;
25439 var earlier = r.touchData.earlier;
25440 var zoom = cy.zoom();
25441
25442 if (e.touches[0]) {
25443 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25444 now[0] = pos[0];
25445 now[1] = pos[1];
25446 }
25447
25448 if (e.touches[1]) {
25449 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25450 now[2] = pos[0];
25451 now[3] = pos[1];
25452 }
25453
25454 if (e.touches[2]) {
25455 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25456 now[4] = pos[0];
25457 now[5] = pos[1];
25458 }
25459
25460 var startGPos = r.touchData.startGPosition;
25461 var isOverThresholdDrag;
25462
25463 if (capture && e.touches[0] && startGPos) {
25464 var disp = [];
25465
25466 for (var j = 0; j < now.length; j++) {
25467 disp[j] = now[j] - earlier[j];
25468 }
25469
25470 var dx = e.touches[0].clientX - startGPos[0];
25471 var dx2 = dx * dx;
25472 var dy = e.touches[0].clientY - startGPos[1];
25473 var dy2 = dy * dy;
25474 var dist2 = dx2 + dy2;
25475 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25476 } // context swipe cancelling
25477
25478
25479 if (capture && r.touchData.cxt) {
25480 e.preventDefault();
25481 var f1x2 = e.touches[0].clientX - offsetLeft,
25482 f1y2 = e.touches[0].clientY - offsetTop;
25483 var f2x2 = e.touches[1].clientX - offsetLeft,
25484 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25485
25486 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25487 var factorSq = distance2Sq / distance1Sq;
25488 var distThreshold = 150;
25489 var distThresholdSq = distThreshold * distThreshold;
25490 var factorThreshold = 1.5;
25491 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25492
25493 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25494 r.touchData.cxt = false;
25495 r.data.bgActivePosistion = undefined;
25496 r.redrawHint('select', true);
25497 var cxtEvt = {
25498 originalEvent: e,
25499 type: 'cxttapend',
25500 position: {
25501 x: now[0],
25502 y: now[1]
25503 }
25504 };
25505
25506 if (r.touchData.start) {
25507 r.touchData.start.unactivate().emit(cxtEvt);
25508 r.touchData.start = null;
25509 } else {
25510 cy.emit(cxtEvt);
25511 }
25512 }
25513 } // context swipe
25514
25515
25516 if (capture && r.touchData.cxt) {
25517 var cxtEvt = {
25518 originalEvent: e,
25519 type: 'cxtdrag',
25520 position: {
25521 x: now[0],
25522 y: now[1]
25523 }
25524 };
25525 r.data.bgActivePosistion = undefined;
25526 r.redrawHint('select', true);
25527
25528 if (r.touchData.start) {
25529 r.touchData.start.emit(cxtEvt);
25530 } else {
25531 cy.emit(cxtEvt);
25532 }
25533
25534 if (r.touchData.start) {
25535 r.touchData.start._private.grabbed = false;
25536 }
25537
25538 r.touchData.cxtDragged = true;
25539 var near = r.findNearestElement(now[0], now[1], true, true);
25540
25541 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25542 if (r.touchData.cxtOver) {
25543 r.touchData.cxtOver.emit({
25544 originalEvent: e,
25545 type: 'cxtdragout',
25546 position: {
25547 x: now[0],
25548 y: now[1]
25549 }
25550 });
25551 }
25552
25553 r.touchData.cxtOver = near;
25554
25555 if (near) {
25556 near.emit({
25557 originalEvent: e,
25558 type: 'cxtdragover',
25559 position: {
25560 x: now[0],
25561 y: now[1]
25562 }
25563 });
25564 }
25565 } // box selection
25566
25567 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25568 e.preventDefault();
25569 r.data.bgActivePosistion = undefined;
25570 this.lastThreeTouch = +new Date();
25571
25572 if (!r.touchData.selecting) {
25573 cy.emit({
25574 originalEvent: e,
25575 type: 'boxstart',
25576 position: {
25577 x: now[0],
25578 y: now[1]
25579 }
25580 });
25581 }
25582
25583 r.touchData.selecting = true;
25584 r.touchData.didSelect = true;
25585 select[4] = 1;
25586
25587 if (!select || select.length === 0 || select[0] === undefined) {
25588 select[0] = (now[0] + now[2] + now[4]) / 3;
25589 select[1] = (now[1] + now[3] + now[5]) / 3;
25590 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25591 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25592 } else {
25593 select[2] = (now[0] + now[2] + now[4]) / 3;
25594 select[3] = (now[1] + now[3] + now[5]) / 3;
25595 }
25596
25597 r.redrawHint('select', true);
25598 r.redraw(); // pinch to zoom
25599 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25600 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25601 // two fingers => pinch to zoom
25602 e.preventDefault();
25603 r.data.bgActivePosistion = undefined;
25604 r.redrawHint('select', true);
25605 var draggedEles = r.dragData.touchDragEles;
25606
25607 if (draggedEles) {
25608 r.redrawHint('drag', true);
25609
25610 for (var i = 0; i < draggedEles.length; i++) {
25611 var de_p = draggedEles[i]._private;
25612 de_p.grabbed = false;
25613 de_p.rscratch.inDragLayer = false;
25614 }
25615 }
25616
25617 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25618
25619 var f1x2 = e.touches[0].clientX - offsetLeft,
25620 f1y2 = e.touches[0].clientY - offsetTop;
25621 var f2x2 = e.touches[1].clientX - offsetLeft,
25622 f2y2 = e.touches[1].clientY - offsetTop;
25623 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25624 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25625
25626 var factor = distance2 / distance1;
25627
25628 if (twoFingersStartInside) {
25629 // delta finger1
25630 var df1x = f1x2 - f1x1;
25631 var df1y = f1y2 - f1y1; // delta finger 2
25632
25633 var df2x = f2x2 - f2x1;
25634 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25635 // i.e. so pinching cancels out and moving together pans
25636
25637 var tx = (df1x + df2x) / 2;
25638 var ty = (df1y + df2y) / 2; // now calculate the zoom
25639
25640 var zoom1 = cy.zoom();
25641 var zoom2 = zoom1 * factor;
25642 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25643
25644 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25645 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25646 var pan2 = {
25647 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25648 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25649 }; // remove dragged eles
25650
25651 if (_start && _start.active()) {
25652 var draggedEles = r.dragData.touchDragEles;
25653 freeDraggedElements(draggedEles);
25654 r.redrawHint('drag', true);
25655 r.redrawHint('eles', true);
25656
25657 _start.unactivate().emit('freeon');
25658
25659 draggedEles.emit('free');
25660
25661 if (r.dragData.didDrag) {
25662 _start.emit('dragfreeon');
25663
25664 draggedEles.emit('dragfree');
25665 }
25666 }
25667
25668 cy.viewport({
25669 zoom: zoom2,
25670 pan: pan2,
25671 cancelOnFailedZoom: true
25672 });
25673 distance1 = distance2;
25674 f1x1 = f1x2;
25675 f1y1 = f1y2;
25676 f2x1 = f2x2;
25677 f2y1 = f2y2;
25678 r.pinching = true;
25679 } // Re-project
25680
25681
25682 if (e.touches[0]) {
25683 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25684 now[0] = pos[0];
25685 now[1] = pos[1];
25686 }
25687
25688 if (e.touches[1]) {
25689 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25690 now[2] = pos[0];
25691 now[3] = pos[1];
25692 }
25693
25694 if (e.touches[2]) {
25695 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25696 now[4] = pos[0];
25697 now[5] = pos[1];
25698 }
25699 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25700 ) {
25701 var start = r.touchData.start;
25702 var last = r.touchData.last;
25703 var near;
25704
25705 if (!r.hoverData.draggingEles && !r.swipePanning) {
25706 near = r.findNearestElement(now[0], now[1], true, true);
25707 }
25708
25709 if (capture && start != null) {
25710 e.preventDefault();
25711 } // dragging nodes
25712
25713
25714 if (capture && start != null && r.nodeIsDraggable(start)) {
25715 if (isOverThresholdDrag) {
25716 // then dragging can happen
25717 var draggedEles = r.dragData.touchDragEles;
25718 var justStartedDrag = !r.dragData.didDrag;
25719
25720 if (justStartedDrag) {
25721 addNodesToDrag(draggedEles, {
25722 inDragLayer: true
25723 });
25724 }
25725
25726 r.dragData.didDrag = true;
25727 var totalShift = {
25728 x: 0,
25729 y: 0
25730 };
25731
25732 if (number(disp[0]) && number(disp[1])) {
25733 totalShift.x += disp[0];
25734 totalShift.y += disp[1];
25735
25736 if (justStartedDrag) {
25737 r.redrawHint('eles', true);
25738 var dragDelta = r.touchData.dragDelta;
25739
25740 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25741 totalShift.x += dragDelta[0];
25742 totalShift.y += dragDelta[1];
25743 }
25744 }
25745 }
25746
25747 r.hoverData.draggingEles = true;
25748 draggedEles.silentShift(totalShift).emit('position drag');
25749 r.redrawHint('drag', true);
25750
25751 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25752 r.redrawHint('eles', true);
25753 }
25754
25755 r.redraw();
25756 } else {
25757 // otherise keep track of drag delta for later
25758 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25759
25760 if (dragDelta.length === 0) {
25761 dragDelta.push(disp[0]);
25762 dragDelta.push(disp[1]);
25763 } else {
25764 dragDelta[0] += disp[0];
25765 dragDelta[1] += disp[1];
25766 }
25767 }
25768 } // touchmove
25769
25770
25771 {
25772 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25773 x: now[0],
25774 y: now[1]
25775 });
25776
25777 if ((!start || !start.grabbed()) && near != last) {
25778 if (last) {
25779 last.emit({
25780 originalEvent: e,
25781 type: 'tapdragout',
25782 position: {
25783 x: now[0],
25784 y: now[1]
25785 }
25786 });
25787 }
25788
25789 if (near) {
25790 near.emit({
25791 originalEvent: e,
25792 type: 'tapdragover',
25793 position: {
25794 x: now[0],
25795 y: now[1]
25796 }
25797 });
25798 }
25799 }
25800
25801 r.touchData.last = near;
25802 } // check to cancel taphold
25803
25804 if (capture) {
25805 for (var i = 0; i < now.length; i++) {
25806 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25807 r.touchData.singleTouchMoved = true;
25808 }
25809 }
25810 } // panning
25811
25812
25813 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25814 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25815
25816 if (allowPassthrough) {
25817 e.preventDefault();
25818
25819 if (!r.data.bgActivePosistion) {
25820 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25821 }
25822
25823 if (r.swipePanning) {
25824 cy.panBy({
25825 x: disp[0] * zoom,
25826 y: disp[1] * zoom
25827 });
25828 } else if (isOverThresholdDrag) {
25829 r.swipePanning = true;
25830 cy.panBy({
25831 x: dx * zoom,
25832 y: dy * zoom
25833 });
25834
25835 if (start) {
25836 start.unactivate();
25837 r.redrawHint('select', true);
25838 r.touchData.start = null;
25839 }
25840 }
25841 } // Re-project
25842
25843
25844 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25845 now[0] = pos[0];
25846 now[1] = pos[1];
25847 }
25848 }
25849
25850 for (var j = 0; j < now.length; j++) {
25851 earlier[j] = now[j];
25852 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25853
25854
25855 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25856 r.data.bgActivePosistion = undefined;
25857 r.redrawHint('select', true);
25858 r.redraw();
25859 }
25860 }, false);
25861 var touchcancelHandler;
25862 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25863 // eslint-disable-line no-unused-vars
25864 var start = r.touchData.start;
25865 r.touchData.capture = false;
25866
25867 if (start) {
25868 start.unactivate();
25869 }
25870 });
25871 var touchendHandler;
25872 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25873 // eslint-disable-line no-unused-vars
25874 var start = r.touchData.start;
25875 var capture = r.touchData.capture;
25876
25877 if (capture) {
25878 if (e.touches.length === 0) {
25879 r.touchData.capture = false;
25880 }
25881
25882 e.preventDefault();
25883 } else {
25884 return;
25885 }
25886
25887 var select = r.selection;
25888 r.swipePanning = false;
25889 r.hoverData.draggingEles = false;
25890 var cy = r.cy;
25891 var zoom = cy.zoom();
25892 var now = r.touchData.now;
25893 var earlier = r.touchData.earlier;
25894
25895 if (e.touches[0]) {
25896 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25897 now[0] = pos[0];
25898 now[1] = pos[1];
25899 }
25900
25901 if (e.touches[1]) {
25902 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25903 now[2] = pos[0];
25904 now[3] = pos[1];
25905 }
25906
25907 if (e.touches[2]) {
25908 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25909 now[4] = pos[0];
25910 now[5] = pos[1];
25911 }
25912
25913 if (start) {
25914 start.unactivate();
25915 }
25916
25917 var ctxTapend;
25918
25919 if (r.touchData.cxt) {
25920 ctxTapend = {
25921 originalEvent: e,
25922 type: 'cxttapend',
25923 position: {
25924 x: now[0],
25925 y: now[1]
25926 }
25927 };
25928
25929 if (start) {
25930 start.emit(ctxTapend);
25931 } else {
25932 cy.emit(ctxTapend);
25933 }
25934
25935 if (!r.touchData.cxtDragged) {
25936 var ctxTap = {
25937 originalEvent: e,
25938 type: 'cxttap',
25939 position: {
25940 x: now[0],
25941 y: now[1]
25942 }
25943 };
25944
25945 if (start) {
25946 start.emit(ctxTap);
25947 } else {
25948 cy.emit(ctxTap);
25949 }
25950 }
25951
25952 if (r.touchData.start) {
25953 r.touchData.start._private.grabbed = false;
25954 }
25955
25956 r.touchData.cxt = false;
25957 r.touchData.start = null;
25958 r.redraw();
25959 return;
25960 } // no more box selection if we don't have three fingers
25961
25962
25963 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25964 r.touchData.selecting = false;
25965 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25966 select[0] = undefined;
25967 select[1] = undefined;
25968 select[2] = undefined;
25969 select[3] = undefined;
25970 select[4] = 0;
25971 r.redrawHint('select', true);
25972 cy.emit({
25973 type: 'boxend',
25974 originalEvent: e,
25975 position: {
25976 x: now[0],
25977 y: now[1]
25978 }
25979 });
25980
25981 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25982 return ele.selectable() && !ele.selected();
25983 };
25984
25985 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25986
25987 if (box.nonempty()) {
25988 r.redrawHint('eles', true);
25989 }
25990
25991 r.redraw();
25992 }
25993
25994 if (start != null) {
25995 start.unactivate();
25996 }
25997
25998 if (e.touches[2]) {
25999 r.data.bgActivePosistion = undefined;
26000 r.redrawHint('select', true);
26001 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26002 r.data.bgActivePosistion = undefined;
26003 r.redrawHint('select', true);
26004 var draggedEles = r.dragData.touchDragEles;
26005
26006 if (start != null) {
26007 var startWasGrabbed = start._private.grabbed;
26008 freeDraggedElements(draggedEles);
26009 r.redrawHint('drag', true);
26010 r.redrawHint('eles', true);
26011
26012 if (startWasGrabbed) {
26013 start.emit('freeon');
26014 draggedEles.emit('free');
26015
26016 if (r.dragData.didDrag) {
26017 start.emit('dragfreeon');
26018 draggedEles.emit('dragfree');
26019 }
26020 }
26021
26022 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26023 x: now[0],
26024 y: now[1]
26025 });
26026 start.unactivate();
26027 r.touchData.start = null;
26028 } else {
26029 var near = r.findNearestElement(now[0], now[1], true, true);
26030 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26031 x: now[0],
26032 y: now[1]
26033 });
26034 }
26035
26036 var dx = r.touchData.startPosition[0] - now[0];
26037 var dx2 = dx * dx;
26038 var dy = r.touchData.startPosition[1] - now[1];
26039 var dy2 = dy * dy;
26040 var dist2 = dx2 + dy2;
26041 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26042
26043 if (!r.touchData.singleTouchMoved) {
26044 if (!start) {
26045 cy.$(':selected').unselect(['tapunselect']);
26046 }
26047
26048 triggerEvents(start, ['tap', 'vclick'], e, {
26049 x: now[0],
26050 y: now[1]
26051 });
26052 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26053
26054
26055 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26056 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26057 ) {
26058 if (cy.selectionType() === 'single') {
26059 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26060 start.select(['tapselect']);
26061 } else {
26062 if (start.selected()) {
26063 start.unselect(['tapunselect']);
26064 } else {
26065 start.select(['tapselect']);
26066 }
26067 }
26068
26069 r.redrawHint('eles', true);
26070 }
26071
26072 r.touchData.singleTouchMoved = true;
26073 }
26074
26075 for (var j = 0; j < now.length; j++) {
26076 earlier[j] = now[j];
26077 }
26078
26079 r.dragData.didDrag = false; // reset for next touchstart
26080
26081 if (e.touches.length === 0) {
26082 r.touchData.dragDelta = [];
26083 r.touchData.startPosition = null;
26084 r.touchData.startGPosition = null;
26085 r.touchData.didSelect = false;
26086 }
26087
26088 if (e.touches.length < 2) {
26089 if (e.touches.length === 1) {
26090 // the old start global pos'n may not be the same finger that remains
26091 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26092 }
26093
26094 r.pinching = false;
26095 r.redrawHint('eles', true);
26096 r.redraw();
26097 } //r.redraw();
26098
26099 }, false); // fallback compatibility layer for ms pointer events
26100
26101 if (typeof TouchEvent === 'undefined') {
26102 var pointers = [];
26103
26104 var makeTouch = function makeTouch(e) {
26105 return {
26106 clientX: e.clientX,
26107 clientY: e.clientY,
26108 force: 1,
26109 identifier: e.pointerId,
26110 pageX: e.pageX,
26111 pageY: e.pageY,
26112 radiusX: e.width / 2,
26113 radiusY: e.height / 2,
26114 screenX: e.screenX,
26115 screenY: e.screenY,
26116 target: e.target
26117 };
26118 };
26119
26120 var makePointer = function makePointer(e) {
26121 return {
26122 event: e,
26123 touch: makeTouch(e)
26124 };
26125 };
26126
26127 var addPointer = function addPointer(e) {
26128 pointers.push(makePointer(e));
26129 };
26130
26131 var removePointer = function removePointer(e) {
26132 for (var i = 0; i < pointers.length; i++) {
26133 var p = pointers[i];
26134
26135 if (p.event.pointerId === e.pointerId) {
26136 pointers.splice(i, 1);
26137 return;
26138 }
26139 }
26140 };
26141
26142 var updatePointer = function updatePointer(e) {
26143 var p = pointers.filter(function (p) {
26144 return p.event.pointerId === e.pointerId;
26145 })[0];
26146 p.event = e;
26147 p.touch = makeTouch(e);
26148 };
26149
26150 var addTouchesToEvent = function addTouchesToEvent(e) {
26151 e.touches = pointers.map(function (p) {
26152 return p.touch;
26153 });
26154 };
26155
26156 var pointerIsMouse = function pointerIsMouse(e) {
26157 return e.pointerType === 'mouse' || e.pointerType === 4;
26158 };
26159
26160 r.registerBinding(r.container, 'pointerdown', function (e) {
26161 if (pointerIsMouse(e)) {
26162 return;
26163 } // mouse already handled
26164
26165
26166 e.preventDefault();
26167 addPointer(e);
26168 addTouchesToEvent(e);
26169 touchstartHandler(e);
26170 });
26171 r.registerBinding(r.container, 'pointerup', function (e) {
26172 if (pointerIsMouse(e)) {
26173 return;
26174 } // mouse already handled
26175
26176
26177 removePointer(e);
26178 addTouchesToEvent(e);
26179 touchendHandler(e);
26180 });
26181 r.registerBinding(r.container, 'pointercancel', function (e) {
26182 if (pointerIsMouse(e)) {
26183 return;
26184 } // mouse already handled
26185
26186
26187 removePointer(e);
26188 addTouchesToEvent(e);
26189 touchcancelHandler(e);
26190 });
26191 r.registerBinding(r.container, 'pointermove', function (e) {
26192 if (pointerIsMouse(e)) {
26193 return;
26194 } // mouse already handled
26195
26196
26197 e.preventDefault();
26198 updatePointer(e);
26199 addTouchesToEvent(e);
26200 touchmoveHandler(e);
26201 });
26202 }
26203};
26204
26205var BRp$d = {};
26206
26207BRp$d.generatePolygon = function (name, points) {
26208 return this.nodeShapes[name] = {
26209 renderer: this,
26210 name: name,
26211 points: points,
26212 draw: function draw(context, centerX, centerY, width, height) {
26213 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26214 },
26215 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26216 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26217 },
26218 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26219 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26220 }
26221 };
26222};
26223
26224BRp$d.generateEllipse = function () {
26225 return this.nodeShapes['ellipse'] = {
26226 renderer: this,
26227 name: 'ellipse',
26228 draw: function draw(context, centerX, centerY, width, height) {
26229 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26230 },
26231 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26232 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26233 },
26234 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26235 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26236 }
26237 };
26238};
26239
26240BRp$d.generateRoundPolygon = function (name, points) {
26241 // Pre-compute control points
26242 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26243 // the unit vectors.
26244 // For simplicity the layout will be:
26245 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26246 var allPoints = new Array(points.length * 2);
26247
26248 for (var i = 0; i < points.length / 2; i++) {
26249 var sourceIndex = i * 2;
26250 var destIndex = void 0;
26251
26252 if (i < points.length / 2 - 1) {
26253 destIndex = (i + 1) * 2;
26254 } else {
26255 destIndex = 0;
26256 }
26257
26258 allPoints[i * 4] = points[sourceIndex];
26259 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26260 var xDest = points[destIndex] - points[sourceIndex];
26261 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26262 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26263 allPoints[i * 4 + 2] = xDest / norm;
26264 allPoints[i * 4 + 3] = yDest / norm;
26265 }
26266
26267 return this.nodeShapes[name] = {
26268 renderer: this,
26269 name: name,
26270 points: allPoints,
26271 draw: function draw(context, centerX, centerY, width, height) {
26272 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26273 },
26274 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26275 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26276 },
26277 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26278 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26279 }
26280 };
26281};
26282
26283BRp$d.generateRoundRectangle = function () {
26284 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26285 renderer: this,
26286 name: 'round-rectangle',
26287 points: generateUnitNgonPointsFitToSquare(4, 0),
26288 draw: function draw(context, centerX, centerY, width, height) {
26289 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26290 },
26291 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26292 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26293 },
26294 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26295 var cornerRadius = getRoundRectangleRadius(width, height);
26296 var diam = cornerRadius * 2; // Check hBox
26297
26298 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26299 return true;
26300 } // Check vBox
26301
26302
26303 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26304 return true;
26305 } // Check top left quarter circle
26306
26307
26308 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26309 return true;
26310 } // Check top right quarter circle
26311
26312
26313 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26314 return true;
26315 } // Check bottom right quarter circle
26316
26317
26318 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26319 return true;
26320 } // Check bottom left quarter circle
26321
26322
26323 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26324 return true;
26325 }
26326
26327 return false;
26328 }
26329 };
26330};
26331
26332BRp$d.generateCutRectangle = function () {
26333 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26334 renderer: this,
26335 name: 'cut-rectangle',
26336 cornerLength: getCutRectangleCornerLength(),
26337 points: generateUnitNgonPointsFitToSquare(4, 0),
26338 draw: function draw(context, centerX, centerY, width, height) {
26339 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26340 },
26341 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26342 var cl = this.cornerLength;
26343 var hh = height / 2;
26344 var hw = width / 2;
26345 var xBegin = centerX - hw;
26346 var xEnd = centerX + hw;
26347 var yBegin = centerY - hh;
26348 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26349
26350 return {
26351 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26352 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26353 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26354 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26355 };
26356 },
26357 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26358 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26359 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26360 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26361 },
26362 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26363 // Check hBox
26364 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26365 return true;
26366 } // Check vBox
26367
26368
26369 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26370 return true;
26371 }
26372
26373 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26374 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26375 }
26376 };
26377};
26378
26379BRp$d.generateBarrel = function () {
26380 return this.nodeShapes['barrel'] = {
26381 renderer: this,
26382 name: 'barrel',
26383 points: generateUnitNgonPointsFitToSquare(4, 0),
26384 draw: function draw(context, centerX, centerY, width, height) {
26385 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26386 },
26387 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26388 // use two fixed t values for the bezier curve approximation
26389 var t0 = 0.15;
26390 var t1 = 0.5;
26391 var t2 = 0.85;
26392 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26393
26394 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26395 // approximate curve pts based on the two t values
26396 var m0 = qbezierPtAt({
26397 x: pts[0],
26398 y: pts[1]
26399 }, {
26400 x: pts[2],
26401 y: pts[3]
26402 }, {
26403 x: pts[4],
26404 y: pts[5]
26405 }, t0);
26406 var m1 = qbezierPtAt({
26407 x: pts[0],
26408 y: pts[1]
26409 }, {
26410 x: pts[2],
26411 y: pts[3]
26412 }, {
26413 x: pts[4],
26414 y: pts[5]
26415 }, t1);
26416 var m2 = qbezierPtAt({
26417 x: pts[0],
26418 y: pts[1]
26419 }, {
26420 x: pts[2],
26421 y: pts[3]
26422 }, {
26423 x: pts[4],
26424 y: pts[5]
26425 }, t2);
26426 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26427 };
26428
26429 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26430 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26431 },
26432 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26433 var hh = height / 2;
26434 var hw = width / 2;
26435 var xBegin = centerX - hw;
26436 var xEnd = centerX + hw;
26437 var yBegin = centerY - hh;
26438 var yEnd = centerY + hh;
26439 var curveConstants = getBarrelCurveConstants(width, height);
26440 var hOffset = curveConstants.heightOffset;
26441 var wOffset = curveConstants.widthOffset;
26442 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26443
26444 var pts = {
26445 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26446 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26447 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26448 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26449 };
26450 pts.topLeft.isTop = true;
26451 pts.topRight.isTop = true;
26452 pts.bottomLeft.isBottom = true;
26453 pts.bottomRight.isBottom = true;
26454 return pts;
26455 },
26456 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26457 var curveConstants = getBarrelCurveConstants(width, height);
26458 var hOffset = curveConstants.heightOffset;
26459 var wOffset = curveConstants.widthOffset; // Check hBox
26460
26461 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26462 return true;
26463 } // Check vBox
26464
26465
26466 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26467 return true;
26468 }
26469
26470 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26471
26472 var getCurveT = function getCurveT(x, y, curvePts) {
26473 var x0 = curvePts[4];
26474 var x1 = curvePts[2];
26475 var x2 = curvePts[0];
26476 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26477
26478 var y2 = curvePts[1];
26479 var xMin = Math.min(x0, x2);
26480 var xMax = Math.max(x0, x2);
26481 var yMin = Math.min(y0, y2);
26482 var yMax = Math.max(y0, y2);
26483
26484 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26485 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26486 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26487 var validRoots = roots.filter(function (r) {
26488 return 0 <= r && r <= 1;
26489 });
26490
26491 if (validRoots.length > 0) {
26492 return validRoots[0];
26493 }
26494 }
26495
26496 return null;
26497 };
26498
26499 var curveRegions = Object.keys(barrelCurvePts);
26500
26501 for (var i = 0; i < curveRegions.length; i++) {
26502 var corner = curveRegions[i];
26503 var cornerPts = barrelCurvePts[corner];
26504 var t = getCurveT(x, y, cornerPts);
26505
26506 if (t == null) {
26507 continue;
26508 }
26509
26510 var y0 = cornerPts[5];
26511 var y1 = cornerPts[3];
26512 var y2 = cornerPts[1];
26513 var bezY = qbezierAt(y0, y1, y2, t);
26514
26515 if (cornerPts.isTop && bezY <= y) {
26516 return true;
26517 }
26518
26519 if (cornerPts.isBottom && y <= bezY) {
26520 return true;
26521 }
26522 }
26523
26524 return false;
26525 }
26526 };
26527};
26528
26529BRp$d.generateBottomRoundrectangle = function () {
26530 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26531 renderer: this,
26532 name: 'bottom-round-rectangle',
26533 points: generateUnitNgonPointsFitToSquare(4, 0),
26534 draw: function draw(context, centerX, centerY, width, height) {
26535 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26536 },
26537 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26538 var topStartX = nodeX - (width / 2 + padding);
26539 var topStartY = nodeY - (height / 2 + padding);
26540 var topEndY = topStartY;
26541 var topEndX = nodeX + (width / 2 + padding);
26542 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26543
26544 if (topIntersections.length > 0) {
26545 return topIntersections;
26546 }
26547
26548 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26549 },
26550 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26551 var cornerRadius = getRoundRectangleRadius(width, height);
26552 var diam = 2 * cornerRadius; // Check hBox
26553
26554 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26555 return true;
26556 } // Check vBox
26557
26558
26559 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26560 return true;
26561 } // check non-rounded top side
26562
26563
26564 var outerWidth = width / 2 + 2 * padding;
26565 var outerHeight = height / 2 + 2 * padding;
26566 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26567
26568 if (pointInsidePolygonPoints(x, y, points)) {
26569 return true;
26570 } // Check bottom right quarter circle
26571
26572
26573 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26574 return true;
26575 } // Check bottom left quarter circle
26576
26577
26578 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26579 return true;
26580 }
26581
26582 return false;
26583 }
26584 };
26585};
26586
26587BRp$d.registerNodeShapes = function () {
26588 var nodeShapes = this.nodeShapes = {};
26589 var renderer = this;
26590 this.generateEllipse();
26591 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26592 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26593 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26594 nodeShapes['square'] = nodeShapes['rectangle'];
26595 this.generateRoundRectangle();
26596 this.generateCutRectangle();
26597 this.generateBarrel();
26598 this.generateBottomRoundrectangle();
26599 {
26600 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26601 this.generatePolygon('diamond', diamondPoints);
26602 this.generateRoundPolygon('round-diamond', diamondPoints);
26603 }
26604 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26605 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26606 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26607 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26608 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26609 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26610 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26611 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26612 var star5Points = new Array(20);
26613 {
26614 var outerPoints = generateUnitNgonPoints(5, 0);
26615 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26616
26617 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26618 innerRadius *= 1.57;
26619
26620 for (var i = 0; i < innerPoints.length / 2; i++) {
26621 innerPoints[i * 2] *= innerRadius;
26622 innerPoints[i * 2 + 1] *= innerRadius;
26623 }
26624
26625 for (var i = 0; i < 20 / 4; i++) {
26626 star5Points[i * 4] = outerPoints[i * 2];
26627 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26628 star5Points[i * 4 + 2] = innerPoints[i * 2];
26629 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26630 }
26631 }
26632 star5Points = fitPolygonToSquare(star5Points);
26633 this.generatePolygon('star', star5Points);
26634 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26635 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26636 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]);
26637 {
26638 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26639 this.generatePolygon('tag', tagPoints);
26640 this.generateRoundPolygon('round-tag', tagPoints);
26641 }
26642
26643 nodeShapes.makePolygon = function (points) {
26644 // use caching on user-specified polygons so they are as fast as native shapes
26645 var key = points.join('$');
26646 var name = 'polygon-' + key;
26647 var shape;
26648
26649 if (shape = this[name]) {
26650 // got cached shape
26651 return shape;
26652 } // create and cache new shape
26653
26654
26655 return renderer.generatePolygon(name, points);
26656 };
26657};
26658
26659var BRp$e = {};
26660
26661BRp$e.timeToRender = function () {
26662 return this.redrawTotalTime / this.redrawCount;
26663};
26664
26665BRp$e.redraw = function (options) {
26666 options = options || staticEmptyObject();
26667 var r = this;
26668
26669 if (r.averageRedrawTime === undefined) {
26670 r.averageRedrawTime = 0;
26671 }
26672
26673 if (r.lastRedrawTime === undefined) {
26674 r.lastRedrawTime = 0;
26675 }
26676
26677 if (r.lastDrawTime === undefined) {
26678 r.lastDrawTime = 0;
26679 }
26680
26681 r.requestedFrame = true;
26682 r.renderOptions = options;
26683};
26684
26685BRp$e.beforeRender = function (fn, priority) {
26686 // the renderer can't add tick callbacks when destroyed
26687 if (this.destroyed) {
26688 return;
26689 }
26690
26691 if (priority == null) {
26692 error('Priority is not optional for beforeRender');
26693 }
26694
26695 var cbs = this.beforeRenderCallbacks;
26696 cbs.push({
26697 fn: fn,
26698 priority: priority
26699 }); // higher priority callbacks executed first
26700
26701 cbs.sort(function (a, b) {
26702 return b.priority - a.priority;
26703 });
26704};
26705
26706var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26707 var cbs = r.beforeRenderCallbacks;
26708
26709 for (var i = 0; i < cbs.length; i++) {
26710 cbs[i].fn(willDraw, startTime);
26711 }
26712};
26713
26714BRp$e.startRenderLoop = function () {
26715 var r = this;
26716 var cy = r.cy;
26717
26718 if (r.renderLoopStarted) {
26719 return;
26720 } else {
26721 r.renderLoopStarted = true;
26722 }
26723
26724 var renderFn = function renderFn(requestTime) {
26725 if (r.destroyed) {
26726 return;
26727 }
26728
26729 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26730 beforeRenderCallbacks(r, true, requestTime);
26731 var startTime = performanceNow();
26732 r.render(r.renderOptions);
26733 var endTime = r.lastDrawTime = performanceNow();
26734
26735 if (r.averageRedrawTime === undefined) {
26736 r.averageRedrawTime = endTime - startTime;
26737 }
26738
26739 if (r.redrawCount === undefined) {
26740 r.redrawCount = 0;
26741 }
26742
26743 r.redrawCount++;
26744
26745 if (r.redrawTotalTime === undefined) {
26746 r.redrawTotalTime = 0;
26747 }
26748
26749 var duration = endTime - startTime;
26750 r.redrawTotalTime += duration;
26751 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26752
26753 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26754 r.requestedFrame = false;
26755 } else {
26756 beforeRenderCallbacks(r, false, requestTime);
26757 }
26758
26759 r.skipFrame = false;
26760 requestAnimationFrame(renderFn);
26761 };
26762
26763 requestAnimationFrame(renderFn);
26764};
26765
26766var BaseRenderer = function BaseRenderer(options) {
26767 this.init(options);
26768};
26769
26770var BR = BaseRenderer;
26771var BRp$f = BR.prototype;
26772BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26773
26774BRp$f.init = function (options) {
26775 var r = this;
26776 r.options = options;
26777 r.cy = options.cy;
26778 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26779
26780 if (window$1) {
26781 var document = window$1.document;
26782 var head = document.head;
26783 var stylesheetId = '__________cytoscape_stylesheet';
26784 var className = '__________cytoscape_container';
26785 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26786
26787 if (ctr.className.indexOf(className) < 0) {
26788 ctr.className = (ctr.className || '') + ' ' + className;
26789 }
26790
26791 if (!stylesheetAlreadyExists) {
26792 var stylesheet = document.createElement('style');
26793 stylesheet.id = stylesheetId;
26794 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26795 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26796 }
26797
26798 var computedStyle = window$1.getComputedStyle(ctr);
26799 var position = computedStyle.getPropertyValue('position');
26800
26801 if (position === 'static') {
26802 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26803 }
26804 }
26805
26806 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26807
26808 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26809
26810 r.hoverData = {
26811 down: null,
26812 last: null,
26813 downTime: null,
26814 triggerMode: null,
26815 dragging: false,
26816 initialPan: [null, null],
26817 capture: false
26818 };
26819 r.dragData = {
26820 possibleDragElements: []
26821 };
26822 r.touchData = {
26823 start: null,
26824 capture: false,
26825 // These 3 fields related to tap, taphold events
26826 startPosition: [null, null, null, null, null, null],
26827 singleTouchStartTime: null,
26828 singleTouchMoved: true,
26829 now: [null, null, null, null, null, null],
26830 earlier: [null, null, null, null, null, null]
26831 };
26832 r.redraws = 0;
26833 r.showFps = options.showFps;
26834 r.debug = options.debug;
26835 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26836 r.textureOnViewport = options.textureOnViewport;
26837 r.wheelSensitivity = options.wheelSensitivity;
26838 r.motionBlurEnabled = options.motionBlur; // on by default
26839
26840 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26841 r.motionBlur = options.motionBlur; // for initial kick off
26842
26843 r.motionBlurOpacity = options.motionBlurOpacity;
26844 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26845 r.motionBlurPxRatio = 1;
26846 r.mbPxRBlurry = 1; //0.8;
26847
26848 r.minMbLowQualFrames = 4;
26849 r.fullQualityMb = false;
26850 r.clearedForMotionBlur = [];
26851 r.desktopTapThreshold = options.desktopTapThreshold;
26852 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26853 r.touchTapThreshold = options.touchTapThreshold;
26854 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26855 r.tapholdDuration = 500;
26856 r.bindings = [];
26857 r.beforeRenderCallbacks = [];
26858 r.beforeRenderPriorities = {
26859 // higher priority execs before lower one
26860 animations: 400,
26861 eleCalcs: 300,
26862 eleTxrDeq: 200,
26863 lyrTxrDeq: 150,
26864 lyrTxrSkip: 100
26865 };
26866 r.registerNodeShapes();
26867 r.registerArrowShapes();
26868 r.registerCalculationListeners();
26869};
26870
26871BRp$f.notify = function (eventName, eles) {
26872 var r = this;
26873 var cy = r.cy; // the renderer can't be notified after it's destroyed
26874
26875 if (this.destroyed) {
26876 return;
26877 }
26878
26879 if (eventName === 'init') {
26880 r.load();
26881 return;
26882 }
26883
26884 if (eventName === 'destroy') {
26885 r.destroy();
26886 return;
26887 }
26888
26889 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26890 r.invalidateCachedZSortedEles();
26891 }
26892
26893 if (eventName === 'viewport') {
26894 r.redrawHint('select', true);
26895 }
26896
26897 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26898 r.invalidateContainerClientCoordsCache();
26899 r.matchCanvasSize(r.container);
26900 }
26901
26902 r.redrawHint('eles', true);
26903 r.redrawHint('drag', true);
26904 this.startRenderLoop();
26905 this.redraw();
26906};
26907
26908BRp$f.destroy = function () {
26909 var r = this;
26910 r.destroyed = true;
26911 r.cy.stopAnimationLoop();
26912
26913 for (var i = 0; i < r.bindings.length; i++) {
26914 var binding = r.bindings[i];
26915 var b = binding;
26916 var tgt = b.target;
26917 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26918 }
26919
26920 r.bindings = [];
26921 r.beforeRenderCallbacks = [];
26922 r.onUpdateEleCalcsFns = [];
26923
26924 if (r.removeObserver) {
26925 r.removeObserver.disconnect();
26926 }
26927
26928 if (r.styleObserver) {
26929 r.styleObserver.disconnect();
26930 }
26931
26932 if (r.resizeObserver) {
26933 r.resizeObserver.disconnect();
26934 }
26935
26936 if (r.labelCalcDiv) {
26937 try {
26938 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26939 } catch (e) {// ie10 issue #1014
26940 }
26941 }
26942};
26943
26944BRp$f.isHeadless = function () {
26945 return false;
26946};
26947
26948[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26949 extend(BRp$f, props);
26950});
26951
26952var fullFpsTime = 1000 / 60; // assume 60 frames per second
26953
26954var defs = {
26955 setupDequeueing: function setupDequeueing(opts) {
26956 return function setupDequeueingImpl() {
26957 var self = this;
26958 var r = this.renderer;
26959
26960 if (self.dequeueingSetup) {
26961 return;
26962 } else {
26963 self.dequeueingSetup = true;
26964 }
26965
26966 var queueRedraw = util(function () {
26967 r.redrawHint('eles', true);
26968 r.redrawHint('drag', true);
26969 r.redraw();
26970 }, opts.deqRedrawThreshold);
26971
26972 var dequeue = function dequeue(willDraw, frameStartTime) {
26973 var startTime = performanceNow();
26974 var avgRenderTime = r.averageRedrawTime;
26975 var renderTime = r.lastRedrawTime;
26976 var deqd = [];
26977 var extent = r.cy.extent();
26978 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26979 // queue won't automatically be flushed before dequeueing starts
26980
26981 if (!willDraw) {
26982 r.flushRenderedStyleQueue();
26983 }
26984
26985 while (true) {
26986 // eslint-disable-line no-constant-condition
26987 var now = performanceNow();
26988 var duration = now - startTime;
26989 var frameDuration = now - frameStartTime;
26990
26991 if (renderTime < fullFpsTime) {
26992 // if we're rendering faster than the ideal fps, then do dequeueing
26993 // during all of the remaining frame time
26994 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26995
26996 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26997 break;
26998 }
26999 } else {
27000 if (willDraw) {
27001 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27002 break;
27003 }
27004 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27005 break;
27006 }
27007 }
27008
27009 var thisDeqd = opts.deq(self, pixelRatio, extent);
27010
27011 if (thisDeqd.length > 0) {
27012 for (var i = 0; i < thisDeqd.length; i++) {
27013 deqd.push(thisDeqd[i]);
27014 }
27015 } else {
27016 break;
27017 }
27018 } // callbacks on dequeue
27019
27020
27021 if (deqd.length > 0) {
27022 opts.onDeqd(self, deqd);
27023
27024 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27025 queueRedraw();
27026 }
27027 }
27028 };
27029
27030 var priority = opts.priority || noop;
27031 r.beforeRender(dequeue, priority(self));
27032 };
27033 }
27034};
27035
27036// Uses keys so elements may share the same cache.
27037
27038var ElementTextureCacheLookup =
27039/*#__PURE__*/
27040function () {
27041 function ElementTextureCacheLookup(getKey) {
27042 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27043
27044 _classCallCheck(this, ElementTextureCacheLookup);
27045
27046 this.idsByKey = new Map$1();
27047 this.keyForId = new Map$1();
27048 this.cachesByLvl = new Map$1();
27049 this.lvls = [];
27050 this.getKey = getKey;
27051 this.doesEleInvalidateKey = doesEleInvalidateKey;
27052 }
27053
27054 _createClass(ElementTextureCacheLookup, [{
27055 key: "getIdsFor",
27056 value: function getIdsFor(key) {
27057 if (key == null) {
27058 error("Can not get id list for null key");
27059 }
27060
27061 var idsByKey = this.idsByKey;
27062 var ids = this.idsByKey.get(key);
27063
27064 if (!ids) {
27065 ids = new Set$1();
27066 idsByKey.set(key, ids);
27067 }
27068
27069 return ids;
27070 }
27071 }, {
27072 key: "addIdForKey",
27073 value: function addIdForKey(key, id) {
27074 if (key != null) {
27075 this.getIdsFor(key).add(id);
27076 }
27077 }
27078 }, {
27079 key: "deleteIdForKey",
27080 value: function deleteIdForKey(key, id) {
27081 if (key != null) {
27082 this.getIdsFor(key)["delete"](id);
27083 }
27084 }
27085 }, {
27086 key: "getNumberOfIdsForKey",
27087 value: function getNumberOfIdsForKey(key) {
27088 if (key == null) {
27089 return 0;
27090 } else {
27091 return this.getIdsFor(key).size;
27092 }
27093 }
27094 }, {
27095 key: "updateKeyMappingFor",
27096 value: function updateKeyMappingFor(ele) {
27097 var id = ele.id();
27098 var prevKey = this.keyForId.get(id);
27099 var currKey = this.getKey(ele);
27100 this.deleteIdForKey(prevKey, id);
27101 this.addIdForKey(currKey, id);
27102 this.keyForId.set(id, currKey);
27103 }
27104 }, {
27105 key: "deleteKeyMappingFor",
27106 value: function deleteKeyMappingFor(ele) {
27107 var id = ele.id();
27108 var prevKey = this.keyForId.get(id);
27109 this.deleteIdForKey(prevKey, id);
27110 this.keyForId["delete"](id);
27111 }
27112 }, {
27113 key: "keyHasChangedFor",
27114 value: function keyHasChangedFor(ele) {
27115 var id = ele.id();
27116 var prevKey = this.keyForId.get(id);
27117 var newKey = this.getKey(ele);
27118 return prevKey !== newKey;
27119 }
27120 }, {
27121 key: "isInvalid",
27122 value: function isInvalid(ele) {
27123 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27124 }
27125 }, {
27126 key: "getCachesAt",
27127 value: function getCachesAt(lvl) {
27128 var cachesByLvl = this.cachesByLvl,
27129 lvls = this.lvls;
27130 var caches = cachesByLvl.get(lvl);
27131
27132 if (!caches) {
27133 caches = new Map$1();
27134 cachesByLvl.set(lvl, caches);
27135 lvls.push(lvl);
27136 }
27137
27138 return caches;
27139 }
27140 }, {
27141 key: "getCache",
27142 value: function getCache(key, lvl) {
27143 return this.getCachesAt(lvl).get(key);
27144 }
27145 }, {
27146 key: "get",
27147 value: function get(ele, lvl) {
27148 var key = this.getKey(ele);
27149 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27150
27151 if (cache != null) {
27152 this.updateKeyMappingFor(ele);
27153 }
27154
27155 return cache;
27156 }
27157 }, {
27158 key: "getForCachedKey",
27159 value: function getForCachedKey(ele, lvl) {
27160 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27161
27162 var cache = this.getCache(key, lvl);
27163 return cache;
27164 }
27165 }, {
27166 key: "hasCache",
27167 value: function hasCache(key, lvl) {
27168 return this.getCachesAt(lvl).has(key);
27169 }
27170 }, {
27171 key: "has",
27172 value: function has(ele, lvl) {
27173 var key = this.getKey(ele);
27174 return this.hasCache(key, lvl);
27175 }
27176 }, {
27177 key: "setCache",
27178 value: function setCache(key, lvl, cache) {
27179 cache.key = key;
27180 this.getCachesAt(lvl).set(key, cache);
27181 }
27182 }, {
27183 key: "set",
27184 value: function set(ele, lvl, cache) {
27185 var key = this.getKey(ele);
27186 this.setCache(key, lvl, cache);
27187 this.updateKeyMappingFor(ele);
27188 }
27189 }, {
27190 key: "deleteCache",
27191 value: function deleteCache(key, lvl) {
27192 this.getCachesAt(lvl)["delete"](key);
27193 }
27194 }, {
27195 key: "delete",
27196 value: function _delete(ele, lvl) {
27197 var key = this.getKey(ele);
27198 this.deleteCache(key, lvl);
27199 }
27200 }, {
27201 key: "invalidateKey",
27202 value: function invalidateKey(key) {
27203 var _this = this;
27204
27205 this.lvls.forEach(function (lvl) {
27206 return _this.deleteCache(key, lvl);
27207 });
27208 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27209
27210 }, {
27211 key: "invalidate",
27212 value: function invalidate(ele) {
27213 var id = ele.id();
27214 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27215
27216 this.deleteKeyMappingFor(ele);
27217 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27218
27219 if (entireKeyInvalidated) {
27220 // clear mapping for current key
27221 this.invalidateKey(key);
27222 }
27223
27224 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27225 }
27226 }]);
27227
27228 return ElementTextureCacheLookup;
27229}();
27230
27231var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27232
27233var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27234
27235var minLvl = -4; // when scaling smaller than that we don't need to re-render
27236
27237var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27238
27239var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27240
27241var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27242
27243var defTxrWidth = 1024; // default/minimum texture width
27244
27245var maxTxrW = 1024; // the maximum width of a texture
27246
27247var maxTxrH = 1024; // the maximum height of a texture
27248
27249var minUtility = 0.2; // if usage of texture is less than this, it is retired
27250
27251var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27252
27253var maxFullnessChecks = 10; // dequeued after this many checks
27254
27255var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27256
27257var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27258
27259var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27260
27261var deqFastCost = 0.9; // % of frame time to be used when >60fps
27262
27263var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27264
27265var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27266
27267var getTxrReasons = {
27268 dequeue: 'dequeue',
27269 downscale: 'downscale',
27270 highQuality: 'highQuality'
27271};
27272var initDefaults = defaults({
27273 getKey: null,
27274 doesEleInvalidateKey: falsify,
27275 drawElement: null,
27276 getBoundingBox: null,
27277 getRotationPoint: null,
27278 getRotationOffset: null,
27279 isVisible: trueify,
27280 allowEdgeTxrCaching: true,
27281 allowParentTxrCaching: true
27282});
27283
27284var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27285 var self = this;
27286 self.renderer = renderer;
27287 self.onDequeues = [];
27288 var opts = initDefaults(initOptions);
27289 extend(self, opts);
27290 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27291 self.setupDequeueing();
27292};
27293
27294var ETCp = ElementTextureCache.prototype;
27295ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27296
27297ETCp.getTextureQueue = function (txrH) {
27298 var self = this;
27299 self.eleImgCaches = self.eleImgCaches || {};
27300 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27301}; // the list of usused textures which can be recycled (in use in texture queue)
27302
27303
27304ETCp.getRetiredTextureQueue = function (txrH) {
27305 var self = this;
27306 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27307 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27308 return rtxtrQ;
27309}; // queue of element draw requests at different scale levels
27310
27311
27312ETCp.getElementQueue = function () {
27313 var self = this;
27314 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27315 return b.reqs - a.reqs;
27316 });
27317 return q;
27318}; // queue of element draw requests at different scale levels (element id lookup)
27319
27320
27321ETCp.getElementKeyToQueue = function () {
27322 var self = this;
27323 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27324 return k2q;
27325};
27326
27327ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27328 var self = this;
27329 var r = this.renderer;
27330 var zoom = r.cy.zoom();
27331 var lookup = this.lookup;
27332
27333 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27334 return null;
27335 }
27336
27337 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27338 return null;
27339 }
27340
27341 if (lvl == null) {
27342 lvl = Math.ceil(log2(zoom * pxRatio));
27343 }
27344
27345 if (lvl < minLvl) {
27346 lvl = minLvl;
27347 } else if (zoom >= maxZoom || lvl > maxLvl) {
27348 return null;
27349 }
27350
27351 var scale = Math.pow(2, lvl);
27352 var eleScaledH = bb.h * scale;
27353 var eleScaledW = bb.w * scale;
27354 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27355
27356 if (!this.isVisible(ele, scaledLabelShown)) {
27357 return null;
27358 }
27359
27360 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27361
27362 if (eleCache && eleCache.invalidated) {
27363 eleCache.invalidated = false;
27364 eleCache.texture.invalidatedWidth -= eleCache.width;
27365 }
27366
27367 if (eleCache) {
27368 return eleCache;
27369 }
27370
27371 var txrH; // which texture height this ele belongs to
27372
27373 if (eleScaledH <= minTxrH) {
27374 txrH = minTxrH;
27375 } else if (eleScaledH <= txrStepH) {
27376 txrH = txrStepH;
27377 } else {
27378 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27379 }
27380
27381 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27382 return null; // caching large elements is not efficient
27383 }
27384
27385 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27386
27387 var txr = txrQ[txrQ.length - 2];
27388
27389 var addNewTxr = function addNewTxr() {
27390 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27391 }; // try the last one if there is no second last one
27392
27393
27394 if (!txr) {
27395 txr = txrQ[txrQ.length - 1];
27396 } // if the last one doesn't exist, we need a first one
27397
27398
27399 if (!txr) {
27400 txr = addNewTxr();
27401 } // if there's no room in the current texture, we need a new one
27402
27403
27404 if (txr.width - txr.usedWidth < eleScaledW) {
27405 txr = addNewTxr();
27406 }
27407
27408 var scalableFrom = function scalableFrom(otherCache) {
27409 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27410 };
27411
27412 var deqing = reason && reason === getTxrReasons.dequeue;
27413 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27414 var downscaleReq = reason && reason === getTxrReasons.downscale;
27415 var higherCache; // the nearest cache with a higher level
27416
27417 for (var l = lvl + 1; l <= maxLvl; l++) {
27418 var c = lookup.get(ele, l);
27419
27420 if (c) {
27421 higherCache = c;
27422 break;
27423 }
27424 }
27425
27426 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27427
27428 var downscale = function downscale() {
27429 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27430 }; // reset ele area in texture
27431
27432
27433 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27434 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27435
27436 if (scalableFrom(oneUpCache)) {
27437 // then we can relatively cheaply rescale the existing image w/o rerendering
27438 downscale();
27439 } else if (scalableFrom(higherCache)) {
27440 // then use the higher cache for now and queue the next level down
27441 // to cheaply scale towards the smaller level
27442 if (highQualityReq) {
27443 for (var _l = higherCache.level; _l > lvl; _l--) {
27444 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27445 }
27446
27447 downscale();
27448 } else {
27449 self.queueElement(ele, higherCache.level - 1);
27450 return higherCache;
27451 }
27452 } else {
27453 var lowerCache; // the nearest cache with a lower level
27454
27455 if (!deqing && !highQualityReq && !downscaleReq) {
27456 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27457 var _c = lookup.get(ele, _l2);
27458
27459 if (_c) {
27460 lowerCache = _c;
27461 break;
27462 }
27463 }
27464 }
27465
27466 if (scalableFrom(lowerCache)) {
27467 // then use the lower quality cache for now and queue the better one for later
27468 self.queueElement(ele, lvl);
27469 return lowerCache;
27470 }
27471
27472 txr.context.translate(txr.usedWidth, 0);
27473 txr.context.scale(scale, scale);
27474 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27475 txr.context.scale(1 / scale, 1 / scale);
27476 txr.context.translate(-txr.usedWidth, 0);
27477 }
27478
27479 eleCache = {
27480 x: txr.usedWidth,
27481 texture: txr,
27482 level: lvl,
27483 scale: scale,
27484 width: eleScaledW,
27485 height: eleScaledH,
27486 scaledLabelShown: scaledLabelShown
27487 };
27488 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27489 txr.eleCaches.push(eleCache);
27490 lookup.set(ele, lvl, eleCache);
27491 self.checkTextureFullness(txr);
27492 return eleCache;
27493};
27494
27495ETCp.invalidateElements = function (eles) {
27496 for (var i = 0; i < eles.length; i++) {
27497 this.invalidateElement(eles[i]);
27498 }
27499};
27500
27501ETCp.invalidateElement = function (ele) {
27502 var self = this;
27503 var lookup = self.lookup;
27504 var caches = [];
27505 var invalid = lookup.isInvalid(ele);
27506
27507 if (!invalid) {
27508 return; // override the invalidation request if the element key has not changed
27509 }
27510
27511 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27512 var cache = lookup.getForCachedKey(ele, lvl);
27513
27514 if (cache) {
27515 caches.push(cache);
27516 }
27517 }
27518
27519 var noOtherElesUseCache = lookup.invalidate(ele);
27520
27521 if (noOtherElesUseCache) {
27522 for (var i = 0; i < caches.length; i++) {
27523 var _cache = caches[i];
27524 var txr = _cache.texture; // remove space from the texture it belongs to
27525
27526 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27527
27528 _cache.invalidated = true; // retire the texture if its utility is low
27529
27530 self.checkTextureUtility(txr);
27531 }
27532 } // remove from queue since the old req was for the old state
27533
27534
27535 self.removeFromQueue(ele);
27536};
27537
27538ETCp.checkTextureUtility = function (txr) {
27539 // invalidate all entries in the cache if the cache size is small
27540 if (txr.invalidatedWidth >= minUtility * txr.width) {
27541 this.retireTexture(txr);
27542 }
27543};
27544
27545ETCp.checkTextureFullness = function (txr) {
27546 // if texture has been mostly filled and passed over several times, remove
27547 // it from the queue so we don't need to waste time looking at it to put new things
27548 var self = this;
27549 var txrQ = self.getTextureQueue(txr.height);
27550
27551 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27552 removeFromArray(txrQ, txr);
27553 } else {
27554 txr.fullnessChecks++;
27555 }
27556};
27557
27558ETCp.retireTexture = function (txr) {
27559 var self = this;
27560 var txrH = txr.height;
27561 var txrQ = self.getTextureQueue(txrH);
27562 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27563
27564 removeFromArray(txrQ, txr);
27565 txr.retired = true; // remove the refs from the eles to the caches:
27566
27567 var eleCaches = txr.eleCaches;
27568
27569 for (var i = 0; i < eleCaches.length; i++) {
27570 var eleCache = eleCaches[i];
27571 lookup.deleteCache(eleCache.key, eleCache.level);
27572 }
27573
27574 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27575
27576 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27577 rtxtrQ.push(txr);
27578};
27579
27580ETCp.addTexture = function (txrH, minW) {
27581 var self = this;
27582 var txrQ = self.getTextureQueue(txrH);
27583 var txr = {};
27584 txrQ.push(txr);
27585 txr.eleCaches = [];
27586 txr.height = txrH;
27587 txr.width = Math.max(defTxrWidth, minW);
27588 txr.usedWidth = 0;
27589 txr.invalidatedWidth = 0;
27590 txr.fullnessChecks = 0;
27591 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27592 txr.context = txr.canvas.getContext('2d');
27593 return txr;
27594};
27595
27596ETCp.recycleTexture = function (txrH, minW) {
27597 var self = this;
27598 var txrQ = self.getTextureQueue(txrH);
27599 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27600
27601 for (var i = 0; i < rtxtrQ.length; i++) {
27602 var txr = rtxtrQ[i];
27603
27604 if (txr.width >= minW) {
27605 txr.retired = false;
27606 txr.usedWidth = 0;
27607 txr.invalidatedWidth = 0;
27608 txr.fullnessChecks = 0;
27609 clearArray(txr.eleCaches);
27610 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27611 txr.context.clearRect(0, 0, txr.width, txr.height);
27612 removeFromArray(rtxtrQ, txr);
27613 txrQ.push(txr);
27614 return txr;
27615 }
27616 }
27617};
27618
27619ETCp.queueElement = function (ele, lvl) {
27620 var self = this;
27621 var q = self.getElementQueue();
27622 var k2q = self.getElementKeyToQueue();
27623 var key = this.getKey(ele);
27624 var existingReq = k2q[key];
27625
27626 if (existingReq) {
27627 // use the max lvl b/c in between lvls are cheap to make
27628 existingReq.level = Math.max(existingReq.level, lvl);
27629 existingReq.eles.merge(ele);
27630 existingReq.reqs++;
27631 q.updateItem(existingReq);
27632 } else {
27633 var req = {
27634 eles: ele.spawn().merge(ele),
27635 level: lvl,
27636 reqs: 1,
27637 key: key
27638 };
27639 q.push(req);
27640 k2q[key] = req;
27641 }
27642};
27643
27644ETCp.dequeue = function (pxRatio
27645/*, extent*/
27646) {
27647 var self = this;
27648 var q = self.getElementQueue();
27649 var k2q = self.getElementKeyToQueue();
27650 var dequeued = [];
27651 var lookup = self.lookup;
27652
27653 for (var i = 0; i < maxDeqSize; i++) {
27654 if (q.size() > 0) {
27655 var req = q.pop();
27656 var key = req.key;
27657 var ele = req.eles[0]; // all eles have the same key
27658
27659 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27660
27661 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27662
27663 if (cacheExists) {
27664 continue;
27665 }
27666
27667 dequeued.push(req);
27668 var bb = self.getBoundingBox(ele);
27669 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27670 } else {
27671 break;
27672 }
27673 }
27674
27675 return dequeued;
27676};
27677
27678ETCp.removeFromQueue = function (ele) {
27679 var self = this;
27680 var q = self.getElementQueue();
27681 var k2q = self.getElementKeyToQueue();
27682 var key = this.getKey(ele);
27683 var req = k2q[key];
27684
27685 if (req != null) {
27686 if (req.eles.length === 1) {
27687 // remove if last ele in the req
27688 // bring to front of queue
27689 req.reqs = MAX_INT;
27690 q.updateItem(req);
27691 q.pop(); // remove from queue
27692
27693 k2q[key] = null; // remove from lookup map
27694 } else {
27695 // otherwise just remove ele from req
27696 req.eles.unmerge(ele);
27697 }
27698 }
27699};
27700
27701ETCp.onDequeue = function (fn) {
27702 this.onDequeues.push(fn);
27703};
27704
27705ETCp.offDequeue = function (fn) {
27706 removeFromArray(this.onDequeues, fn);
27707};
27708
27709ETCp.setupDequeueing = defs.setupDequeueing({
27710 deqRedrawThreshold: deqRedrawThreshold,
27711 deqCost: deqCost,
27712 deqAvgCost: deqAvgCost,
27713 deqNoDrawCost: deqNoDrawCost,
27714 deqFastCost: deqFastCost,
27715 deq: function deq(self, pxRatio, extent) {
27716 return self.dequeue(pxRatio, extent);
27717 },
27718 onDeqd: function onDeqd(self, deqd) {
27719 for (var i = 0; i < self.onDequeues.length; i++) {
27720 var fn = self.onDequeues[i];
27721 fn(deqd);
27722 }
27723 },
27724 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27725 for (var i = 0; i < deqd.length; i++) {
27726 var eles = deqd[i].eles;
27727
27728 for (var j = 0; j < eles.length; j++) {
27729 var bb = eles[j].boundingBox();
27730
27731 if (boundingBoxesIntersect(bb, extent)) {
27732 return true;
27733 }
27734 }
27735 }
27736
27737 return false;
27738 },
27739 priority: function priority(self) {
27740 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27741 }
27742});
27743
27744var defNumLayers = 1; // default number of layers to use
27745
27746var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27747
27748var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27749
27750var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27751
27752var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27753
27754var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27755
27756var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27757
27758var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27759
27760var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27761
27762var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27763
27764var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27765
27766var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27767
27768var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27769
27770var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27771// var log = function(){ console.log.apply( console, arguments ); };
27772
27773var LayeredTextureCache = function LayeredTextureCache(renderer) {
27774 var self = this;
27775 var r = self.renderer = renderer;
27776 var cy = r.cy;
27777 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27778
27779 self.firstGet = true;
27780 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27781 self.skipping = false;
27782 self.eleTxrDeqs = cy.collection();
27783 self.scheduleElementRefinement = util(function () {
27784 self.refineElementTextures(self.eleTxrDeqs);
27785 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27786 }, refineEleDebounceTime);
27787 r.beforeRender(function (willDraw, now) {
27788 if (now - self.lastInvalidationTime <= invalidThreshold) {
27789 self.skipping = true;
27790 } else {
27791 self.skipping = false;
27792 }
27793 }, r.beforeRenderPriorities.lyrTxrSkip);
27794
27795 var qSort = function qSort(a, b) {
27796 return b.reqs - a.reqs;
27797 };
27798
27799 self.layersQueue = new Heap(qSort);
27800 self.setupDequeueing();
27801};
27802
27803var LTCp = LayeredTextureCache.prototype;
27804var layerIdPool = 0;
27805var MAX_INT$1 = Math.pow(2, 53) - 1;
27806
27807LTCp.makeLayer = function (bb, lvl) {
27808 var scale = Math.pow(2, lvl);
27809 var w = Math.ceil(bb.w * scale);
27810 var h = Math.ceil(bb.h * scale);
27811 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27812 var layer = {
27813 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27814 bb: bb,
27815 level: lvl,
27816 width: w,
27817 height: h,
27818 canvas: canvas,
27819 context: canvas.getContext('2d'),
27820 eles: [],
27821 elesQueue: [],
27822 reqs: 0
27823 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27824
27825 var cxt = layer.context;
27826 var dx = -layer.bb.x1;
27827 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27828
27829 cxt.scale(scale, scale);
27830 cxt.translate(dx, dy);
27831 return layer;
27832};
27833
27834LTCp.getLayers = function (eles, pxRatio, lvl) {
27835 var self = this;
27836 var r = self.renderer;
27837 var cy = r.cy;
27838 var zoom = cy.zoom();
27839 var firstGet = self.firstGet;
27840 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27841 //log eles.map(function(ele){ return ele.id() }) );
27842
27843 if (lvl == null) {
27844 lvl = Math.ceil(log2(zoom * pxRatio));
27845
27846 if (lvl < minLvl$1) {
27847 lvl = minLvl$1;
27848 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27849 return null;
27850 }
27851 }
27852
27853 self.validateLayersElesOrdering(lvl, eles);
27854 var layersByLvl = self.layersByLevel;
27855 var scale = Math.pow(2, lvl);
27856 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27857 var bb;
27858 var lvlComplete = self.levelIsComplete(lvl, eles);
27859 var tmpLayers;
27860
27861 var checkTempLevels = function checkTempLevels() {
27862 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27863 self.validateLayersElesOrdering(l, eles);
27864
27865 if (self.levelIsComplete(l, eles)) {
27866 tmpLayers = layersByLvl[l];
27867 return true;
27868 }
27869 };
27870
27871 var checkLvls = function checkLvls(dir) {
27872 if (tmpLayers) {
27873 return;
27874 }
27875
27876 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27877 if (canUseAsTmpLvl(l)) {
27878 break;
27879 }
27880 }
27881 };
27882
27883 checkLvls(+1);
27884 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27885
27886 for (var i = layers.length - 1; i >= 0; i--) {
27887 var layer = layers[i];
27888
27889 if (layer.invalid) {
27890 removeFromArray(layers, layer);
27891 }
27892 }
27893 };
27894
27895 if (!lvlComplete) {
27896 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27897 // and later queue the current layerset so we can get the proper quality level soon
27898 checkTempLevels();
27899 } else {
27900 // log('level complete, using existing layers\n--');
27901 return layers;
27902 }
27903
27904 var getBb = function getBb() {
27905 if (!bb) {
27906 bb = makeBoundingBox();
27907
27908 for (var i = 0; i < eles.length; i++) {
27909 updateBoundingBox(bb, eles[i].boundingBox());
27910 }
27911 }
27912
27913 return bb;
27914 };
27915
27916 var makeLayer = function makeLayer(opts) {
27917 opts = opts || {};
27918 var after = opts.after;
27919 getBb();
27920 var area = bb.w * scale * (bb.h * scale);
27921
27922 if (area > maxLayerArea) {
27923 return null;
27924 }
27925
27926 var layer = self.makeLayer(bb, lvl);
27927
27928 if (after != null) {
27929 var index = layers.indexOf(after) + 1;
27930 layers.splice(index, 0, layer);
27931 } else if (opts.insert === undefined || opts.insert) {
27932 // no after specified => first layer made so put at start
27933 layers.unshift(layer);
27934 } // if( tmpLayers ){
27935 //self.queueLayer( layer );
27936 // }
27937
27938
27939 return layer;
27940 };
27941
27942 if (self.skipping && !firstGet) {
27943 // log('skip layers');
27944 return null;
27945 } // log('do layers');
27946
27947
27948 var layer = null;
27949 var maxElesPerLayer = eles.length / defNumLayers;
27950 var allowLazyQueueing = !firstGet;
27951
27952 for (var i = 0; i < eles.length; i++) {
27953 var ele = eles[i];
27954 var rs = ele._private.rscratch;
27955 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27956
27957 var existingLayer = caches[lvl];
27958
27959 if (existingLayer) {
27960 // reuse layer for later eles
27961 // log('reuse layer for', ele.id());
27962 layer = existingLayer;
27963 continue;
27964 }
27965
27966 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27967 // log('make new layer for ele %s', ele.id());
27968 layer = makeLayer({
27969 insert: true,
27970 after: layer
27971 }); // if now layer can be built then we can't use layers at this level
27972
27973 if (!layer) {
27974 return null;
27975 } // log('new layer with id %s', layer.id);
27976
27977 }
27978
27979 if (tmpLayers || allowLazyQueueing) {
27980 // log('queue ele %s in layer %s', ele.id(), layer.id);
27981 self.queueLayer(layer, ele);
27982 } else {
27983 // log('draw ele %s in layer %s', ele.id(), layer.id);
27984 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27985 }
27986
27987 layer.eles.push(ele);
27988 caches[lvl] = layer;
27989 } // log('--');
27990
27991
27992 if (tmpLayers) {
27993 // then we only queued the current layerset and can't draw it yet
27994 return tmpLayers;
27995 }
27996
27997 if (allowLazyQueueing) {
27998 // log('lazy queue level', lvl);
27999 return null;
28000 }
28001
28002 return layers;
28003}; // a layer may want to use an ele cache of a higher level to avoid blurriness
28004// so the layer level might not equal the ele level
28005
28006
28007LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28008 return lvl;
28009};
28010
28011LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28012 var self = this;
28013 var r = this.renderer;
28014 var context = layer.context;
28015 var bb = ele.boundingBox();
28016
28017 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28018 return;
28019 }
28020
28021 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28022
28023 {
28024 r.setImgSmoothing(context, false);
28025 }
28026
28027 {
28028 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28029 }
28030
28031 {
28032 r.setImgSmoothing(context, true);
28033 }
28034};
28035
28036LTCp.levelIsComplete = function (lvl, eles) {
28037 var self = this;
28038 var layers = self.layersByLevel[lvl];
28039
28040 if (!layers || layers.length === 0) {
28041 return false;
28042 }
28043
28044 var numElesInLayers = 0;
28045
28046 for (var i = 0; i < layers.length; i++) {
28047 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28048
28049 if (layer.reqs > 0) {
28050 return false;
28051 } // if the layer is invalid, the level is not complete
28052
28053
28054 if (layer.invalid) {
28055 return false;
28056 }
28057
28058 numElesInLayers += layer.eles.length;
28059 } // we should have exactly the number of eles passed in to be complete
28060
28061
28062 if (numElesInLayers !== eles.length) {
28063 return false;
28064 }
28065
28066 return true;
28067};
28068
28069LTCp.validateLayersElesOrdering = function (lvl, eles) {
28070 var layers = this.layersByLevel[lvl];
28071
28072 if (!layers) {
28073 return;
28074 } // if in a layer the eles are not in the same order, then the layer is invalid
28075 // (i.e. there is an ele in between the eles in the layer)
28076
28077
28078 for (var i = 0; i < layers.length; i++) {
28079 var layer = layers[i];
28080 var offset = -1; // find the offset
28081
28082 for (var j = 0; j < eles.length; j++) {
28083 if (layer.eles[0] === eles[j]) {
28084 offset = j;
28085 break;
28086 }
28087 }
28088
28089 if (offset < 0) {
28090 // then the layer has nonexistant elements and is invalid
28091 this.invalidateLayer(layer);
28092 continue;
28093 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28094
28095
28096 var o = offset;
28097
28098 for (var j = 0; j < layer.eles.length; j++) {
28099 if (layer.eles[j] !== eles[o + j]) {
28100 // log('invalidate based on ordering', layer.id);
28101 this.invalidateLayer(layer);
28102 break;
28103 }
28104 }
28105 }
28106};
28107
28108LTCp.updateElementsInLayers = function (eles, update) {
28109 var self = this;
28110 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28111 // layer itself along the way
28112
28113 for (var i = 0; i < eles.length; i++) {
28114 var req = isEles ? null : eles[i];
28115 var ele = isEles ? eles[i] : eles[i].ele;
28116 var rs = ele._private.rscratch;
28117 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28118
28119 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28120 var layer = caches[l];
28121
28122 if (!layer) {
28123 continue;
28124 } // if update is a request from the ele cache, then it affects only
28125 // the matching level
28126
28127
28128 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28129 continue;
28130 }
28131
28132 update(layer, ele, req);
28133 }
28134 }
28135};
28136
28137LTCp.haveLayers = function () {
28138 var self = this;
28139 var haveLayers = false;
28140
28141 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28142 var layers = self.layersByLevel[l];
28143
28144 if (layers && layers.length > 0) {
28145 haveLayers = true;
28146 break;
28147 }
28148 }
28149
28150 return haveLayers;
28151};
28152
28153LTCp.invalidateElements = function (eles) {
28154 var self = this;
28155
28156 if (eles.length === 0) {
28157 return;
28158 }
28159
28160 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28161
28162 if (eles.length === 0 || !self.haveLayers()) {
28163 return;
28164 }
28165
28166 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28167 self.invalidateLayer(layer);
28168 });
28169};
28170
28171LTCp.invalidateLayer = function (layer) {
28172 // log('update invalidate layer time');
28173 this.lastInvalidationTime = performanceNow();
28174
28175 if (layer.invalid) {
28176 return;
28177 } // save cycles
28178
28179
28180 var lvl = layer.level;
28181 var eles = layer.eles;
28182 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28183
28184 removeFromArray(layers, layer); // layer.eles = [];
28185
28186 layer.elesQueue = [];
28187 layer.invalid = true;
28188
28189 if (layer.replacement) {
28190 layer.replacement.invalid = true;
28191 }
28192
28193 for (var i = 0; i < eles.length; i++) {
28194 var caches = eles[i]._private.rscratch.imgLayerCaches;
28195
28196 if (caches) {
28197 caches[lvl] = null;
28198 }
28199 }
28200};
28201
28202LTCp.refineElementTextures = function (eles) {
28203 var self = this; // log('refine', eles.length);
28204
28205 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28206 var rLyr = layer.replacement;
28207
28208 if (!rLyr) {
28209 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28210 rLyr.replaces = layer;
28211 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28212 }
28213
28214 if (!rLyr.reqs) {
28215 for (var i = 0; i < rLyr.eles.length; i++) {
28216 self.queueLayer(rLyr, rLyr.eles[i]);
28217 } // log('queue replacement layer refinement', rLyr.id);
28218
28219 }
28220 });
28221};
28222
28223LTCp.enqueueElementRefinement = function (ele) {
28224
28225 this.eleTxrDeqs.merge(ele);
28226 this.scheduleElementRefinement();
28227};
28228
28229LTCp.queueLayer = function (layer, ele) {
28230 var self = this;
28231 var q = self.layersQueue;
28232 var elesQ = layer.elesQueue;
28233 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28234
28235 if (layer.replacement) {
28236 return;
28237 }
28238
28239 if (ele) {
28240 if (hasId[ele.id()]) {
28241 return;
28242 }
28243
28244 elesQ.push(ele);
28245 hasId[ele.id()] = true;
28246 }
28247
28248 if (layer.reqs) {
28249 layer.reqs++;
28250 q.updateItem(layer);
28251 } else {
28252 layer.reqs = 1;
28253 q.push(layer);
28254 }
28255};
28256
28257LTCp.dequeue = function (pxRatio) {
28258 var self = this;
28259 var q = self.layersQueue;
28260 var deqd = [];
28261 var eleDeqs = 0;
28262
28263 while (eleDeqs < maxDeqSize$1) {
28264 if (q.size() === 0) {
28265 break;
28266 }
28267
28268 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28269
28270 if (layer.replacement) {
28271 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28272 q.pop();
28273 continue;
28274 } // if this is a replacement layer that has been superceded, then forget it
28275
28276
28277 if (layer.replaces && layer !== layer.replaces.replacement) {
28278 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28279 q.pop();
28280 continue;
28281 }
28282
28283 if (layer.invalid) {
28284 // log('replacement layer %s is invalid; dequeued', layer.id);
28285 q.pop();
28286 continue;
28287 }
28288
28289 var ele = layer.elesQueue.shift();
28290
28291 if (ele) {
28292 // log('dequeue layer %s', layer.id);
28293 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28294 eleDeqs++;
28295 }
28296
28297 if (deqd.length === 0) {
28298 // we need only one entry in deqd to queue redrawing etc
28299 deqd.push(true);
28300 } // if the layer has all its eles done, then remove from the queue
28301
28302
28303 if (layer.elesQueue.length === 0) {
28304 q.pop();
28305 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28306 // when a replacement layer is dequeued, it replaces the old layer in the level
28307
28308 if (layer.replaces) {
28309 self.applyLayerReplacement(layer);
28310 }
28311
28312 self.requestRedraw();
28313 }
28314 }
28315
28316 return deqd;
28317};
28318
28319LTCp.applyLayerReplacement = function (layer) {
28320 var self = this;
28321 var layersInLevel = self.layersByLevel[layer.level];
28322 var replaced = layer.replaces;
28323 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28324 // refs would be a mistake (i.e. overwriting the true active layer)
28325
28326 if (index < 0 || replaced.invalid) {
28327 // log('replacement layer would have no effect', layer.id);
28328 return;
28329 }
28330
28331 layersInLevel[index] = layer; // replace level ref
28332 // replace refs in eles
28333
28334 for (var i = 0; i < layer.eles.length; i++) {
28335 var _p = layer.eles[i]._private;
28336 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28337
28338 if (cache) {
28339 cache[layer.level] = layer;
28340 }
28341 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28342
28343
28344 self.requestRedraw();
28345};
28346
28347LTCp.requestRedraw = util(function () {
28348 var r = this.renderer;
28349 r.redrawHint('eles', true);
28350 r.redrawHint('drag', true);
28351 r.redraw();
28352}, 100);
28353LTCp.setupDequeueing = defs.setupDequeueing({
28354 deqRedrawThreshold: deqRedrawThreshold$1,
28355 deqCost: deqCost$1,
28356 deqAvgCost: deqAvgCost$1,
28357 deqNoDrawCost: deqNoDrawCost$1,
28358 deqFastCost: deqFastCost$1,
28359 deq: function deq(self, pxRatio) {
28360 return self.dequeue(pxRatio);
28361 },
28362 onDeqd: noop,
28363 shouldRedraw: trueify,
28364 priority: function priority(self) {
28365 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28366 }
28367});
28368
28369var CRp = {};
28370var impl;
28371
28372function polygon(context, points) {
28373 for (var i = 0; i < points.length; i++) {
28374 var pt = points[i];
28375 context.lineTo(pt.x, pt.y);
28376 }
28377}
28378
28379function triangleBackcurve(context, points, controlPoint) {
28380 var firstPt;
28381
28382 for (var i = 0; i < points.length; i++) {
28383 var pt = points[i];
28384
28385 if (i === 0) {
28386 firstPt = pt;
28387 }
28388
28389 context.lineTo(pt.x, pt.y);
28390 }
28391
28392 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28393}
28394
28395function triangleTee(context, trianglePoints, teePoints) {
28396 if (context.beginPath) {
28397 context.beginPath();
28398 }
28399
28400 var triPts = trianglePoints;
28401
28402 for (var i = 0; i < triPts.length; i++) {
28403 var pt = triPts[i];
28404 context.lineTo(pt.x, pt.y);
28405 }
28406
28407 var teePts = teePoints;
28408 var firstTeePt = teePoints[0];
28409 context.moveTo(firstTeePt.x, firstTeePt.y);
28410
28411 for (var i = 1; i < teePts.length; i++) {
28412 var pt = teePts[i];
28413 context.lineTo(pt.x, pt.y);
28414 }
28415
28416 if (context.closePath) {
28417 context.closePath();
28418 }
28419}
28420
28421function circleTriangle(context, trianglePoints, rx, ry, r) {
28422 if (context.beginPath) {
28423 context.beginPath();
28424 }
28425
28426 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28427 var triPts = trianglePoints;
28428 var firstTrPt = triPts[0];
28429 context.moveTo(firstTrPt.x, firstTrPt.y);
28430
28431 for (var i = 0; i < triPts.length; i++) {
28432 var pt = triPts[i];
28433 context.lineTo(pt.x, pt.y);
28434 }
28435
28436 if (context.closePath) {
28437 context.closePath();
28438 }
28439}
28440
28441function circle(context, rx, ry, r) {
28442 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28443}
28444
28445CRp.arrowShapeImpl = function (name) {
28446 return (impl || (impl = {
28447 'polygon': polygon,
28448 'triangle-backcurve': triangleBackcurve,
28449 'triangle-tee': triangleTee,
28450 'circle-triangle': circleTriangle,
28451 'triangle-cross': triangleTee,
28452 'circle': circle
28453 }))[name];
28454};
28455
28456var CRp$1 = {};
28457
28458CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28459 var r = this;
28460
28461 if (ele.isNode()) {
28462 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28463 } else {
28464 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28465 }
28466};
28467
28468CRp$1.drawElementOverlay = function (context, ele) {
28469 var r = this;
28470
28471 if (ele.isNode()) {
28472 r.drawNodeOverlay(context, ele);
28473 } else {
28474 r.drawEdgeOverlay(context, ele);
28475 }
28476};
28477
28478CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28479 var r = this;
28480 var bb = eleTxrCache.getBoundingBox(ele);
28481
28482 if (bb.w === 0 || bb.h === 0) {
28483 return;
28484 } // ignore zero size case
28485
28486
28487 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28488
28489 if (eleCache != null) {
28490 var opacity = getOpacity(r, ele);
28491
28492 if (opacity === 0) {
28493 return;
28494 }
28495
28496 var theta = getRotation(r, ele);
28497 var x1 = bb.x1,
28498 y1 = bb.y1,
28499 w = bb.w,
28500 h = bb.h;
28501 var x, y, sx, sy, smooth;
28502
28503 if (theta !== 0) {
28504 var rotPt = eleTxrCache.getRotationPoint(ele);
28505 sx = rotPt.x;
28506 sy = rotPt.y;
28507 context.translate(sx, sy);
28508 context.rotate(theta);
28509 smooth = r.getImgSmoothing(context);
28510
28511 if (!smooth) {
28512 r.setImgSmoothing(context, true);
28513 }
28514
28515 var off = eleTxrCache.getRotationOffset(ele);
28516 x = off.x;
28517 y = off.y;
28518 } else {
28519 x = x1;
28520 y = y1;
28521 }
28522
28523 var oldGlobalAlpha;
28524
28525 if (opacity !== 1) {
28526 oldGlobalAlpha = context.globalAlpha;
28527 context.globalAlpha = oldGlobalAlpha * opacity;
28528 }
28529
28530 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28531
28532 if (opacity !== 1) {
28533 context.globalAlpha = oldGlobalAlpha;
28534 }
28535
28536 if (theta !== 0) {
28537 context.rotate(-theta);
28538 context.translate(-sx, -sy);
28539
28540 if (!smooth) {
28541 r.setImgSmoothing(context, false);
28542 }
28543 }
28544 } else {
28545 eleTxrCache.drawElement(context, ele); // direct draw fallback
28546 }
28547};
28548
28549var getZeroRotation = function getZeroRotation() {
28550 return 0;
28551};
28552
28553var getLabelRotation = function getLabelRotation(r, ele) {
28554 return r.getTextAngle(ele, null);
28555};
28556
28557var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28558 return r.getTextAngle(ele, 'source');
28559};
28560
28561var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28562 return r.getTextAngle(ele, 'target');
28563};
28564
28565var getOpacity = function getOpacity(r, ele) {
28566 return ele.effectiveOpacity();
28567};
28568
28569var getTextOpacity = function getTextOpacity(e, ele) {
28570 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28571};
28572
28573CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28574 var r = this;
28575 var _r$data = r.data,
28576 eleTxrCache = _r$data.eleTxrCache,
28577 lblTxrCache = _r$data.lblTxrCache,
28578 slbTxrCache = _r$data.slbTxrCache,
28579 tlbTxrCache = _r$data.tlbTxrCache;
28580 var bb = ele.boundingBox();
28581 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28582
28583 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28584 return;
28585 }
28586
28587 if (!extent || boundingBoxesIntersect(bb, extent)) {
28588 var isEdge = ele.isEdge();
28589
28590 var badLine = ele.element()._private.rscratch.badLine;
28591
28592 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28593
28594 if (!isEdge || !badLine) {
28595 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28596 }
28597
28598 if (isEdge && !badLine) {
28599 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28600 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28601 }
28602
28603 r.drawElementOverlay(context, ele);
28604 }
28605};
28606
28607CRp$1.drawElements = function (context, eles) {
28608 var r = this;
28609
28610 for (var i = 0; i < eles.length; i++) {
28611 var ele = eles[i];
28612 r.drawElement(context, ele);
28613 }
28614};
28615
28616CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28617 var r = this;
28618
28619 for (var i = 0; i < eles.length; i++) {
28620 var ele = eles[i];
28621 r.drawCachedElement(context, ele, pxRatio, extent);
28622 }
28623};
28624
28625CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28626 var r = this;
28627
28628 for (var i = 0; i < eles.length; i++) {
28629 var ele = eles[i];
28630
28631 if (!ele.isNode()) {
28632 continue;
28633 }
28634
28635 r.drawCachedElement(context, ele, pxRatio, extent);
28636 }
28637};
28638
28639CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28640 var r = this;
28641 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28642
28643 if (layers) {
28644 for (var i = 0; i < layers.length; i++) {
28645 var layer = layers[i];
28646 var bb = layer.bb;
28647
28648 if (bb.w === 0 || bb.h === 0) {
28649 continue;
28650 }
28651
28652 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28653 }
28654 } else {
28655 // fall back on plain caching if no layers
28656 r.drawCachedElements(context, eles, pxRatio, extent);
28657 }
28658};
28659
28660/* global Path2D */
28661var CRp$2 = {};
28662
28663CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28664 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28665 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28666 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28667 var r = this;
28668 var rs = edge._private.rscratch;
28669
28670 if (shouldDrawOpacity && !edge.visible()) {
28671 return;
28672 } // if bezier ctrl pts can not be calculated, then die
28673
28674
28675 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28676 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28677 return;
28678 }
28679
28680 var bb;
28681
28682 if (shiftToOriginWithBb) {
28683 bb = shiftToOriginWithBb;
28684 context.translate(-bb.x1, -bb.y1);
28685 }
28686
28687 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28688 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28689 var lineStyle = edge.pstyle('line-style').value;
28690 var edgeWidth = edge.pstyle('width').pfValue;
28691 var lineCap = edge.pstyle('line-cap').value;
28692 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
28693
28694 var effectiveArrowOpacity = opacity * lineOpacity;
28695
28696 var drawLine = function drawLine() {
28697 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28698 context.lineWidth = edgeWidth;
28699 context.lineCap = lineCap;
28700 r.eleStrokeStyle(context, edge, strokeOpacity);
28701 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28702 context.lineCap = 'butt'; // reset for other drawing functions
28703 };
28704
28705 var drawOverlay = function drawOverlay() {
28706 if (!shouldDrawOverlay) {
28707 return;
28708 }
28709
28710 r.drawEdgeOverlay(context, edge);
28711 };
28712
28713 var drawArrows = function drawArrows() {
28714 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28715 r.drawArrowheads(context, edge, arrowOpacity);
28716 };
28717
28718 var drawText = function drawText() {
28719 r.drawElementText(context, edge, null, drawLabel);
28720 };
28721
28722 context.lineJoin = 'round';
28723 var ghost = edge.pstyle('ghost').value === 'yes';
28724
28725 if (ghost) {
28726 var gx = edge.pstyle('ghost-offset-x').pfValue;
28727 var gy = edge.pstyle('ghost-offset-y').pfValue;
28728 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28729 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28730 context.translate(gx, gy);
28731 drawLine(effectiveGhostOpacity);
28732 drawArrows(effectiveGhostOpacity);
28733 context.translate(-gx, -gy);
28734 }
28735
28736 drawLine();
28737 drawArrows();
28738 drawOverlay();
28739 drawText();
28740
28741 if (shiftToOriginWithBb) {
28742 context.translate(bb.x1, bb.y1);
28743 }
28744};
28745
28746CRp$2.drawEdgeOverlay = function (context, edge) {
28747 if (!edge.visible()) {
28748 return;
28749 }
28750
28751 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28752
28753 if (overlayOpacity === 0) {
28754 return;
28755 }
28756
28757 var r = this;
28758 var usePaths = r.usePaths();
28759 var rs = edge._private.rscratch;
28760 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28761 var overlayWidth = 2 * overlayPadding;
28762 var overlayColor = edge.pstyle('overlay-color').value;
28763 context.lineWidth = overlayWidth;
28764
28765 if (rs.edgeType === 'self' && !usePaths) {
28766 context.lineCap = 'butt';
28767 } else {
28768 context.lineCap = 'round';
28769 }
28770
28771 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28772 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28773};
28774
28775CRp$2.drawEdgePath = function (edge, context, pts, type) {
28776 var rs = edge._private.rscratch;
28777 var canvasCxt = context;
28778 var path;
28779 var pathCacheHit = false;
28780 var usePaths = this.usePaths();
28781 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28782 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28783
28784 if (usePaths) {
28785 var pathCacheKey = pts.join('$');
28786 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28787
28788 if (keyMatches) {
28789 path = context = rs.pathCache;
28790 pathCacheHit = true;
28791 } else {
28792 path = context = new Path2D();
28793 rs.pathCacheKey = pathCacheKey;
28794 rs.pathCache = path;
28795 }
28796 }
28797
28798 if (canvasCxt.setLineDash) {
28799 // for very outofdate browsers
28800 switch (type) {
28801 case 'dotted':
28802 canvasCxt.setLineDash([1, 1]);
28803 break;
28804
28805 case 'dashed':
28806 canvasCxt.setLineDash(lineDashPattern);
28807 canvasCxt.lineDashOffset = lineDashOffset;
28808 break;
28809
28810 case 'solid':
28811 canvasCxt.setLineDash([]);
28812 break;
28813 }
28814 }
28815
28816 if (!pathCacheHit && !rs.badLine) {
28817 if (context.beginPath) {
28818 context.beginPath();
28819 }
28820
28821 context.moveTo(pts[0], pts[1]);
28822
28823 switch (rs.edgeType) {
28824 case 'bezier':
28825 case 'self':
28826 case 'compound':
28827 case 'multibezier':
28828 for (var i = 2; i + 3 < pts.length; i += 4) {
28829 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28830 }
28831
28832 break;
28833
28834 case 'straight':
28835 case 'segments':
28836 case 'haystack':
28837 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28838 context.lineTo(pts[_i], pts[_i + 1]);
28839 }
28840
28841 break;
28842 }
28843 }
28844
28845 context = canvasCxt;
28846
28847 if (usePaths) {
28848 context.stroke(path);
28849 } else {
28850 context.stroke();
28851 } // reset any line dashes
28852
28853
28854 if (context.setLineDash) {
28855 // for very outofdate browsers
28856 context.setLineDash([]);
28857 }
28858};
28859
28860CRp$2.drawArrowheads = function (context, edge, opacity) {
28861 var rs = edge._private.rscratch;
28862 var isHaystack = rs.edgeType === 'haystack';
28863
28864 if (!isHaystack) {
28865 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28866 }
28867
28868 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28869 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28870
28871 if (!isHaystack) {
28872 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28873 }
28874};
28875
28876CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28877 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28878 return;
28879 }
28880
28881 var self = this;
28882 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28883
28884 if (arrowShape === 'none') {
28885 return;
28886 }
28887
28888 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28889 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28890 var edgeWidth = edge.pstyle('width').pfValue;
28891 var edgeOpacity = edge.pstyle('opacity').value;
28892
28893 if (opacity === undefined) {
28894 opacity = edgeOpacity;
28895 }
28896
28897 var gco = context.globalCompositeOperation;
28898
28899 if (opacity !== 1 || arrowFill === 'hollow') {
28900 // then extra clear is needed
28901 context.globalCompositeOperation = 'destination-out';
28902 self.colorFillStyle(context, 255, 255, 255, 1);
28903 self.colorStrokeStyle(context, 255, 255, 255, 1);
28904 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28905 context.globalCompositeOperation = gco;
28906 } // otherwise, the opaque arrow clears it for free :)
28907
28908
28909 var color = edge.pstyle(prefix + '-arrow-color').value;
28910 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28911 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28912 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28913};
28914
28915CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28916 var r = this;
28917 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28918 var pathCacheHit = false;
28919 var path;
28920 var canvasContext = context;
28921 var translation = {
28922 x: x,
28923 y: y
28924 };
28925 var scale = edge.pstyle('arrow-scale').value;
28926 var size = this.getArrowWidth(edgeWidth, scale);
28927 var shapeImpl = r.arrowShapes[shape];
28928
28929 if (usePaths) {
28930 var cache = r.arrowPathCache = r.arrowPathCache || [];
28931 var key = hashString(shape);
28932 var cachedPath = cache[key];
28933
28934 if (cachedPath != null) {
28935 path = context = cachedPath;
28936 pathCacheHit = true;
28937 } else {
28938 path = context = new Path2D();
28939 cache[key] = path;
28940 }
28941 }
28942
28943 if (!pathCacheHit) {
28944 if (context.beginPath) {
28945 context.beginPath();
28946 }
28947
28948 if (usePaths) {
28949 // store in the path cache with values easily manipulated later
28950 shapeImpl.draw(context, 1, 0, {
28951 x: 0,
28952 y: 0
28953 }, 1);
28954 } else {
28955 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28956 }
28957
28958 if (context.closePath) {
28959 context.closePath();
28960 }
28961 }
28962
28963 context = canvasContext;
28964
28965 if (usePaths) {
28966 // set transform to arrow position/orientation
28967 context.translate(x, y);
28968 context.rotate(angle);
28969 context.scale(size, size);
28970 }
28971
28972 if (fill === 'filled' || fill === 'both') {
28973 if (usePaths) {
28974 context.fill(path);
28975 } else {
28976 context.fill();
28977 }
28978 }
28979
28980 if (fill === 'hollow' || fill === 'both') {
28981 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28982 context.lineJoin = 'miter';
28983
28984 if (usePaths) {
28985 context.stroke(path);
28986 } else {
28987 context.stroke();
28988 }
28989 }
28990
28991 if (usePaths) {
28992 // reset transform by applying inverse
28993 context.scale(1 / size, 1 / size);
28994 context.rotate(-angle);
28995 context.translate(-x, -y);
28996 }
28997};
28998
28999var CRp$3 = {};
29000
29001CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29002 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29003 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29004 return;
29005 }
29006
29007 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29008};
29009
29010CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29011 var r = this;
29012 var pos = node.position();
29013 var nodeX = pos.x;
29014 var nodeY = pos.y;
29015 var styleObj = node.cy().style();
29016 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29017 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29018 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29019 var nodeW = node.width();
29020 var nodeH = node.height();
29021 var paddingX2 = node.padding() * 2;
29022 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29023 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29024 var rs = node._private.rscratch;
29025 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29026 var shouldClip = clip === 'node';
29027 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29028 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29029 var imgW = img.width || img.cachedW;
29030 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29031
29032 if (null == imgW || null == imgH) {
29033 document.body.appendChild(img); // eslint-disable-line no-undef
29034
29035 imgW = img.cachedW = img.width || img.offsetWidth;
29036 imgH = img.cachedH = img.height || img.offsetHeight;
29037 document.body.removeChild(img); // eslint-disable-line no-undef
29038 }
29039
29040 var w = imgW;
29041 var h = imgH;
29042
29043 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29044 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29045 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29046 } else {
29047 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29048 }
29049 }
29050
29051 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29052 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29053 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29054 } else {
29055 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29056 }
29057 }
29058
29059 if (w === 0 || h === 0) {
29060 return; // no point in drawing empty image (and chrome is broken in this case)
29061 }
29062
29063 if (fit === 'contain') {
29064 var scale = Math.min(nodeTW / w, nodeTH / h);
29065 w *= scale;
29066 h *= scale;
29067 } else if (fit === 'cover') {
29068 var scale = Math.max(nodeTW / w, nodeTH / h);
29069 w *= scale;
29070 h *= scale;
29071 }
29072
29073 var x = nodeX - nodeTW / 2; // left
29074
29075 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29076 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29077
29078 if (posXUnits === '%') {
29079 x += (nodeTW - w) * posXPfVal;
29080 } else {
29081 x += posXPfVal;
29082 }
29083
29084 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29085 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29086
29087 if (offXUnits === '%') {
29088 x += (nodeTW - w) * offXPfVal;
29089 } else {
29090 x += offXPfVal;
29091 }
29092
29093 var y = nodeY - nodeTH / 2; // top
29094
29095 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29096 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29097
29098 if (posYUnits === '%') {
29099 y += (nodeTH - h) * posYPfVal;
29100 } else {
29101 y += posYPfVal;
29102 }
29103
29104 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29105 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29106
29107 if (offYUnits === '%') {
29108 y += (nodeTH - h) * offYPfVal;
29109 } else {
29110 y += offYPfVal;
29111 }
29112
29113 if (rs.pathCache) {
29114 x -= nodeX;
29115 y -= nodeY;
29116 nodeX = 0;
29117 nodeY = 0;
29118 }
29119
29120 var gAlpha = context.globalAlpha;
29121 context.globalAlpha = imgOpacity;
29122 var smoothingEnabled = r.getImgSmoothing(context);
29123 var isSmoothingSwitched = false;
29124
29125 if (smooth === 'no' && smoothingEnabled) {
29126 r.setImgSmoothing(context, false);
29127 isSmoothingSwitched = true;
29128 } else if (smooth === 'yes' && !smoothingEnabled) {
29129 r.setImgSmoothing(context, true);
29130 isSmoothingSwitched = true;
29131 }
29132
29133 if (repeat === 'no-repeat') {
29134 if (shouldClip) {
29135 context.save();
29136
29137 if (rs.pathCache) {
29138 context.clip(rs.pathCache);
29139 } else {
29140 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29141 context.clip();
29142 }
29143 }
29144
29145 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29146
29147 if (shouldClip) {
29148 context.restore();
29149 }
29150 } else {
29151 var pattern = context.createPattern(img, repeat);
29152 context.fillStyle = pattern;
29153 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29154 context.translate(x, y);
29155 context.fill();
29156 context.translate(-x, -y);
29157 }
29158
29159 context.globalAlpha = gAlpha;
29160
29161 if (isSmoothingSwitched) {
29162 r.setImgSmoothing(context, smoothingEnabled);
29163 }
29164};
29165
29166var CRp$4 = {};
29167
29168CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29169 if (!scale) {
29170 var zoom = ele.cy().zoom();
29171 var pxRatio = this.getPixelRatio();
29172 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29173
29174 scale = Math.pow(2, lvl);
29175 }
29176
29177 var computedSize = ele.pstyle('font-size').pfValue * scale;
29178 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29179
29180 if (computedSize < minSize) {
29181 return false;
29182 }
29183
29184 return true;
29185};
29186
29187CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29188 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29189 var r = this;
29190
29191 if (force == null) {
29192 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29193 return;
29194 }
29195 } else if (force === false) {
29196 return;
29197 }
29198
29199 if (ele.isNode()) {
29200 var label = ele.pstyle('label');
29201
29202 if (!label || !label.value) {
29203 return;
29204 }
29205
29206 var justification = r.getLabelJustification(ele);
29207 context.textAlign = justification;
29208 context.textBaseline = 'bottom';
29209 } else {
29210 var badLine = ele.element()._private.rscratch.badLine;
29211
29212 var _label = ele.pstyle('label');
29213
29214 var srcLabel = ele.pstyle('source-label');
29215 var tgtLabel = ele.pstyle('target-label');
29216
29217 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29218 return;
29219 }
29220
29221 context.textAlign = 'center';
29222 context.textBaseline = 'bottom';
29223 }
29224
29225 var applyRotation = !shiftToOriginWithBb;
29226 var bb;
29227
29228 if (shiftToOriginWithBb) {
29229 bb = shiftToOriginWithBb;
29230 context.translate(-bb.x1, -bb.y1);
29231 }
29232
29233 if (prefix == null) {
29234 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29235
29236 if (ele.isEdge()) {
29237 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29238 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29239 }
29240 } else {
29241 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29242 }
29243
29244 if (shiftToOriginWithBb) {
29245 context.translate(bb.x1, bb.y1);
29246 }
29247};
29248
29249CRp$4.getFontCache = function (context) {
29250 var cache;
29251 this.fontCaches = this.fontCaches || [];
29252
29253 for (var i = 0; i < this.fontCaches.length; i++) {
29254 cache = this.fontCaches[i];
29255
29256 if (cache.context === context) {
29257 return cache;
29258 }
29259 }
29260
29261 cache = {
29262 context: context
29263 };
29264 this.fontCaches.push(cache);
29265 return cache;
29266}; // set up canvas context with font
29267// returns transformed text string
29268
29269
29270CRp$4.setupTextStyle = function (context, ele) {
29271 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29272 // Font style
29273 var labelStyle = ele.pstyle('font-style').strValue;
29274 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29275 var labelFamily = ele.pstyle('font-family').strValue;
29276 var labelWeight = ele.pstyle('font-weight').strValue;
29277 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29278 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29279 var color = ele.pstyle('color').value;
29280 var outlineColor = ele.pstyle('text-outline-color').value;
29281 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29282 context.lineJoin = 'round'; // so text outlines aren't jagged
29283
29284 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29285 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29286}; // TODO ensure re-used
29287
29288
29289function roundRect(ctx, x, y, width, height) {
29290 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29291 ctx.beginPath();
29292 ctx.moveTo(x + radius, y);
29293 ctx.lineTo(x + width - radius, y);
29294 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29295 ctx.lineTo(x + width, y + height - radius);
29296 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29297 ctx.lineTo(x + radius, y + height);
29298 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29299 ctx.lineTo(x, y + radius);
29300 ctx.quadraticCurveTo(x, y, x + radius, y);
29301 ctx.closePath();
29302 ctx.fill();
29303}
29304
29305CRp$4.getTextAngle = function (ele, prefix) {
29306 var theta;
29307 var _p = ele._private;
29308 var rscratch = _p.rscratch;
29309 var pdash = prefix ? prefix + '-' : '';
29310 var rotation = ele.pstyle(pdash + 'text-rotation');
29311 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29312
29313 if (rotation.strValue === 'autorotate') {
29314 theta = ele.isEdge() ? textAngle : 0;
29315 } else if (rotation.strValue === 'none') {
29316 theta = 0;
29317 } else {
29318 theta = rotation.pfValue;
29319 }
29320
29321 return theta;
29322};
29323
29324CRp$4.drawText = function (context, ele, prefix) {
29325 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29326 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29327 var _p = ele._private;
29328 var rscratch = _p.rscratch;
29329 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29330
29331 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29332 return;
29333 } // use 'main' as an alias for the main label (i.e. null prefix)
29334
29335
29336 if (prefix === 'main') {
29337 prefix = null;
29338 }
29339
29340 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29341 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29342 var orgTextX, orgTextY; // used for rotation
29343
29344 var text = this.getLabelText(ele, prefix);
29345
29346 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29347 this.setupTextStyle(context, ele, useEleOpacity);
29348 var pdash = prefix ? prefix + '-' : '';
29349 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29350 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29351 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29352 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29353 var isEdge = ele.isEdge();
29354 var halign = ele.pstyle('text-halign').value;
29355 var valign = ele.pstyle('text-valign').value;
29356
29357 if (isEdge) {
29358 halign = 'center';
29359 valign = 'center';
29360 }
29361
29362 textX += marginX;
29363 textY += marginY;
29364 var theta;
29365
29366 if (!applyRotation) {
29367 theta = 0;
29368 } else {
29369 theta = this.getTextAngle(ele, prefix);
29370 }
29371
29372 if (theta !== 0) {
29373 orgTextX = textX;
29374 orgTextY = textY;
29375 context.translate(orgTextX, orgTextY);
29376 context.rotate(theta);
29377 textX = 0;
29378 textY = 0;
29379 }
29380
29381 switch (valign) {
29382 case 'top':
29383 break;
29384
29385 case 'center':
29386 textY += textH / 2;
29387 break;
29388
29389 case 'bottom':
29390 textY += textH;
29391 break;
29392 }
29393
29394 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29395 var borderOpacity = ele.pstyle('text-border-opacity').value;
29396 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29397 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29398
29399 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29400 var bgX = textX - backgroundPadding;
29401
29402 switch (halign) {
29403 case 'left':
29404 bgX -= textW;
29405 break;
29406
29407 case 'center':
29408 bgX -= textW / 2;
29409 break;
29410 }
29411
29412 var bgY = textY - textH - backgroundPadding;
29413 var bgW = textW + 2 * backgroundPadding;
29414 var bgH = textH + 2 * backgroundPadding;
29415
29416 if (backgroundOpacity > 0) {
29417 var textFill = context.fillStyle;
29418 var textBackgroundColor = ele.pstyle('text-background-color').value;
29419 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29420 var styleShape = ele.pstyle('text-background-shape').strValue;
29421
29422 if (styleShape.indexOf('round') === 0) {
29423 roundRect(context, bgX, bgY, bgW, bgH, 2);
29424 } else {
29425 context.fillRect(bgX, bgY, bgW, bgH);
29426 }
29427
29428 context.fillStyle = textFill;
29429 }
29430
29431 if (textBorderWidth > 0 && borderOpacity > 0) {
29432 var textStroke = context.strokeStyle;
29433 var textLineWidth = context.lineWidth;
29434 var textBorderColor = ele.pstyle('text-border-color').value;
29435 var textBorderStyle = ele.pstyle('text-border-style').value;
29436 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29437 context.lineWidth = textBorderWidth;
29438
29439 if (context.setLineDash) {
29440 // for very outofdate browsers
29441 switch (textBorderStyle) {
29442 case 'dotted':
29443 context.setLineDash([1, 1]);
29444 break;
29445
29446 case 'dashed':
29447 context.setLineDash([4, 2]);
29448 break;
29449
29450 case 'double':
29451 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29452
29453 context.setLineDash([]);
29454 break;
29455
29456 case 'solid':
29457 context.setLineDash([]);
29458 break;
29459 }
29460 }
29461
29462 context.strokeRect(bgX, bgY, bgW, bgH);
29463
29464 if (textBorderStyle === 'double') {
29465 var whiteWidth = textBorderWidth / 2;
29466 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29467 }
29468
29469 if (context.setLineDash) {
29470 // for very outofdate browsers
29471 context.setLineDash([]);
29472 }
29473
29474 context.lineWidth = textLineWidth;
29475 context.strokeStyle = textStroke;
29476 }
29477 }
29478
29479 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29480
29481 if (lineWidth > 0) {
29482 context.lineWidth = lineWidth;
29483 }
29484
29485 if (ele.pstyle('text-wrap').value === 'wrap') {
29486 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29487 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29488 var halfTextW = textW / 2;
29489 var justification = this.getLabelJustification(ele);
29490
29491 if (justification === 'auto') ; else if (halign === 'left') {
29492 // auto justification : right
29493 if (justification === 'left') {
29494 textX += -textW;
29495 } else if (justification === 'center') {
29496 textX += -halfTextW;
29497 } // else same as auto
29498
29499 } else if (halign === 'center') {
29500 // auto justfication : center
29501 if (justification === 'left') {
29502 textX += -halfTextW;
29503 } else if (justification === 'right') {
29504 textX += halfTextW;
29505 } // else same as auto
29506
29507 } else if (halign === 'right') {
29508 // auto justification : left
29509 if (justification === 'center') {
29510 textX += halfTextW;
29511 } else if (justification === 'right') {
29512 textX += textW;
29513 } // else same as auto
29514
29515 }
29516
29517 switch (valign) {
29518 case 'top':
29519 textY -= (lines.length - 1) * lineHeight;
29520 break;
29521
29522 case 'center':
29523 case 'bottom':
29524 textY -= (lines.length - 1) * lineHeight;
29525 break;
29526 }
29527
29528 for (var l = 0; l < lines.length; l++) {
29529 if (lineWidth > 0) {
29530 context.strokeText(lines[l], textX, textY);
29531 }
29532
29533 context.fillText(lines[l], textX, textY);
29534 textY += lineHeight;
29535 }
29536 } else {
29537 if (lineWidth > 0) {
29538 context.strokeText(text, textX, textY);
29539 }
29540
29541 context.fillText(text, textX, textY);
29542 }
29543
29544 if (theta !== 0) {
29545 context.rotate(-theta);
29546 context.translate(-orgTextX, -orgTextY);
29547 }
29548 }
29549};
29550
29551/* global Path2D */
29552var CRp$5 = {};
29553
29554CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29555 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29556 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29557 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29558 var r = this;
29559 var nodeWidth, nodeHeight;
29560 var _p = node._private;
29561 var rs = _p.rscratch;
29562 var pos = node.position();
29563
29564 if (!number(pos.x) || !number(pos.y)) {
29565 return; // can't draw node with undefined position
29566 }
29567
29568 if (shouldDrawOpacity && !node.visible()) {
29569 return;
29570 }
29571
29572 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29573 var usePaths = r.usePaths();
29574 var path;
29575 var pathCacheHit = false;
29576 var padding = node.padding();
29577 nodeWidth = node.width() + 2 * padding;
29578 nodeHeight = node.height() + 2 * padding; //
29579 // setup shift
29580
29581 var bb;
29582
29583 if (shiftToOriginWithBb) {
29584 bb = shiftToOriginWithBb;
29585 context.translate(-bb.x1, -bb.y1);
29586 } //
29587 // load bg image
29588
29589
29590 var bgImgProp = node.pstyle('background-image');
29591 var urls = bgImgProp.value;
29592 var urlDefined = new Array(urls.length);
29593 var image = new Array(urls.length);
29594 var numImages = 0;
29595
29596 for (var i = 0; i < urls.length; i++) {
29597 var url = urls[i];
29598 var defd = urlDefined[i] = url != null && url !== 'none';
29599
29600 if (defd) {
29601 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29602 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29603
29604 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29605 _p.backgroundTimestamp = Date.now();
29606 node.emitAndNotify('background');
29607 });
29608 }
29609 } //
29610 // setup styles
29611
29612
29613 var darkness = node.pstyle('background-blacken').value;
29614 var borderWidth = node.pstyle('border-width').pfValue;
29615 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29616 var borderColor = node.pstyle('border-color').value;
29617 var borderStyle = node.pstyle('border-style').value;
29618 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29619 context.lineJoin = 'miter'; // so borders are square with the node shape
29620
29621 var setupShapeColor = function setupShapeColor() {
29622 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29623 r.eleFillStyle(context, node, bgOpy);
29624 };
29625
29626 var setupBorderColor = function setupBorderColor() {
29627 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29628 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29629 }; //
29630 // setup shape
29631
29632
29633 var styleShape = node.pstyle('shape').strValue;
29634 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29635
29636 if (usePaths) {
29637 context.translate(pos.x, pos.y);
29638 var pathCache = r.nodePathCache = r.nodePathCache || [];
29639 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29640 var cachedPath = pathCache[key];
29641
29642 if (cachedPath != null) {
29643 path = cachedPath;
29644 pathCacheHit = true;
29645 rs.pathCache = path;
29646 } else {
29647 path = new Path2D();
29648 pathCache[key] = rs.pathCache = path;
29649 }
29650 }
29651
29652 var drawShape = function drawShape() {
29653 if (!pathCacheHit) {
29654 var npos = pos;
29655
29656 if (usePaths) {
29657 npos = {
29658 x: 0,
29659 y: 0
29660 };
29661 }
29662
29663 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29664 }
29665
29666 if (usePaths) {
29667 context.fill(path);
29668 } else {
29669 context.fill();
29670 }
29671 };
29672
29673 var drawImages = function drawImages() {
29674 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29675 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29676 var prevBging = _p.backgrounding;
29677 var totalCompleted = 0;
29678
29679 for (var _i = 0; _i < image.length; _i++) {
29680 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29681
29682 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29683 totalCompleted++;
29684 continue;
29685 }
29686
29687 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29688 totalCompleted++;
29689 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29690 }
29691 }
29692
29693 _p.backgrounding = !(totalCompleted === numImages);
29694
29695 if (prevBging !== _p.backgrounding) {
29696 // update style b/c :backgrounding state changed
29697 node.updateStyle(false);
29698 }
29699 };
29700
29701 var drawPie = function drawPie() {
29702 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29703 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29704
29705 if (r.hasPie(node)) {
29706 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29707
29708 if (redrawShape) {
29709 if (!usePaths) {
29710 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29711 }
29712 }
29713 }
29714 };
29715
29716 var darken = function darken() {
29717 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29718 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29719 var c = darkness > 0 ? 0 : 255;
29720
29721 if (darkness !== 0) {
29722 r.colorFillStyle(context, c, c, c, opacity);
29723
29724 if (usePaths) {
29725 context.fill(path);
29726 } else {
29727 context.fill();
29728 }
29729 }
29730 };
29731
29732 var drawBorder = function drawBorder() {
29733 if (borderWidth > 0) {
29734 context.lineWidth = borderWidth;
29735 context.lineCap = 'butt';
29736
29737 if (context.setLineDash) {
29738 // for very outofdate browsers
29739 switch (borderStyle) {
29740 case 'dotted':
29741 context.setLineDash([1, 1]);
29742 break;
29743
29744 case 'dashed':
29745 context.setLineDash([4, 2]);
29746 break;
29747
29748 case 'solid':
29749 case 'double':
29750 context.setLineDash([]);
29751 break;
29752 }
29753 }
29754
29755 if (usePaths) {
29756 context.stroke(path);
29757 } else {
29758 context.stroke();
29759 }
29760
29761 if (borderStyle === 'double') {
29762 context.lineWidth = borderWidth / 3;
29763 var gco = context.globalCompositeOperation;
29764 context.globalCompositeOperation = 'destination-out';
29765
29766 if (usePaths) {
29767 context.stroke(path);
29768 } else {
29769 context.stroke();
29770 }
29771
29772 context.globalCompositeOperation = gco;
29773 } // reset in case we changed the border style
29774
29775
29776 if (context.setLineDash) {
29777 // for very outofdate browsers
29778 context.setLineDash([]);
29779 }
29780 }
29781 };
29782
29783 var drawOverlay = function drawOverlay() {
29784 if (shouldDrawOverlay) {
29785 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29786 }
29787 };
29788
29789 var drawText = function drawText() {
29790 r.drawElementText(context, node, null, drawLabel);
29791 };
29792
29793 var ghost = node.pstyle('ghost').value === 'yes';
29794
29795 if (ghost) {
29796 var gx = node.pstyle('ghost-offset-x').pfValue;
29797 var gy = node.pstyle('ghost-offset-y').pfValue;
29798 var ghostOpacity = node.pstyle('ghost-opacity').value;
29799 var effGhostOpacity = ghostOpacity * eleOpacity;
29800 context.translate(gx, gy);
29801 setupShapeColor(ghostOpacity * bgOpacity);
29802 drawShape();
29803 drawImages(effGhostOpacity, true);
29804 setupBorderColor(ghostOpacity * borderOpacity);
29805 drawBorder();
29806 drawPie(darkness !== 0 || borderWidth !== 0);
29807 drawImages(effGhostOpacity, false);
29808 darken(effGhostOpacity);
29809 context.translate(-gx, -gy);
29810 }
29811
29812 setupShapeColor();
29813 drawShape();
29814 drawImages(eleOpacity, true);
29815 setupBorderColor();
29816 drawBorder();
29817 drawPie(darkness !== 0 || borderWidth !== 0);
29818 drawImages(eleOpacity, false);
29819 darken();
29820
29821 if (usePaths) {
29822 context.translate(-pos.x, -pos.y);
29823 }
29824
29825 drawText();
29826 drawOverlay(); //
29827 // clean up shift
29828
29829 if (shiftToOriginWithBb) {
29830 context.translate(bb.x1, bb.y1);
29831 }
29832};
29833
29834CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29835 var r = this;
29836
29837 if (!node.visible()) {
29838 return;
29839 }
29840
29841 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29842 var overlayOpacity = node.pstyle('overlay-opacity').value;
29843 var overlayColor = node.pstyle('overlay-color').value;
29844
29845 if (overlayOpacity > 0) {
29846 pos = pos || node.position();
29847
29848 if (nodeWidth == null || nodeHeight == null) {
29849 var padding = node.padding();
29850 nodeWidth = node.width() + 2 * padding;
29851 nodeHeight = node.height() + 2 * padding;
29852 }
29853
29854 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29855 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29856 context.fill();
29857 }
29858}; // does the node have at least one pie piece?
29859
29860
29861CRp$5.hasPie = function (node) {
29862 node = node[0]; // ensure ele ref
29863
29864 return node._private.hasPie;
29865};
29866
29867CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29868 node = node[0]; // ensure ele ref
29869
29870 pos = pos || node.position();
29871 var cyStyle = node.cy().style();
29872 var pieSize = node.pstyle('pie-size');
29873 var x = pos.x;
29874 var y = pos.y;
29875 var nodeW = node.width();
29876 var nodeH = node.height();
29877 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29878
29879 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29880
29881 var usePaths = this.usePaths();
29882
29883 if (usePaths) {
29884 x = 0;
29885 y = 0;
29886 }
29887
29888 if (pieSize.units === '%') {
29889 radius = radius * pieSize.pfValue;
29890 } else if (pieSize.pfValue !== undefined) {
29891 radius = pieSize.pfValue / 2;
29892 }
29893
29894 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29895 // 1..N
29896 var size = node.pstyle('pie-' + i + '-background-size').value;
29897 var color = node.pstyle('pie-' + i + '-background-color').value;
29898 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29899 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29900 // percent can't push beyond 1
29901
29902 if (percent + lastPercent > 1) {
29903 percent = 1 - lastPercent;
29904 }
29905
29906 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29907
29908 var angleDelta = 2 * Math.PI * percent;
29909 var angleEnd = angleStart + angleDelta; // ignore if
29910 // - zero size
29911 // - we're already beyond the full circle
29912 // - adding the current slice would go beyond the full circle
29913
29914 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29915 continue;
29916 }
29917
29918 context.beginPath();
29919 context.moveTo(x, y);
29920 context.arc(x, y, radius, angleStart, angleEnd);
29921 context.closePath();
29922 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29923 context.fill();
29924 lastPercent += percent;
29925 }
29926};
29927
29928var CRp$6 = {};
29929var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29930
29931CRp$6.getPixelRatio = function () {
29932 var context = this.data.contexts[0];
29933
29934 if (this.forcedPixelRatio != null) {
29935 return this.forcedPixelRatio;
29936 }
29937
29938 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29939 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29940};
29941
29942CRp$6.paintCache = function (context) {
29943 var caches = this.paintCaches = this.paintCaches || [];
29944 var needToCreateCache = true;
29945 var cache;
29946
29947 for (var i = 0; i < caches.length; i++) {
29948 cache = caches[i];
29949
29950 if (cache.context === context) {
29951 needToCreateCache = false;
29952 break;
29953 }
29954 }
29955
29956 if (needToCreateCache) {
29957 cache = {
29958 context: context
29959 };
29960 caches.push(cache);
29961 }
29962
29963 return cache;
29964};
29965
29966CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29967 var gradientStyle;
29968 var usePaths = this.usePaths();
29969 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29970 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29971
29972 if (fill === 'radial-gradient') {
29973 if (ele.isEdge()) {
29974 var start = ele.sourceEndpoint(),
29975 end = ele.targetEndpoint(),
29976 mid = ele.midpoint();
29977 var d1 = dist(start, mid);
29978 var d2 = dist(end, mid);
29979 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29980 } else {
29981 var pos = usePaths ? {
29982 x: 0,
29983 y: 0
29984 } : ele.position(),
29985 width = ele.paddedWidth(),
29986 height = ele.paddedHeight();
29987 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29988 }
29989 } else {
29990 if (ele.isEdge()) {
29991 var _start = ele.sourceEndpoint(),
29992 _end = ele.targetEndpoint();
29993
29994 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29995 } else {
29996 var _pos = usePaths ? {
29997 x: 0,
29998 y: 0
29999 } : ele.position(),
30000 _width = ele.paddedWidth(),
30001 _height = ele.paddedHeight(),
30002 halfWidth = _width / 2,
30003 halfHeight = _height / 2;
30004
30005 var direction = ele.pstyle('background-gradient-direction').value;
30006
30007 switch (direction) {
30008 case 'to-bottom':
30009 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30010 break;
30011
30012 case 'to-top':
30013 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30014 break;
30015
30016 case 'to-left':
30017 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30018 break;
30019
30020 case 'to-right':
30021 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30022 break;
30023
30024 case 'to-bottom-right':
30025 case 'to-right-bottom':
30026 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30027 break;
30028
30029 case 'to-top-right':
30030 case 'to-right-top':
30031 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30032 break;
30033
30034 case 'to-bottom-left':
30035 case 'to-left-bottom':
30036 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30037 break;
30038
30039 case 'to-top-left':
30040 case 'to-left-top':
30041 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30042 break;
30043 }
30044 }
30045 }
30046
30047 if (!gradientStyle) return null; // invalid gradient style
30048
30049 var hasPositions = positions.length === colors.length;
30050 var length = colors.length;
30051
30052 for (var i = 0; i < length; i++) {
30053 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30054 }
30055
30056 return gradientStyle;
30057};
30058
30059CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30060 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30061 if (!gradientStyle) return null; // error
30062
30063 context.fillStyle = gradientStyle;
30064};
30065
30066CRp$6.colorFillStyle = function (context, r, g, b, a) {
30067 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30068 // var cache = this.paintCache(context);
30069 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30070 // if( cache.fillStyle !== fillStyle ){
30071 // context.fillStyle = cache.fillStyle = fillStyle;
30072 // }
30073};
30074
30075CRp$6.eleFillStyle = function (context, ele, opacity) {
30076 var backgroundFill = ele.pstyle('background-fill').value;
30077
30078 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30079 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30080 } else {
30081 var backgroundColor = ele.pstyle('background-color').value;
30082 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30083 }
30084};
30085
30086CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30087 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30088 if (!gradientStyle) return null; // error
30089
30090 context.strokeStyle = gradientStyle;
30091};
30092
30093CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30094 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30095 // var cache = this.paintCache(context);
30096 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30097 // if( cache.strokeStyle !== strokeStyle ){
30098 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30099 // }
30100};
30101
30102CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30103 var lineFill = ele.pstyle('line-fill').value;
30104
30105 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30106 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30107 } else {
30108 var lineColor = ele.pstyle('line-color').value;
30109 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30110 }
30111}; // Resize canvas
30112
30113
30114CRp$6.matchCanvasSize = function (container) {
30115 var r = this;
30116 var data = r.data;
30117 var bb = r.findContainerClientCoords();
30118 var width = bb[2];
30119 var height = bb[3];
30120 var pixelRatio = r.getPixelRatio();
30121 var mbPxRatio = r.motionBlurPxRatio;
30122
30123 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30124 pixelRatio = mbPxRatio;
30125 }
30126
30127 var canvasWidth = width * pixelRatio;
30128 var canvasHeight = height * pixelRatio;
30129 var canvas;
30130
30131 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30132 return; // save cycles if same
30133 }
30134
30135 r.fontCaches = null; // resizing resets the style
30136
30137 var canvasContainer = data.canvasContainer;
30138 canvasContainer.style.width = width + 'px';
30139 canvasContainer.style.height = height + 'px';
30140
30141 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30142 canvas = data.canvases[i];
30143 canvas.width = canvasWidth;
30144 canvas.height = canvasHeight;
30145 canvas.style.width = width + 'px';
30146 canvas.style.height = height + 'px';
30147 }
30148
30149 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30150 canvas = data.bufferCanvases[i];
30151 canvas.width = canvasWidth;
30152 canvas.height = canvasHeight;
30153 canvas.style.width = width + 'px';
30154 canvas.style.height = height + 'px';
30155 }
30156
30157 r.textureMult = 1;
30158
30159 if (pixelRatio <= 1) {
30160 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30161 r.textureMult = 2;
30162 canvas.width = canvasWidth * r.textureMult;
30163 canvas.height = canvasHeight * r.textureMult;
30164 }
30165
30166 r.canvasWidth = canvasWidth;
30167 r.canvasHeight = canvasHeight;
30168};
30169
30170CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30171 this.render({
30172 forcedContext: cxt,
30173 forcedZoom: zoom,
30174 forcedPan: pan,
30175 drawAllLayers: true,
30176 forcedPxRatio: pxRatio
30177 });
30178};
30179
30180CRp$6.render = function (options) {
30181 options = options || staticEmptyObject();
30182 var forcedContext = options.forcedContext;
30183 var drawAllLayers = options.drawAllLayers;
30184 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30185 var forcedZoom = options.forcedZoom;
30186 var forcedPan = options.forcedPan;
30187 var r = this;
30188 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30189 var cy = r.cy;
30190 var data = r.data;
30191 var needDraw = data.canvasNeedsRedraw;
30192 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30193 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30194 var mbPxRatio = r.motionBlurPxRatio;
30195 var hasCompoundNodes = cy.hasCompoundNodes();
30196 var inNodeDragGesture = r.hoverData.draggingEles;
30197 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30198 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30199 var motionBlurFadeEffect = motionBlur;
30200
30201 if (!forcedContext) {
30202 if (r.prevPxRatio !== pixelRatio) {
30203 r.invalidateContainerClientCoordsCache();
30204 r.matchCanvasSize(r.container);
30205 r.redrawHint('eles', true);
30206 r.redrawHint('drag', true);
30207 }
30208
30209 r.prevPxRatio = pixelRatio;
30210 }
30211
30212 if (!forcedContext && r.motionBlurTimeout) {
30213 clearTimeout(r.motionBlurTimeout);
30214 }
30215
30216 if (motionBlur) {
30217 if (r.mbFrames == null) {
30218 r.mbFrames = 0;
30219 }
30220
30221 r.mbFrames++;
30222
30223 if (r.mbFrames < 3) {
30224 // need several frames before even high quality motionblur
30225 motionBlurFadeEffect = false;
30226 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30227
30228
30229 if (r.mbFrames > r.minMbLowQualFrames) {
30230 //r.fullQualityMb = false;
30231 r.motionBlurPxRatio = r.mbPxRBlurry;
30232 }
30233 }
30234
30235 if (r.clearingMotionBlur) {
30236 r.motionBlurPxRatio = 1;
30237 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30238 // because a rogue async texture frame would clear needDraw
30239
30240
30241 if (r.textureDrawLastFrame && !textureDraw) {
30242 needDraw[r.NODE] = true;
30243 needDraw[r.SELECT_BOX] = true;
30244 }
30245
30246 var style = cy.style();
30247 var zoom = cy.zoom();
30248 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30249 var pan = cy.pan();
30250 var effectivePan = {
30251 x: pan.x,
30252 y: pan.y
30253 };
30254 var vp = {
30255 zoom: zoom,
30256 pan: {
30257 x: pan.x,
30258 y: pan.y
30259 }
30260 };
30261 var prevVp = r.prevViewport;
30262 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)
30263
30264 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30265 r.motionBlurPxRatio = 1;
30266 }
30267
30268 if (forcedPan) {
30269 effectivePan = forcedPan;
30270 } // apply pixel ratio
30271
30272
30273 effectiveZoom *= pixelRatio;
30274 effectivePan.x *= pixelRatio;
30275 effectivePan.y *= pixelRatio;
30276 var eles = r.getCachedZSortedEles();
30277
30278 function mbclear(context, x, y, w, h) {
30279 var gco = context.globalCompositeOperation;
30280 context.globalCompositeOperation = 'destination-out';
30281 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30282 context.fillRect(x, y, w, h);
30283 context.globalCompositeOperation = gco;
30284 }
30285
30286 function setContextTransform(context, clear) {
30287 var ePan, eZoom, w, h;
30288
30289 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30290 ePan = {
30291 x: pan.x * mbPxRatio,
30292 y: pan.y * mbPxRatio
30293 };
30294 eZoom = zoom * mbPxRatio;
30295 w = r.canvasWidth * mbPxRatio;
30296 h = r.canvasHeight * mbPxRatio;
30297 } else {
30298 ePan = effectivePan;
30299 eZoom = effectiveZoom;
30300 w = r.canvasWidth;
30301 h = r.canvasHeight;
30302 }
30303
30304 context.setTransform(1, 0, 0, 1, 0, 0);
30305
30306 if (clear === 'motionBlur') {
30307 mbclear(context, 0, 0, w, h);
30308 } else if (!forcedContext && (clear === undefined || clear)) {
30309 context.clearRect(0, 0, w, h);
30310 }
30311
30312 if (!drawAllLayers) {
30313 context.translate(ePan.x, ePan.y);
30314 context.scale(eZoom, eZoom);
30315 }
30316
30317 if (forcedPan) {
30318 context.translate(forcedPan.x, forcedPan.y);
30319 }
30320
30321 if (forcedZoom) {
30322 context.scale(forcedZoom, forcedZoom);
30323 }
30324 }
30325
30326 if (!textureDraw) {
30327 r.textureDrawLastFrame = false;
30328 }
30329
30330 if (textureDraw) {
30331 r.textureDrawLastFrame = true;
30332
30333 if (!r.textureCache) {
30334 r.textureCache = {};
30335 r.textureCache.bb = cy.mutableElements().boundingBox();
30336 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30337 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30338 cxt.setTransform(1, 0, 0, 1, 0, 0);
30339 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30340 r.render({
30341 forcedContext: cxt,
30342 drawOnlyNodeLayer: true,
30343 forcedPxRatio: pixelRatio * r.textureMult
30344 });
30345 var vp = r.textureCache.viewport = {
30346 zoom: cy.zoom(),
30347 pan: cy.pan(),
30348 width: r.canvasWidth,
30349 height: r.canvasHeight
30350 };
30351 vp.mpan = {
30352 x: (0 - vp.pan.x) / vp.zoom,
30353 y: (0 - vp.pan.y) / vp.zoom
30354 };
30355 }
30356
30357 needDraw[r.DRAG] = false;
30358 needDraw[r.NODE] = false;
30359 var context = data.contexts[r.NODE];
30360 var texture = r.textureCache.texture;
30361 var vp = r.textureCache.viewport;
30362 context.setTransform(1, 0, 0, 1, 0, 0);
30363
30364 if (motionBlur) {
30365 mbclear(context, 0, 0, vp.width, vp.height);
30366 } else {
30367 context.clearRect(0, 0, vp.width, vp.height);
30368 }
30369
30370 var outsideBgColor = style.core('outside-texture-bg-color').value;
30371 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30372 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30373 context.fillRect(0, 0, vp.width, vp.height);
30374 var zoom = cy.zoom();
30375 setContextTransform(context, false);
30376 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30377 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30378 } else if (r.textureOnViewport && !forcedContext) {
30379 // clear the cache since we don't need it
30380 r.textureCache = null;
30381 }
30382
30383 var extent = cy.extent();
30384 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30385 var hideEdges = r.hideEdgesOnViewport && vpManip;
30386 var needMbClear = [];
30387 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30388
30389 if (needMbClear[r.NODE]) {
30390 r.clearedForMotionBlur[r.NODE] = true;
30391 }
30392
30393 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30394
30395 if (needMbClear[r.DRAG]) {
30396 r.clearedForMotionBlur[r.DRAG] = true;
30397 }
30398
30399 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30400 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30401 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30402 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30403 setContextTransform(context, clear);
30404
30405 if (hideEdges) {
30406 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30407 } else {
30408 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30409 }
30410
30411 if (r.debug) {
30412 r.drawDebugPoints(context, eles.nondrag);
30413 }
30414
30415 if (!drawAllLayers && !motionBlur) {
30416 needDraw[r.NODE] = false;
30417 }
30418 }
30419
30420 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30421 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30422 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30423 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30424
30425 if (hideEdges) {
30426 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30427 } else {
30428 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30429 }
30430
30431 if (r.debug) {
30432 r.drawDebugPoints(context, eles.drag);
30433 }
30434
30435 if (!drawAllLayers && !motionBlur) {
30436 needDraw[r.DRAG] = false;
30437 }
30438 }
30439
30440 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30441 var context = forcedContext || data.contexts[r.SELECT_BOX];
30442 setContextTransform(context);
30443
30444 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30445 var zoom = r.cy.zoom();
30446 var borderWidth = style.core('selection-box-border-width').value / zoom;
30447 context.lineWidth = borderWidth;
30448 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 + ')';
30449 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30450
30451 if (borderWidth > 0) {
30452 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 + ')';
30453 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30454 }
30455 }
30456
30457 if (data.bgActivePosistion && !r.hoverData.selecting) {
30458 var zoom = r.cy.zoom();
30459 var pos = data.bgActivePosistion;
30460 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 + ')';
30461 context.beginPath();
30462 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30463 context.fill();
30464 }
30465
30466 var timeToRender = r.lastRedrawTime;
30467
30468 if (r.showFps && timeToRender) {
30469 timeToRender = Math.round(timeToRender);
30470 var fps = Math.round(1000 / timeToRender);
30471 context.setTransform(1, 0, 0, 1, 0, 0);
30472 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30473 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30474 context.lineWidth = 1;
30475 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30476 var maxFps = 60;
30477 context.strokeRect(0, 30, 250, 20);
30478 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30479 }
30480
30481 if (!drawAllLayers) {
30482 needDraw[r.SELECT_BOX] = false;
30483 }
30484 } // motionblur: blit rendered blurry frames
30485
30486
30487 if (motionBlur && mbPxRatio !== 1) {
30488 var cxtNode = data.contexts[r.NODE];
30489 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30490 var cxtDrag = data.contexts[r.DRAG];
30491 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30492
30493 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30494 cxt.setTransform(1, 0, 0, 1, 0, 0);
30495
30496 if (needClear || !motionBlurFadeEffect) {
30497 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30498 } else {
30499 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30500 }
30501
30502 var pxr = mbPxRatio;
30503 cxt.drawImage(txt, // img
30504 0, 0, // sx, sy
30505 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30506 0, 0, // x, y
30507 r.canvasWidth, r.canvasHeight // w, h
30508 );
30509 };
30510
30511 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30512 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30513 needDraw[r.NODE] = false;
30514 }
30515
30516 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30517 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30518 needDraw[r.DRAG] = false;
30519 }
30520 }
30521
30522 r.prevViewport = vp;
30523
30524 if (r.clearingMotionBlur) {
30525 r.clearingMotionBlur = false;
30526 r.motionBlurCleared = true;
30527 r.motionBlur = true;
30528 }
30529
30530 if (motionBlur) {
30531 r.motionBlurTimeout = setTimeout(function () {
30532 r.motionBlurTimeout = null;
30533 r.clearedForMotionBlur[r.NODE] = false;
30534 r.clearedForMotionBlur[r.DRAG] = false;
30535 r.motionBlur = false;
30536 r.clearingMotionBlur = !textureDraw;
30537 r.mbFrames = 0;
30538 needDraw[r.NODE] = true;
30539 needDraw[r.DRAG] = true;
30540 r.redraw();
30541 }, motionBlurDelay);
30542 }
30543
30544 if (!forcedContext) {
30545 cy.emit('render');
30546 }
30547};
30548
30549var CRp$7 = {}; // @O Polygon drawing
30550
30551CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30552 var halfW = width / 2;
30553 var halfH = height / 2;
30554
30555 if (context.beginPath) {
30556 context.beginPath();
30557 }
30558
30559 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30560
30561 for (var i = 1; i < points.length / 2; i++) {
30562 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30563 }
30564
30565 context.closePath();
30566};
30567
30568CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30569 var halfW = width / 2;
30570 var halfH = height / 2;
30571 var cornerRadius = getRoundPolygonRadius(width, height);
30572
30573 if (context.beginPath) {
30574 context.beginPath();
30575 }
30576
30577 for (var _i = 0; _i < points.length / 4; _i++) {
30578 var sourceUv = void 0,
30579 destUv = void 0;
30580
30581 if (_i === 0) {
30582 sourceUv = points.length - 2;
30583 } else {
30584 sourceUv = _i * 4 - 2;
30585 }
30586
30587 destUv = _i * 4 + 2;
30588 var px = x + halfW * points[_i * 4];
30589 var py = y + halfH * points[_i * 4 + 1];
30590 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30591 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30592 var cp0x = px - offset * points[sourceUv];
30593 var cp0y = py - offset * points[sourceUv + 1];
30594 var cp1x = px + offset * points[destUv];
30595 var cp1y = py + offset * points[destUv + 1];
30596
30597 if (_i === 0) {
30598 context.moveTo(cp0x, cp0y);
30599 } else {
30600 context.lineTo(cp0x, cp0y);
30601 }
30602
30603 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30604 }
30605
30606 context.closePath();
30607}; // Round rectangle drawing
30608
30609
30610CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30611 var halfWidth = width / 2;
30612 var halfHeight = height / 2;
30613 var cornerRadius = getRoundRectangleRadius(width, height);
30614
30615 if (context.beginPath) {
30616 context.beginPath();
30617 } // Start at top middle
30618
30619
30620 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30621
30622 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30623
30624 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30625
30626 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30627
30628 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30629
30630 context.lineTo(x, y - halfHeight);
30631 context.closePath();
30632};
30633
30634CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30635 var halfWidth = width / 2;
30636 var halfHeight = height / 2;
30637 var cornerRadius = getRoundRectangleRadius(width, height);
30638
30639 if (context.beginPath) {
30640 context.beginPath();
30641 } // Start at top middle
30642
30643
30644 context.moveTo(x, y - halfHeight);
30645 context.lineTo(x + halfWidth, y - halfHeight);
30646 context.lineTo(x + halfWidth, y);
30647 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30648 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30649 context.lineTo(x - halfWidth, y - halfHeight);
30650 context.lineTo(x, y - halfHeight);
30651 context.closePath();
30652};
30653
30654CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30655 var halfWidth = width / 2;
30656 var halfHeight = height / 2;
30657 var cornerLength = getCutRectangleCornerLength();
30658
30659 if (context.beginPath) {
30660 context.beginPath();
30661 }
30662
30663 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30664 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30665 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30666 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30667 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30668 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30669 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30670 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30671 context.closePath();
30672};
30673
30674CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30675 var halfWidth = width / 2;
30676 var halfHeight = height / 2;
30677 var xBegin = x - halfWidth;
30678 var xEnd = x + halfWidth;
30679 var yBegin = y - halfHeight;
30680 var yEnd = y + halfHeight;
30681 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30682 var wOffset = barrelCurveConstants.widthOffset;
30683 var hOffset = barrelCurveConstants.heightOffset;
30684 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30685
30686 if (context.beginPath) {
30687 context.beginPath();
30688 }
30689
30690 context.moveTo(xBegin, yBegin + hOffset);
30691 context.lineTo(xBegin, yEnd - hOffset);
30692 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30693 context.lineTo(xEnd - wOffset, yEnd);
30694 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30695 context.lineTo(xEnd, yBegin + hOffset);
30696 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30697 context.lineTo(xBegin + wOffset, yBegin);
30698 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30699 context.closePath();
30700};
30701
30702var sin0 = Math.sin(0);
30703var cos0 = Math.cos(0);
30704var sin = {};
30705var cos = {};
30706var ellipseStepSize = Math.PI / 40;
30707
30708for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30709 sin[i] = Math.sin(i);
30710 cos[i] = Math.cos(i);
30711}
30712
30713CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30714 if (context.beginPath) {
30715 context.beginPath();
30716 }
30717
30718 if (context.ellipse) {
30719 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30720 } else {
30721 var xPos, yPos;
30722 var rw = width / 2;
30723 var rh = height / 2;
30724
30725 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30726 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30727 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30728
30729 if (i === 0) {
30730 context.moveTo(xPos, yPos);
30731 } else {
30732 context.lineTo(xPos, yPos);
30733 }
30734 }
30735 }
30736
30737 context.closePath();
30738};
30739
30740/* global atob, ArrayBuffer, Uint8Array, Blob */
30741var CRp$8 = {};
30742
30743CRp$8.createBuffer = function (w, h) {
30744 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30745
30746 buffer.width = w;
30747 buffer.height = h;
30748 return [buffer, buffer.getContext('2d')];
30749};
30750
30751CRp$8.bufferCanvasImage = function (options) {
30752 var cy = this.cy;
30753 var eles = cy.mutableElements();
30754 var bb = eles.boundingBox();
30755 var ctrRect = this.findContainerClientCoords();
30756 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30757 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30758 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30759 var pxRatio = this.getPixelRatio();
30760 var scale = 1;
30761
30762 if (options.scale !== undefined) {
30763 width *= options.scale;
30764 height *= options.scale;
30765 scale = options.scale;
30766 } else if (specdMaxDims) {
30767 var maxScaleW = Infinity;
30768 var maxScaleH = Infinity;
30769
30770 if (number(options.maxWidth)) {
30771 maxScaleW = scale * options.maxWidth / width;
30772 }
30773
30774 if (number(options.maxHeight)) {
30775 maxScaleH = scale * options.maxHeight / height;
30776 }
30777
30778 scale = Math.min(maxScaleW, maxScaleH);
30779 width *= scale;
30780 height *= scale;
30781 }
30782
30783 if (!specdMaxDims) {
30784 width *= pxRatio;
30785 height *= pxRatio;
30786 scale *= pxRatio;
30787 }
30788
30789 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30790
30791 buffCanvas.width = width;
30792 buffCanvas.height = height;
30793 buffCanvas.style.width = width + 'px';
30794 buffCanvas.style.height = height + 'px';
30795 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30796
30797 if (width > 0 && height > 0) {
30798 buffCxt.clearRect(0, 0, width, height);
30799 buffCxt.globalCompositeOperation = 'source-over';
30800 var zsortedEles = this.getCachedZSortedEles();
30801
30802 if (options.full) {
30803 // draw the full bounds of the graph
30804 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30805 buffCxt.scale(scale, scale);
30806 this.drawElements(buffCxt, zsortedEles);
30807 buffCxt.scale(1 / scale, 1 / scale);
30808 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30809 } else {
30810 // draw the current view
30811 var pan = cy.pan();
30812 var translation = {
30813 x: pan.x * scale,
30814 y: pan.y * scale
30815 };
30816 scale *= cy.zoom();
30817 buffCxt.translate(translation.x, translation.y);
30818 buffCxt.scale(scale, scale);
30819 this.drawElements(buffCxt, zsortedEles);
30820 buffCxt.scale(1 / scale, 1 / scale);
30821 buffCxt.translate(-translation.x, -translation.y);
30822 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30823
30824
30825 if (options.bg) {
30826 buffCxt.globalCompositeOperation = 'destination-over';
30827 buffCxt.fillStyle = options.bg;
30828 buffCxt.rect(0, 0, width, height);
30829 buffCxt.fill();
30830 }
30831 }
30832
30833 return buffCanvas;
30834};
30835
30836function b64ToBlob(b64, mimeType) {
30837 var bytes = atob(b64);
30838 var buff = new ArrayBuffer(bytes.length);
30839 var buffUint8 = new Uint8Array(buff);
30840
30841 for (var i = 0; i < bytes.length; i++) {
30842 buffUint8[i] = bytes.charCodeAt(i);
30843 }
30844
30845 return new Blob([buff], {
30846 type: mimeType
30847 });
30848}
30849
30850function b64UriToB64(b64uri) {
30851 var i = b64uri.indexOf(',');
30852 return b64uri.substr(i + 1);
30853}
30854
30855function output(options, canvas, mimeType) {
30856 var getB64Uri = function getB64Uri() {
30857 return canvas.toDataURL(mimeType, options.quality);
30858 };
30859
30860 switch (options.output) {
30861 case 'blob-promise':
30862 return new Promise$1(function (resolve, reject) {
30863 try {
30864 canvas.toBlob(function (blob) {
30865 if (blob != null) {
30866 resolve(blob);
30867 } else {
30868 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30869 }
30870 }, mimeType, options.quality);
30871 } catch (err) {
30872 reject(err);
30873 }
30874 });
30875
30876 case 'blob':
30877 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30878
30879 case 'base64':
30880 return b64UriToB64(getB64Uri());
30881
30882 case 'base64uri':
30883 default:
30884 return getB64Uri();
30885 }
30886}
30887
30888CRp$8.png = function (options) {
30889 return output(options, this.bufferCanvasImage(options), 'image/png');
30890};
30891
30892CRp$8.jpg = function (options) {
30893 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30894};
30895
30896var CRp$9 = {};
30897
30898CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30899 switch (name) {
30900 case 'ellipse':
30901 return this.drawEllipsePath(context, centerX, centerY, width, height);
30902
30903 case 'polygon':
30904 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30905
30906 case 'round-polygon':
30907 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30908
30909 case 'roundrectangle':
30910 case 'round-rectangle':
30911 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30912
30913 case 'cutrectangle':
30914 case 'cut-rectangle':
30915 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30916
30917 case 'bottomroundrectangle':
30918 case 'bottom-round-rectangle':
30919 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30920
30921 case 'barrel':
30922 return this.drawBarrelPath(context, centerX, centerY, width, height);
30923 }
30924};
30925
30926var CR = CanvasRenderer;
30927var CRp$a = CanvasRenderer.prototype;
30928CRp$a.CANVAS_LAYERS = 3; //
30929
30930CRp$a.SELECT_BOX = 0;
30931CRp$a.DRAG = 1;
30932CRp$a.NODE = 2;
30933CRp$a.BUFFER_COUNT = 3; //
30934
30935CRp$a.TEXTURE_BUFFER = 0;
30936CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30937CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30938
30939function CanvasRenderer(options) {
30940 var r = this;
30941 r.data = {
30942 canvases: new Array(CRp$a.CANVAS_LAYERS),
30943 contexts: new Array(CRp$a.CANVAS_LAYERS),
30944 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30945 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30946 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30947 };
30948 var tapHlOffAttr = '-webkit-tap-highlight-color';
30949 var tapHlOffStyle = 'rgba(0,0,0,0)';
30950 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30951
30952 var containerStyle = r.data.canvasContainer.style;
30953 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30954 containerStyle.position = 'relative';
30955 containerStyle.zIndex = '0';
30956 containerStyle.overflow = 'hidden';
30957 var container = options.cy.container();
30958 container.appendChild(r.data.canvasContainer);
30959 container.style[tapHlOffAttr] = tapHlOffStyle;
30960 var styleMap = {
30961 '-webkit-user-select': 'none',
30962 '-moz-user-select': '-moz-none',
30963 'user-select': 'none',
30964 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30965 'outline-style': 'none'
30966 };
30967
30968 if (ms()) {
30969 styleMap['-ms-touch-action'] = 'none';
30970 styleMap['touch-action'] = 'none';
30971 }
30972
30973 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30974 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30975
30976 r.data.contexts[i] = canvas.getContext('2d');
30977 Object.keys(styleMap).forEach(function (k) {
30978 canvas.style[k] = styleMap[k];
30979 });
30980 canvas.style.position = 'absolute';
30981 canvas.setAttribute('data-id', 'layer' + i);
30982 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30983 r.data.canvasContainer.appendChild(canvas);
30984 r.data.canvasNeedsRedraw[i] = false;
30985 }
30986
30987 r.data.topCanvas = r.data.canvases[0];
30988 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30989 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30990 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30991
30992 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30993 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30994
30995 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30996 r.data.bufferCanvases[i].style.position = 'absolute';
30997 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30998 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30999 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31000 }
31001
31002 r.pathsEnabled = true;
31003 var emptyBb = makeBoundingBox();
31004
31005 var getBoxCenter = function getBoxCenter(bb) {
31006 return {
31007 x: (bb.x1 + bb.x2) / 2,
31008 y: (bb.y1 + bb.y2) / 2
31009 };
31010 };
31011
31012 var getCenterOffset = function getCenterOffset(bb) {
31013 return {
31014 x: -bb.w / 2,
31015 y: -bb.h / 2
31016 };
31017 };
31018
31019 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31020 var _p = ele[0]._private;
31021 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31022 return !same;
31023 };
31024
31025 var getStyleKey = function getStyleKey(ele) {
31026 return ele[0]._private.nodeKey;
31027 };
31028
31029 var getLabelKey = function getLabelKey(ele) {
31030 return ele[0]._private.labelStyleKey;
31031 };
31032
31033 var getSourceLabelKey = function getSourceLabelKey(ele) {
31034 return ele[0]._private.sourceLabelStyleKey;
31035 };
31036
31037 var getTargetLabelKey = function getTargetLabelKey(ele) {
31038 return ele[0]._private.targetLabelStyleKey;
31039 };
31040
31041 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31042 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31043 };
31044
31045 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31046 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31047 };
31048
31049 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31050 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31051 };
31052
31053 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31054 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31055 };
31056
31057 var getElementBox = function getElementBox(ele) {
31058 ele.boundingBox();
31059 return ele[0]._private.bodyBounds;
31060 };
31061
31062 var getLabelBox = function getLabelBox(ele) {
31063 ele.boundingBox();
31064 return ele[0]._private.labelBounds.main || emptyBb;
31065 };
31066
31067 var getSourceLabelBox = function getSourceLabelBox(ele) {
31068 ele.boundingBox();
31069 return ele[0]._private.labelBounds.source || emptyBb;
31070 };
31071
31072 var getTargetLabelBox = function getTargetLabelBox(ele) {
31073 ele.boundingBox();
31074 return ele[0]._private.labelBounds.target || emptyBb;
31075 };
31076
31077 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31078 return scaledLabelShown;
31079 };
31080
31081 var getElementRotationPoint = function getElementRotationPoint(ele) {
31082 return getBoxCenter(getElementBox(ele));
31083 };
31084
31085 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31086 var pre = prefix ? prefix + '-' : '';
31087 return {
31088 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31089 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31090 };
31091 };
31092
31093 var getRsPt = function getRsPt(ele, x, y) {
31094 var rs = ele[0]._private.rscratch;
31095 return {
31096 x: rs[x],
31097 y: rs[y]
31098 };
31099 };
31100
31101 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31102 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31103 };
31104
31105 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31106 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31107 };
31108
31109 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31110 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31111 };
31112
31113 var getElementRotationOffset = function getElementRotationOffset(ele) {
31114 return getCenterOffset(getElementBox(ele));
31115 };
31116
31117 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31118 return getCenterOffset(getSourceLabelBox(ele));
31119 };
31120
31121 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31122 return getCenterOffset(getTargetLabelBox(ele));
31123 };
31124
31125 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31126 var bb = getLabelBox(ele);
31127 var p = getCenterOffset(getLabelBox(ele));
31128
31129 if (ele.isNode()) {
31130 switch (ele.pstyle('text-halign').value) {
31131 case 'left':
31132 p.x = -bb.w;
31133 break;
31134
31135 case 'right':
31136 p.x = 0;
31137 break;
31138 }
31139
31140 switch (ele.pstyle('text-valign').value) {
31141 case 'top':
31142 p.y = -bb.h;
31143 break;
31144
31145 case 'bottom':
31146 p.y = 0;
31147 break;
31148 }
31149 }
31150
31151 return p;
31152 };
31153
31154 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31155 getKey: getStyleKey,
31156 doesEleInvalidateKey: backgroundTimestampHasChanged,
31157 drawElement: drawElement,
31158 getBoundingBox: getElementBox,
31159 getRotationPoint: getElementRotationPoint,
31160 getRotationOffset: getElementRotationOffset,
31161 allowEdgeTxrCaching: false,
31162 allowParentTxrCaching: false
31163 });
31164 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31165 getKey: getLabelKey,
31166 drawElement: drawLabel,
31167 getBoundingBox: getLabelBox,
31168 getRotationPoint: getLabelRotationPoint,
31169 getRotationOffset: getLabelRotationOffset,
31170 isVisible: isLabelVisibleAtScale
31171 });
31172 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31173 getKey: getSourceLabelKey,
31174 drawElement: drawSourceLabel,
31175 getBoundingBox: getSourceLabelBox,
31176 getRotationPoint: getSourceLabelRotationPoint,
31177 getRotationOffset: getSourceLabelRotationOffset,
31178 isVisible: isLabelVisibleAtScale
31179 });
31180 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31181 getKey: getTargetLabelKey,
31182 drawElement: drawTargetLabel,
31183 getBoundingBox: getTargetLabelBox,
31184 getRotationPoint: getTargetLabelRotationPoint,
31185 getRotationOffset: getTargetLabelRotationOffset,
31186 isVisible: isLabelVisibleAtScale
31187 });
31188 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31189 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31190 // each cache should check for sub-key diff to see that the update affects that cache particularly
31191 eleTxrCache.invalidateElements(eles);
31192 lblTxrCache.invalidateElements(eles);
31193 slbTxrCache.invalidateElements(eles);
31194 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31195
31196 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31197
31198 for (var _i = 0; _i < eles.length; _i++) {
31199 var _p = eles[_i]._private;
31200 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31201 }
31202 });
31203
31204 var refineInLayers = function refineInLayers(reqs) {
31205 for (var i = 0; i < reqs.length; i++) {
31206 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31207 }
31208 };
31209
31210 eleTxrCache.onDequeue(refineInLayers);
31211 lblTxrCache.onDequeue(refineInLayers);
31212 slbTxrCache.onDequeue(refineInLayers);
31213 tlbTxrCache.onDequeue(refineInLayers);
31214}
31215
31216CRp$a.redrawHint = function (group, bool) {
31217 var r = this;
31218
31219 switch (group) {
31220 case 'eles':
31221 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31222 break;
31223
31224 case 'drag':
31225 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31226 break;
31227
31228 case 'select':
31229 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31230 break;
31231 }
31232}; // whether to use Path2D caching for drawing
31233
31234
31235var pathsImpld = typeof Path2D !== 'undefined';
31236
31237CRp$a.path2dEnabled = function (on) {
31238 if (on === undefined) {
31239 return this.pathsEnabled;
31240 }
31241
31242 this.pathsEnabled = on ? true : false;
31243};
31244
31245CRp$a.usePaths = function () {
31246 return pathsImpld && this.pathsEnabled;
31247};
31248
31249CRp$a.setImgSmoothing = function (context, bool) {
31250 if (context.imageSmoothingEnabled != null) {
31251 context.imageSmoothingEnabled = bool;
31252 } else {
31253 context.webkitImageSmoothingEnabled = bool;
31254 context.mozImageSmoothingEnabled = bool;
31255 context.msImageSmoothingEnabled = bool;
31256 }
31257};
31258
31259CRp$a.getImgSmoothing = function (context) {
31260 if (context.imageSmoothingEnabled != null) {
31261 return context.imageSmoothingEnabled;
31262 } else {
31263 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31264 }
31265};
31266
31267CRp$a.makeOffscreenCanvas = function (width, height) {
31268 var canvas;
31269
31270 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31271 canvas = new OffscreenCanvas(width, height);
31272 } else {
31273 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31274
31275 canvas.width = width;
31276 canvas.height = height;
31277 }
31278
31279 return canvas;
31280};
31281
31282[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31283 extend(CRp$a, props);
31284});
31285
31286var renderer = [{
31287 name: 'null',
31288 impl: NullRenderer
31289}, {
31290 name: 'base',
31291 impl: BR
31292}, {
31293 name: 'canvas',
31294 impl: CR
31295}];
31296
31297var incExts = [{
31298 type: 'layout',
31299 extensions: layout
31300}, {
31301 type: 'renderer',
31302 extensions: renderer
31303}];
31304
31305var extensions = {}; // registered modules for extensions, indexed by name
31306
31307var modules = {};
31308
31309function setExtension(type, name, registrant) {
31310 var ext = registrant;
31311
31312 var overrideErr = function overrideErr(field) {
31313 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31314 };
31315
31316 if (type === 'core') {
31317 if (Core.prototype[name]) {
31318 return overrideErr(name);
31319 } else {
31320 Core.prototype[name] = registrant;
31321 }
31322 } else if (type === 'collection') {
31323 if (Collection.prototype[name]) {
31324 return overrideErr(name);
31325 } else {
31326 Collection.prototype[name] = registrant;
31327 }
31328 } else if (type === 'layout') {
31329 // fill in missing layout functions in the prototype
31330 var Layout = function Layout(options) {
31331 this.options = options;
31332 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31333
31334 if (!plainObject(this._private)) {
31335 this._private = {};
31336 }
31337
31338 this._private.cy = options.cy;
31339 this._private.listeners = [];
31340 this.createEmitter();
31341 };
31342
31343 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31344 var optLayoutFns = [];
31345
31346 for (var i = 0; i < optLayoutFns.length; i++) {
31347 var fnName = optLayoutFns[i];
31348
31349 layoutProto[fnName] = layoutProto[fnName] || function () {
31350 return this;
31351 };
31352 } // either .start() or .run() is defined, so autogen the other
31353
31354
31355 if (layoutProto.start && !layoutProto.run) {
31356 layoutProto.run = function () {
31357 this.start();
31358 return this;
31359 };
31360 } else if (!layoutProto.start && layoutProto.run) {
31361 layoutProto.start = function () {
31362 this.run();
31363 return this;
31364 };
31365 }
31366
31367 var regStop = registrant.prototype.stop;
31368
31369 layoutProto.stop = function () {
31370 var opts = this.options;
31371
31372 if (opts && opts.animate) {
31373 var anis = this.animations;
31374
31375 if (anis) {
31376 for (var _i = 0; _i < anis.length; _i++) {
31377 anis[_i].stop();
31378 }
31379 }
31380 }
31381
31382 if (regStop) {
31383 regStop.call(this);
31384 } else {
31385 this.emit('layoutstop');
31386 }
31387
31388 return this;
31389 };
31390
31391 if (!layoutProto.destroy) {
31392 layoutProto.destroy = function () {
31393 return this;
31394 };
31395 }
31396
31397 layoutProto.cy = function () {
31398 return this._private.cy;
31399 };
31400
31401 var getCy = function getCy(layout) {
31402 return layout._private.cy;
31403 };
31404
31405 var emitterOpts = {
31406 addEventFields: function addEventFields(layout, evt) {
31407 evt.layout = layout;
31408 evt.cy = getCy(layout);
31409 evt.target = layout;
31410 },
31411 bubble: function bubble() {
31412 return true;
31413 },
31414 parent: function parent(layout) {
31415 return getCy(layout);
31416 }
31417 };
31418 extend(layoutProto, {
31419 createEmitter: function createEmitter() {
31420 this._private.emitter = new Emitter(emitterOpts, this);
31421 return this;
31422 },
31423 emitter: function emitter() {
31424 return this._private.emitter;
31425 },
31426 on: function on(evt, cb) {
31427 this.emitter().on(evt, cb);
31428 return this;
31429 },
31430 one: function one(evt, cb) {
31431 this.emitter().one(evt, cb);
31432 return this;
31433 },
31434 once: function once(evt, cb) {
31435 this.emitter().one(evt, cb);
31436 return this;
31437 },
31438 removeListener: function removeListener(evt, cb) {
31439 this.emitter().removeListener(evt, cb);
31440 return this;
31441 },
31442 removeAllListeners: function removeAllListeners() {
31443 this.emitter().removeAllListeners();
31444 return this;
31445 },
31446 emit: function emit(evt, params) {
31447 this.emitter().emit(evt, params);
31448 return this;
31449 }
31450 });
31451 define$3.eventAliasesOn(layoutProto);
31452 ext = Layout; // replace with our wrapped layout
31453 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31454 // user registered renderers inherit from base
31455 var BaseRenderer = getExtension('renderer', 'base');
31456 var bProto = BaseRenderer.prototype;
31457 var RegistrantRenderer = registrant;
31458 var rProto = registrant.prototype;
31459
31460 var Renderer = function Renderer() {
31461 BaseRenderer.apply(this, arguments);
31462 RegistrantRenderer.apply(this, arguments);
31463 };
31464
31465 var proto = Renderer.prototype;
31466
31467 for (var pName in bProto) {
31468 var pVal = bProto[pName];
31469 var existsInR = rProto[pName] != null;
31470
31471 if (existsInR) {
31472 return overrideErr(pName);
31473 }
31474
31475 proto[pName] = pVal; // take impl from base
31476 }
31477
31478 for (var _pName in rProto) {
31479 proto[_pName] = rProto[_pName]; // take impl from registrant
31480 }
31481
31482 bProto.clientFunctions.forEach(function (name) {
31483 proto[name] = proto[name] || function () {
31484 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31485 };
31486 });
31487 ext = Renderer;
31488 }
31489
31490 return setMap({
31491 map: extensions,
31492 keys: [type, name],
31493 value: ext
31494 });
31495}
31496
31497function getExtension(type, name) {
31498 return getMap({
31499 map: extensions,
31500 keys: [type, name]
31501 });
31502}
31503
31504function setModule(type, name, moduleType, moduleName, registrant) {
31505 return setMap({
31506 map: modules,
31507 keys: [type, name, moduleType, moduleName],
31508 value: registrant
31509 });
31510}
31511
31512function getModule(type, name, moduleType, moduleName) {
31513 return getMap({
31514 map: modules,
31515 keys: [type, name, moduleType, moduleName]
31516 });
31517}
31518
31519var extension = function extension() {
31520 // e.g. extension('renderer', 'svg')
31521 if (arguments.length === 2) {
31522 return getExtension.apply(null, arguments);
31523 } // e.g. extension('renderer', 'svg', { ... })
31524 else if (arguments.length === 3) {
31525 return setExtension.apply(null, arguments);
31526 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31527 else if (arguments.length === 4) {
31528 return getModule.apply(null, arguments);
31529 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31530 else if (arguments.length === 5) {
31531 return setModule.apply(null, arguments);
31532 } else {
31533 error('Invalid extension access syntax');
31534 }
31535}; // allows a core instance to access extensions internally
31536
31537
31538Core.prototype.extension = extension; // included extensions
31539
31540incExts.forEach(function (group) {
31541 group.extensions.forEach(function (ext) {
31542 setExtension(group.type, ext.name, ext.impl);
31543 });
31544});
31545
31546// (useful for init)
31547
31548var Stylesheet = function Stylesheet() {
31549 if (!(this instanceof Stylesheet)) {
31550 return new Stylesheet();
31551 }
31552
31553 this.length = 0;
31554};
31555
31556var sheetfn = Stylesheet.prototype;
31557
31558sheetfn.instanceString = function () {
31559 return 'stylesheet';
31560}; // just store the selector to be parsed later
31561
31562
31563sheetfn.selector = function (selector) {
31564 var i = this.length++;
31565 this[i] = {
31566 selector: selector,
31567 properties: []
31568 };
31569 return this; // chaining
31570}; // just store the property to be parsed later
31571
31572
31573sheetfn.css = function (name, value) {
31574 var i = this.length - 1;
31575
31576 if (string(name)) {
31577 this[i].properties.push({
31578 name: name,
31579 value: value
31580 });
31581 } else if (plainObject(name)) {
31582 var map = name;
31583 var propNames = Object.keys(map);
31584
31585 for (var j = 0; j < propNames.length; j++) {
31586 var key = propNames[j];
31587 var mapVal = map[key];
31588
31589 if (mapVal == null) {
31590 continue;
31591 }
31592
31593 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31594
31595 if (prop == null) {
31596 continue;
31597 }
31598
31599 var _name = prop.name;
31600 var _value = mapVal;
31601 this[i].properties.push({
31602 name: _name,
31603 value: _value
31604 });
31605 }
31606 }
31607
31608 return this; // chaining
31609};
31610
31611sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31612
31613sheetfn.generateStyle = function (cy) {
31614 var style = new Style(cy);
31615 return this.appendToStyle(style);
31616}; // append a dummy stylesheet object on a real style object
31617
31618
31619sheetfn.appendToStyle = function (style) {
31620 for (var i = 0; i < this.length; i++) {
31621 var context = this[i];
31622 var selector = context.selector;
31623 var props = context.properties;
31624 style.selector(selector); // apply selector
31625
31626 for (var j = 0; j < props.length; j++) {
31627 var prop = props[j];
31628 style.css(prop.name, prop.value); // apply property
31629 }
31630 }
31631
31632 return style;
31633};
31634
31635var version = "3.18.2";
31636
31637var cytoscape = function cytoscape(options) {
31638 // if no options specified, use default
31639 if (options === undefined) {
31640 options = {};
31641 } // create instance
31642
31643
31644 if (plainObject(options)) {
31645 return new Core(options);
31646 } // allow for registration of extensions
31647 else if (string(options)) {
31648 return extension.apply(extension, arguments);
31649 }
31650}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31651
31652
31653cytoscape.use = function (ext) {
31654 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31655
31656 args.unshift(cytoscape); // cytoscape is first arg to ext
31657
31658 ext.apply(null, args);
31659 return this;
31660};
31661
31662cytoscape.warnings = function (bool) {
31663 return warnings(bool);
31664}; // replaced by build system
31665
31666
31667cytoscape.version = version; // expose public apis (mostly for extensions)
31668
31669cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31670
31671module.exports = cytoscape;