UNPKG

872 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2022, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23import util from 'lodash.debounce';
24import Heap from 'heap';
25import get from 'lodash.get';
26import set from 'lodash.set';
27import toPath from 'lodash.topath';
28
29function _typeof(obj) {
30 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
31 _typeof = function (obj) {
32 return typeof obj;
33 };
34 } else {
35 _typeof = function (obj) {
36 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
37 };
38 }
39
40 return _typeof(obj);
41}
42
43function _classCallCheck(instance, Constructor) {
44 if (!(instance instanceof Constructor)) {
45 throw new TypeError("Cannot call a class as a function");
46 }
47}
48
49function _defineProperties(target, props) {
50 for (var i = 0; i < props.length; i++) {
51 var descriptor = props[i];
52 descriptor.enumerable = descriptor.enumerable || false;
53 descriptor.configurable = true;
54 if ("value" in descriptor) descriptor.writable = true;
55 Object.defineProperty(target, descriptor.key, descriptor);
56 }
57}
58
59function _createClass(Constructor, protoProps, staticProps) {
60 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
61 if (staticProps) _defineProperties(Constructor, staticProps);
62 return Constructor;
63}
64
65function _defineProperty(obj, key, value) {
66 if (key in obj) {
67 Object.defineProperty(obj, key, {
68 value: value,
69 enumerable: true,
70 configurable: true,
71 writable: true
72 });
73 } else {
74 obj[key] = value;
75 }
76
77 return obj;
78}
79
80function _slicedToArray(arr, i) {
81 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
82}
83
84function _arrayWithHoles(arr) {
85 if (Array.isArray(arr)) return arr;
86}
87
88function _iterableToArrayLimit(arr, i) {
89 var _arr = [];
90 var _n = true;
91 var _d = false;
92 var _e = undefined;
93
94 try {
95 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
96 _arr.push(_s.value);
97
98 if (i && _arr.length === i) break;
99 }
100 } catch (err) {
101 _d = true;
102 _e = err;
103 } finally {
104 try {
105 if (!_n && _i["return"] != null) _i["return"]();
106 } finally {
107 if (_d) throw _e;
108 }
109 }
110
111 return _arr;
112}
113
114function _nonIterableRest() {
115 throw new TypeError("Invalid attempt to destructure non-iterable instance");
116}
117
118var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
119
120var navigator = window$1 ? window$1.navigator : null;
121var document$1 = window$1 ? window$1.document : null;
122
123var typeofstr = _typeof('');
124
125var typeofobj = _typeof({});
126
127var typeoffn = _typeof(function () {});
128
129var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
130
131var instanceStr = function instanceStr(obj) {
132 return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
133};
134
135var string = function string(obj) {
136 return obj != null && _typeof(obj) == typeofstr;
137};
138var fn = function fn(obj) {
139 return obj != null && _typeof(obj) === typeoffn;
140};
141var array = function array(obj) {
142 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
143};
144var plainObject = function plainObject(obj) {
145 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
146};
147var object = function object(obj) {
148 return obj != null && _typeof(obj) === typeofobj;
149};
150var number = function number(obj) {
151 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
152};
153var integer = function integer(obj) {
154 return number(obj) && Math.floor(obj) === obj;
155};
156var htmlElement = function htmlElement(obj) {
157 if ('undefined' === typeofhtmlele) {
158 return undefined;
159 } else {
160 return null != obj && obj instanceof HTMLElement;
161 }
162};
163var elementOrCollection = function elementOrCollection(obj) {
164 return element(obj) || collection(obj);
165};
166var element = function element(obj) {
167 return instanceStr(obj) === 'collection' && obj._private.single;
168};
169var collection = function collection(obj) {
170 return instanceStr(obj) === 'collection' && !obj._private.single;
171};
172var core = function core(obj) {
173 return instanceStr(obj) === 'core';
174};
175var stylesheet = function stylesheet(obj) {
176 return instanceStr(obj) === 'stylesheet';
177};
178var event = function event(obj) {
179 return instanceStr(obj) === 'event';
180};
181var emptyString = function emptyString(obj) {
182 if (obj === undefined || obj === null) {
183 // null is empty
184 return true;
185 } else if (obj === '' || obj.match(/^\s+$/)) {
186 return true; // empty string is empty
187 }
188
189 return false; // otherwise, we don't know what we've got
190};
191var domElement = function domElement(obj) {
192 if (typeof HTMLElement === 'undefined') {
193 return false; // we're not in a browser so it doesn't matter
194 } else {
195 return obj instanceof HTMLElement;
196 }
197};
198var boundingBox = function boundingBox(obj) {
199 return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
200};
201var promise = function promise(obj) {
202 return object(obj) && fn(obj.then);
203};
204var ms = function ms() {
205 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
206}; // probably a better way to detect this...
207
208var memoize = function memoize(fn, keyFn) {
209 if (!keyFn) {
210 keyFn = function keyFn() {
211 if (arguments.length === 1) {
212 return arguments[0];
213 } else if (arguments.length === 0) {
214 return 'undefined';
215 }
216
217 var args = [];
218
219 for (var i = 0; i < arguments.length; i++) {
220 args.push(arguments[i]);
221 }
222
223 return args.join('$');
224 };
225 }
226
227 var memoizedFn = function memoizedFn() {
228 var self = this;
229 var args = arguments;
230 var ret;
231 var k = keyFn.apply(self, args);
232 var cache = memoizedFn.cache;
233
234 if (!(ret = cache[k])) {
235 ret = cache[k] = fn.apply(self, args);
236 }
237
238 return ret;
239 };
240
241 memoizedFn.cache = {};
242 return memoizedFn;
243};
244
245var camel2dash = memoize(function (str) {
246 return str.replace(/([A-Z])/g, function (v) {
247 return '-' + v.toLowerCase();
248 });
249});
250var dash2camel = memoize(function (str) {
251 return str.replace(/(-\w)/g, function (v) {
252 return v[1].toUpperCase();
253 });
254});
255var prependCamel = memoize(function (prefix, str) {
256 return prefix + str[0].toUpperCase() + str.substring(1);
257}, function (prefix, str) {
258 return prefix + '$' + str;
259});
260var capitalize = function capitalize(str) {
261 if (emptyString(str)) {
262 return str;
263 }
264
265 return str.charAt(0).toUpperCase() + str.substring(1);
266};
267
268var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
269var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
270var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
271var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
272var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
273var hex3 = '\\#[0-9a-fA-F]{3}';
274var hex6 = '\\#[0-9a-fA-F]{6}';
275
276var ascending = function ascending(a, b) {
277 if (a < b) {
278 return -1;
279 } else if (a > b) {
280 return 1;
281 } else {
282 return 0;
283 }
284};
285var descending = function descending(a, b) {
286 return -1 * ascending(a, b);
287};
288
289var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
290 var args = arguments;
291
292 for (var i = 1; i < args.length; i++) {
293 var obj = args[i];
294
295 if (obj == null) {
296 continue;
297 }
298
299 var keys = Object.keys(obj);
300
301 for (var j = 0; j < keys.length; j++) {
302 var k = keys[j];
303 tgt[k] = obj[k];
304 }
305 }
306
307 return tgt;
308};
309
310var hex2tuple = function hex2tuple(hex) {
311 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
312 return;
313 }
314
315 var shortHex = hex.length === 4;
316 var r, g, b;
317 var base = 16;
318
319 if (shortHex) {
320 r = parseInt(hex[1] + hex[1], base);
321 g = parseInt(hex[2] + hex[2], base);
322 b = parseInt(hex[3] + hex[3], base);
323 } else {
324 r = parseInt(hex[1] + hex[2], base);
325 g = parseInt(hex[3] + hex[4], base);
326 b = parseInt(hex[5] + hex[6], base);
327 }
328
329 return [r, g, b];
330}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
331
332var hsl2tuple = function hsl2tuple(hsl) {
333 var ret;
334 var h, s, l, a, r, g, b;
335
336 function hue2rgb(p, q, t) {
337 if (t < 0) t += 1;
338 if (t > 1) t -= 1;
339 if (t < 1 / 6) return p + (q - p) * 6 * t;
340 if (t < 1 / 2) return q;
341 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
342 return p;
343 }
344
345 var m = new RegExp('^' + hsla + '$').exec(hsl);
346
347 if (m) {
348 // get hue
349 h = parseInt(m[1]);
350
351 if (h < 0) {
352 h = (360 - -1 * h % 360) % 360;
353 } else if (h > 360) {
354 h = h % 360;
355 }
356
357 h /= 360; // normalise on [0, 1]
358
359 s = parseFloat(m[2]);
360
361 if (s < 0 || s > 100) {
362 return;
363 } // saturation is [0, 100]
364
365
366 s = s / 100; // normalise on [0, 1]
367
368 l = parseFloat(m[3]);
369
370 if (l < 0 || l > 100) {
371 return;
372 } // lightness is [0, 100]
373
374
375 l = l / 100; // normalise on [0, 1]
376
377 a = m[4];
378
379 if (a !== undefined) {
380 a = parseFloat(a);
381
382 if (a < 0 || a > 1) {
383 return;
384 } // alpha is [0, 1]
385
386 } // now, convert to rgb
387 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
388
389
390 if (s === 0) {
391 r = g = b = Math.round(l * 255); // achromatic
392 } else {
393 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
394 var p = 2 * l - q;
395 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
396 g = Math.round(255 * hue2rgb(p, q, h));
397 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
398 }
399
400 ret = [r, g, b, a];
401 }
402
403 return ret;
404}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
405
406var rgb2tuple = function rgb2tuple(rgb) {
407 var ret;
408 var m = new RegExp('^' + rgba + '$').exec(rgb);
409
410 if (m) {
411 ret = [];
412 var isPct = [];
413
414 for (var i = 1; i <= 3; i++) {
415 var channel = m[i];
416
417 if (channel[channel.length - 1] === '%') {
418 isPct[i] = true;
419 }
420
421 channel = parseFloat(channel);
422
423 if (isPct[i]) {
424 channel = channel / 100 * 255; // normalise to [0, 255]
425 }
426
427 if (channel < 0 || channel > 255) {
428 return;
429 } // invalid channel value
430
431
432 ret.push(Math.floor(channel));
433 }
434
435 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
436 var allArePct = isPct[1] && isPct[2] && isPct[3];
437
438 if (atLeastOneIsPct && !allArePct) {
439 return;
440 } // must all be percent values if one is
441
442
443 var alpha = m[4];
444
445 if (alpha !== undefined) {
446 alpha = parseFloat(alpha);
447
448 if (alpha < 0 || alpha > 1) {
449 return;
450 } // invalid alpha value
451
452
453 ret.push(alpha);
454 }
455 }
456
457 return ret;
458};
459var colorname2tuple = function colorname2tuple(color) {
460 return colors[color.toLowerCase()];
461};
462var color2tuple = function color2tuple(color) {
463 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
464};
465var colors = {
466 // special colour names
467 transparent: [0, 0, 0, 0],
468 // NB alpha === 0
469 // regular colours
470 aliceblue: [240, 248, 255],
471 antiquewhite: [250, 235, 215],
472 aqua: [0, 255, 255],
473 aquamarine: [127, 255, 212],
474 azure: [240, 255, 255],
475 beige: [245, 245, 220],
476 bisque: [255, 228, 196],
477 black: [0, 0, 0],
478 blanchedalmond: [255, 235, 205],
479 blue: [0, 0, 255],
480 blueviolet: [138, 43, 226],
481 brown: [165, 42, 42],
482 burlywood: [222, 184, 135],
483 cadetblue: [95, 158, 160],
484 chartreuse: [127, 255, 0],
485 chocolate: [210, 105, 30],
486 coral: [255, 127, 80],
487 cornflowerblue: [100, 149, 237],
488 cornsilk: [255, 248, 220],
489 crimson: [220, 20, 60],
490 cyan: [0, 255, 255],
491 darkblue: [0, 0, 139],
492 darkcyan: [0, 139, 139],
493 darkgoldenrod: [184, 134, 11],
494 darkgray: [169, 169, 169],
495 darkgreen: [0, 100, 0],
496 darkgrey: [169, 169, 169],
497 darkkhaki: [189, 183, 107],
498 darkmagenta: [139, 0, 139],
499 darkolivegreen: [85, 107, 47],
500 darkorange: [255, 140, 0],
501 darkorchid: [153, 50, 204],
502 darkred: [139, 0, 0],
503 darksalmon: [233, 150, 122],
504 darkseagreen: [143, 188, 143],
505 darkslateblue: [72, 61, 139],
506 darkslategray: [47, 79, 79],
507 darkslategrey: [47, 79, 79],
508 darkturquoise: [0, 206, 209],
509 darkviolet: [148, 0, 211],
510 deeppink: [255, 20, 147],
511 deepskyblue: [0, 191, 255],
512 dimgray: [105, 105, 105],
513 dimgrey: [105, 105, 105],
514 dodgerblue: [30, 144, 255],
515 firebrick: [178, 34, 34],
516 floralwhite: [255, 250, 240],
517 forestgreen: [34, 139, 34],
518 fuchsia: [255, 0, 255],
519 gainsboro: [220, 220, 220],
520 ghostwhite: [248, 248, 255],
521 gold: [255, 215, 0],
522 goldenrod: [218, 165, 32],
523 gray: [128, 128, 128],
524 grey: [128, 128, 128],
525 green: [0, 128, 0],
526 greenyellow: [173, 255, 47],
527 honeydew: [240, 255, 240],
528 hotpink: [255, 105, 180],
529 indianred: [205, 92, 92],
530 indigo: [75, 0, 130],
531 ivory: [255, 255, 240],
532 khaki: [240, 230, 140],
533 lavender: [230, 230, 250],
534 lavenderblush: [255, 240, 245],
535 lawngreen: [124, 252, 0],
536 lemonchiffon: [255, 250, 205],
537 lightblue: [173, 216, 230],
538 lightcoral: [240, 128, 128],
539 lightcyan: [224, 255, 255],
540 lightgoldenrodyellow: [250, 250, 210],
541 lightgray: [211, 211, 211],
542 lightgreen: [144, 238, 144],
543 lightgrey: [211, 211, 211],
544 lightpink: [255, 182, 193],
545 lightsalmon: [255, 160, 122],
546 lightseagreen: [32, 178, 170],
547 lightskyblue: [135, 206, 250],
548 lightslategray: [119, 136, 153],
549 lightslategrey: [119, 136, 153],
550 lightsteelblue: [176, 196, 222],
551 lightyellow: [255, 255, 224],
552 lime: [0, 255, 0],
553 limegreen: [50, 205, 50],
554 linen: [250, 240, 230],
555 magenta: [255, 0, 255],
556 maroon: [128, 0, 0],
557 mediumaquamarine: [102, 205, 170],
558 mediumblue: [0, 0, 205],
559 mediumorchid: [186, 85, 211],
560 mediumpurple: [147, 112, 219],
561 mediumseagreen: [60, 179, 113],
562 mediumslateblue: [123, 104, 238],
563 mediumspringgreen: [0, 250, 154],
564 mediumturquoise: [72, 209, 204],
565 mediumvioletred: [199, 21, 133],
566 midnightblue: [25, 25, 112],
567 mintcream: [245, 255, 250],
568 mistyrose: [255, 228, 225],
569 moccasin: [255, 228, 181],
570 navajowhite: [255, 222, 173],
571 navy: [0, 0, 128],
572 oldlace: [253, 245, 230],
573 olive: [128, 128, 0],
574 olivedrab: [107, 142, 35],
575 orange: [255, 165, 0],
576 orangered: [255, 69, 0],
577 orchid: [218, 112, 214],
578 palegoldenrod: [238, 232, 170],
579 palegreen: [152, 251, 152],
580 paleturquoise: [175, 238, 238],
581 palevioletred: [219, 112, 147],
582 papayawhip: [255, 239, 213],
583 peachpuff: [255, 218, 185],
584 peru: [205, 133, 63],
585 pink: [255, 192, 203],
586 plum: [221, 160, 221],
587 powderblue: [176, 224, 230],
588 purple: [128, 0, 128],
589 red: [255, 0, 0],
590 rosybrown: [188, 143, 143],
591 royalblue: [65, 105, 225],
592 saddlebrown: [139, 69, 19],
593 salmon: [250, 128, 114],
594 sandybrown: [244, 164, 96],
595 seagreen: [46, 139, 87],
596 seashell: [255, 245, 238],
597 sienna: [160, 82, 45],
598 silver: [192, 192, 192],
599 skyblue: [135, 206, 235],
600 slateblue: [106, 90, 205],
601 slategray: [112, 128, 144],
602 slategrey: [112, 128, 144],
603 snow: [255, 250, 250],
604 springgreen: [0, 255, 127],
605 steelblue: [70, 130, 180],
606 tan: [210, 180, 140],
607 teal: [0, 128, 128],
608 thistle: [216, 191, 216],
609 tomato: [255, 99, 71],
610 turquoise: [64, 224, 208],
611 violet: [238, 130, 238],
612 wheat: [245, 222, 179],
613 white: [255, 255, 255],
614 whitesmoke: [245, 245, 245],
615 yellow: [255, 255, 0],
616 yellowgreen: [154, 205, 50]
617};
618
619var setMap = function setMap(options) {
620 var obj = options.map;
621 var keys = options.keys;
622 var l = keys.length;
623
624 for (var i = 0; i < l; i++) {
625 var key = keys[i];
626
627 if (plainObject(key)) {
628 throw Error('Tried to set map with object key');
629 }
630
631 if (i < keys.length - 1) {
632 // extend the map if necessary
633 if (obj[key] == null) {
634 obj[key] = {};
635 }
636
637 obj = obj[key];
638 } else {
639 // set the value
640 obj[key] = options.value;
641 }
642 }
643}; // gets the value in a map even if it's not built in places
644
645var getMap = function getMap(options) {
646 var obj = options.map;
647 var keys = options.keys;
648 var l = keys.length;
649
650 for (var i = 0; i < l; i++) {
651 var key = keys[i];
652
653 if (plainObject(key)) {
654 throw Error('Tried to get map with object key');
655 }
656
657 obj = obj[key];
658
659 if (obj == null) {
660 return obj;
661 }
662 }
663
664 return obj;
665}; // deletes the entry in the map
666
667var performance = window$1 ? window$1.performance : null;
668var pnow = performance && performance.now ? function () {
669 return performance.now();
670} : function () {
671 return Date.now();
672};
673
674var raf = function () {
675 if (window$1) {
676 if (window$1.requestAnimationFrame) {
677 return function (fn) {
678 window$1.requestAnimationFrame(fn);
679 };
680 } else if (window$1.mozRequestAnimationFrame) {
681 return function (fn) {
682 window$1.mozRequestAnimationFrame(fn);
683 };
684 } else if (window$1.webkitRequestAnimationFrame) {
685 return function (fn) {
686 window$1.webkitRequestAnimationFrame(fn);
687 };
688 } else if (window$1.msRequestAnimationFrame) {
689 return function (fn) {
690 window$1.msRequestAnimationFrame(fn);
691 };
692 }
693 }
694
695 return function (fn) {
696 if (fn) {
697 setTimeout(function () {
698 fn(pnow());
699 }, 1000 / 60);
700 }
701 };
702}();
703
704var requestAnimationFrame = function requestAnimationFrame(fn) {
705 return raf(fn);
706};
707var performanceNow = pnow;
708
709var DEFAULT_HASH_SEED = 9261;
710var K = 65599; // 37 also works pretty well
711
712var DEFAULT_HASH_SEED_ALT = 5381;
713var hashIterableInts = function hashIterableInts(iterator) {
714 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
715 // sdbm/string-hash
716 var hash = seed;
717 var entry;
718
719 for (;;) {
720 entry = iterator.next();
721
722 if (entry.done) {
723 break;
724 }
725
726 hash = hash * K + entry.value | 0;
727 }
728
729 return hash;
730};
731var hashInt = function hashInt(num) {
732 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
733 // sdbm/string-hash
734 return seed * K + num | 0;
735};
736var hashIntAlt = function hashIntAlt(num) {
737 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
738 // djb2/string-hash
739 return (seed << 5) + seed + num | 0;
740};
741var combineHashes = function combineHashes(hash1, hash2) {
742 return hash1 * 0x200000 + hash2;
743};
744var combineHashesArray = function combineHashesArray(hashes) {
745 return hashes[0] * 0x200000 + hashes[1];
746};
747var hashArrays = function hashArrays(hashes1, hashes2) {
748 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
749};
750var hashIntsArray = function hashIntsArray(ints, seed) {
751 var entry = {
752 value: 0,
753 done: false
754 };
755 var i = 0;
756 var length = ints.length;
757 var iterator = {
758 next: function next() {
759 if (i < length) {
760 entry.value = ints[i++];
761 } else {
762 entry.done = true;
763 }
764
765 return entry;
766 }
767 };
768 return hashIterableInts(iterator, seed);
769};
770var hashString = function hashString(str, seed) {
771 var entry = {
772 value: 0,
773 done: false
774 };
775 var i = 0;
776 var length = str.length;
777 var iterator = {
778 next: function next() {
779 if (i < length) {
780 entry.value = str.charCodeAt(i++);
781 } else {
782 entry.done = true;
783 }
784
785 return entry;
786 }
787 };
788 return hashIterableInts(iterator, seed);
789};
790var hashStrings = function hashStrings() {
791 return hashStringsArray(arguments);
792};
793var hashStringsArray = function hashStringsArray(strs) {
794 var hash;
795
796 for (var i = 0; i < strs.length; i++) {
797 var str = strs[i];
798
799 if (i === 0) {
800 hash = hashString(str);
801 } else {
802 hash = hashString(str, hash);
803 }
804 }
805
806 return hash;
807};
808
809/*global console */
810var warningsEnabled = true;
811var warnSupported = console.warn != null; // eslint-disable-line no-console
812
813var traceSupported = console.trace != null; // eslint-disable-line no-console
814
815var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
816var trueify = function trueify() {
817 return true;
818};
819var falsify = function falsify() {
820 return false;
821};
822var zeroify = function zeroify() {
823 return 0;
824};
825var noop = function noop() {};
826var error = function error(msg) {
827 throw new Error(msg);
828};
829var warnings = function warnings(enabled) {
830 if (enabled !== undefined) {
831 warningsEnabled = !!enabled;
832 } else {
833 return warningsEnabled;
834 }
835};
836var warn = function warn(msg) {
837 /* eslint-disable no-console */
838 if (!warnings()) {
839 return;
840 }
841
842 if (warnSupported) {
843 console.warn(msg);
844 } else {
845 console.log(msg);
846
847 if (traceSupported) {
848 console.trace();
849 }
850 }
851};
852/* eslint-enable */
853
854var clone = function clone(obj) {
855 return extend({}, obj);
856}; // gets a shallow copy of the argument
857
858var copy = function copy(obj) {
859 if (obj == null) {
860 return obj;
861 }
862
863 if (array(obj)) {
864 return obj.slice();
865 } else if (plainObject(obj)) {
866 return clone(obj);
867 } else {
868 return obj;
869 }
870};
871var copyArray = function copyArray(arr) {
872 return arr.slice();
873};
874var uuid = function uuid(a, b
875/* placeholders */
876) {
877 for ( // loop :)
878 b = a = ''; // b - result , a - numeric letiable
879 a++ < 36; //
880 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
881 ? // return a random number or 4
882 (a ^ 15 // if "a" is not 15
883 ? // genetate a random number from 0 to 15
884 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
885 : 4 // otherwise 4
886 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
887 ) {
888 }
889
890 return b;
891};
892var _staticEmptyObject = {};
893var staticEmptyObject = function staticEmptyObject() {
894 return _staticEmptyObject;
895};
896var defaults = function defaults(_defaults) {
897 var keys = Object.keys(_defaults);
898 return function (opts) {
899 var filledOpts = {};
900
901 for (var i = 0; i < keys.length; i++) {
902 var key = keys[i];
903 var optVal = opts == null ? undefined : opts[key];
904 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
905 }
906
907 return filledOpts;
908 };
909};
910var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
911 for (var i = arr.length - 1; i >= 0; i--) {
912 if (arr[i] === ele) {
913 arr.splice(i, 1);
914
915 if (oneCopy) {
916 break;
917 }
918 }
919 }
920};
921var clearArray = function clearArray(arr) {
922 arr.splice(0, arr.length);
923};
924var push = function push(arr, otherArr) {
925 for (var i = 0; i < otherArr.length; i++) {
926 var el = otherArr[i];
927 arr.push(el);
928 }
929};
930var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
931 if (prefix) {
932 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
933 }
934
935 return obj[propName];
936};
937var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
938 if (prefix) {
939 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
940 }
941
942 obj[propName] = value;
943};
944
945/* global Map */
946var ObjectMap =
947/*#__PURE__*/
948function () {
949 function ObjectMap() {
950 _classCallCheck(this, ObjectMap);
951
952 this._obj = {};
953 }
954
955 _createClass(ObjectMap, [{
956 key: "set",
957 value: function set(key, val) {
958 this._obj[key] = val;
959 return this;
960 }
961 }, {
962 key: "delete",
963 value: function _delete(key) {
964 this._obj[key] = undefined;
965 return this;
966 }
967 }, {
968 key: "clear",
969 value: function clear() {
970 this._obj = {};
971 }
972 }, {
973 key: "has",
974 value: function has(key) {
975 return this._obj[key] !== undefined;
976 }
977 }, {
978 key: "get",
979 value: function get(key) {
980 return this._obj[key];
981 }
982 }]);
983
984 return ObjectMap;
985}();
986
987var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
988
989/* global Set */
990var undef = "undefined" ;
991
992var ObjectSet =
993/*#__PURE__*/
994function () {
995 function ObjectSet(arrayOrObjectSet) {
996 _classCallCheck(this, ObjectSet);
997
998 this._obj = Object.create(null);
999 this.size = 0;
1000
1001 if (arrayOrObjectSet != null) {
1002 var arr;
1003
1004 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1005 arr = arrayOrObjectSet.toArray();
1006 } else {
1007 arr = arrayOrObjectSet;
1008 }
1009
1010 for (var i = 0; i < arr.length; i++) {
1011 this.add(arr[i]);
1012 }
1013 }
1014 }
1015
1016 _createClass(ObjectSet, [{
1017 key: "instanceString",
1018 value: function instanceString() {
1019 return 'set';
1020 }
1021 }, {
1022 key: "add",
1023 value: function add(val) {
1024 var o = this._obj;
1025
1026 if (o[val] !== 1) {
1027 o[val] = 1;
1028 this.size++;
1029 }
1030 }
1031 }, {
1032 key: "delete",
1033 value: function _delete(val) {
1034 var o = this._obj;
1035
1036 if (o[val] === 1) {
1037 o[val] = 0;
1038 this.size--;
1039 }
1040 }
1041 }, {
1042 key: "clear",
1043 value: function clear() {
1044 this._obj = Object.create(null);
1045 }
1046 }, {
1047 key: "has",
1048 value: function has(val) {
1049 return this._obj[val] === 1;
1050 }
1051 }, {
1052 key: "toArray",
1053 value: function toArray() {
1054 var _this = this;
1055
1056 return Object.keys(this._obj).filter(function (key) {
1057 return _this.has(key);
1058 });
1059 }
1060 }, {
1061 key: "forEach",
1062 value: function forEach(callback, thisArg) {
1063 return this.toArray().forEach(callback, thisArg);
1064 }
1065 }]);
1066
1067 return ObjectSet;
1068}();
1069
1070var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1071
1072var Element = function Element(cy, params) {
1073 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1074
1075 if (cy === undefined || params === undefined || !core(cy)) {
1076 error('An element must have a core reference and parameters set');
1077 return;
1078 }
1079
1080 var group = params.group; // try to automatically infer the group if unspecified
1081
1082 if (group == null) {
1083 if (params.data && params.data.source != null && params.data.target != null) {
1084 group = 'edges';
1085 } else {
1086 group = 'nodes';
1087 }
1088 } // validate group
1089
1090
1091 if (group !== 'nodes' && group !== 'edges') {
1092 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1093 return;
1094 } // make the element array-like, just like a collection
1095
1096
1097 this.length = 1;
1098 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1099
1100 var _p = this._private = {
1101 cy: cy,
1102 single: true,
1103 // indicates this is an element
1104 data: params.data || {},
1105 // data object
1106 position: params.position || {
1107 x: 0,
1108 y: 0
1109 },
1110 // (x, y) position pair
1111 autoWidth: undefined,
1112 // width and height of nodes calculated by the renderer when set to special 'auto' value
1113 autoHeight: undefined,
1114 autoPadding: undefined,
1115 compoundBoundsClean: false,
1116 // whether the compound dimensions need to be recalculated the next time dimensions are read
1117 listeners: [],
1118 // array of bound listeners
1119 group: group,
1120 // string; 'nodes' or 'edges'
1121 style: {},
1122 // properties as set by the style
1123 rstyle: {},
1124 // properties for style sent from the renderer to the core
1125 styleCxts: [],
1126 // applied style contexts from the styler
1127 styleKeys: {},
1128 // per-group keys of style property values
1129 removed: true,
1130 // whether it's inside the vis; true if removed (set true here since we call restore)
1131 selected: params.selected ? true : false,
1132 // whether it's selected
1133 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1134 // whether it's selectable
1135 locked: params.locked ? true : false,
1136 // whether the element is locked (cannot be moved)
1137 grabbed: false,
1138 // whether the element is grabbed by the mouse; renderer sets this privately
1139 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1140 // whether the element can be grabbed
1141 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1142 // whether the element has passthrough panning enabled
1143 active: false,
1144 // whether the element is active from user interaction
1145 classes: new Set$1(),
1146 // map ( className => true )
1147 animation: {
1148 // object for currently-running animations
1149 current: [],
1150 queue: []
1151 },
1152 rscratch: {},
1153 // object in which the renderer can store information
1154 scratch: params.scratch || {},
1155 // scratch objects
1156 edges: [],
1157 // array of connected edges
1158 children: [],
1159 // array of children
1160 parent: null,
1161 // parent ref
1162 traversalCache: {},
1163 // cache of output of traversal functions
1164 backgrounding: false,
1165 // whether background images are loading
1166 bbCache: null,
1167 // cache of the current bounding box
1168 bbCacheShift: {
1169 x: 0,
1170 y: 0
1171 },
1172 // shift applied to cached bb to be applied on next get
1173 bodyBounds: null,
1174 // bounds cache of element body, w/o overlay
1175 overlayBounds: null,
1176 // bounds cache of element body, including overlay
1177 labelBounds: {
1178 // bounds cache of labels
1179 all: null,
1180 source: null,
1181 target: null,
1182 main: null
1183 },
1184 arrowBounds: {
1185 // bounds cache of edge arrows
1186 source: null,
1187 target: null,
1188 'mid-source': null,
1189 'mid-target': null
1190 }
1191 };
1192
1193 if (_p.position.x == null) {
1194 _p.position.x = 0;
1195 }
1196
1197 if (_p.position.y == null) {
1198 _p.position.y = 0;
1199 } // renderedPosition overrides if specified
1200
1201
1202 if (params.renderedPosition) {
1203 var rpos = params.renderedPosition;
1204 var pan = cy.pan();
1205 var zoom = cy.zoom();
1206 _p.position = {
1207 x: (rpos.x - pan.x) / zoom,
1208 y: (rpos.y - pan.y) / zoom
1209 };
1210 }
1211
1212 var classes = [];
1213
1214 if (array(params.classes)) {
1215 classes = params.classes;
1216 } else if (string(params.classes)) {
1217 classes = params.classes.split(/\s+/);
1218 }
1219
1220 for (var i = 0, l = classes.length; i < l; i++) {
1221 var cls = classes[i];
1222
1223 if (!cls || cls === '') {
1224 continue;
1225 }
1226
1227 _p.classes.add(cls);
1228 }
1229
1230 this.createEmitter();
1231 var bypass = params.style || params.css;
1232
1233 if (bypass) {
1234 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1235 this.style(bypass);
1236 }
1237
1238 if (restore === undefined || restore) {
1239 this.restore();
1240 }
1241};
1242
1243var defineSearch = function defineSearch(params) {
1244 params = {
1245 bfs: params.bfs || !params.dfs,
1246 dfs: params.dfs || !params.bfs
1247 }; // from pseudocode on wikipedia
1248
1249 return function searchFn(roots, fn$1, directed) {
1250 var options;
1251
1252 if (plainObject(roots) && !elementOrCollection(roots)) {
1253 options = roots;
1254 roots = options.roots || options.root;
1255 fn$1 = options.visit;
1256 directed = options.directed;
1257 }
1258
1259 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1260 fn$1 = fn(fn$1) ? fn$1 : function () {};
1261 var cy = this._private.cy;
1262 var v = roots = string(roots) ? this.filter(roots) : roots;
1263 var Q = [];
1264 var connectedNodes = [];
1265 var connectedBy = {};
1266 var id2depth = {};
1267 var V = {};
1268 var j = 0;
1269 var found;
1270
1271 var _this$byGroup = this.byGroup(),
1272 nodes = _this$byGroup.nodes,
1273 edges = _this$byGroup.edges; // enqueue v
1274
1275
1276 for (var i = 0; i < v.length; i++) {
1277 var vi = v[i];
1278 var viId = vi.id();
1279
1280 if (vi.isNode()) {
1281 Q.unshift(vi);
1282
1283 if (params.bfs) {
1284 V[viId] = true;
1285 connectedNodes.push(vi);
1286 }
1287
1288 id2depth[viId] = 0;
1289 }
1290 }
1291
1292 var _loop2 = function _loop2() {
1293 var v = params.bfs ? Q.shift() : Q.pop();
1294 var vId = v.id();
1295
1296 if (params.dfs) {
1297 if (V[vId]) {
1298 return "continue";
1299 }
1300
1301 V[vId] = true;
1302 connectedNodes.push(v);
1303 }
1304
1305 var depth = id2depth[vId];
1306 var prevEdge = connectedBy[vId];
1307 var src = prevEdge != null ? prevEdge.source() : null;
1308 var tgt = prevEdge != null ? prevEdge.target() : null;
1309 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1310 var ret = void 0;
1311 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1312
1313 if (ret === true) {
1314 found = v;
1315 return "break";
1316 }
1317
1318 if (ret === false) {
1319 return "break";
1320 }
1321
1322 var vwEdges = v.connectedEdges().filter(function (e) {
1323 return (!directed || e.source().same(v)) && edges.has(e);
1324 });
1325
1326 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1327 var e = vwEdges[_i2];
1328 var w = e.connectedNodes().filter(function (n) {
1329 return !n.same(v) && nodes.has(n);
1330 });
1331 var wId = w.id();
1332
1333 if (w.length !== 0 && !V[wId]) {
1334 w = w[0];
1335 Q.push(w);
1336
1337 if (params.bfs) {
1338 V[wId] = true;
1339 connectedNodes.push(w);
1340 }
1341
1342 connectedBy[wId] = e;
1343 id2depth[wId] = id2depth[vId] + 1;
1344 }
1345 }
1346 };
1347
1348 _loop: while (Q.length !== 0) {
1349 var _ret = _loop2();
1350
1351 switch (_ret) {
1352 case "continue":
1353 continue;
1354
1355 case "break":
1356 break _loop;
1357 }
1358 }
1359
1360 var connectedEles = cy.collection();
1361
1362 for (var _i = 0; _i < connectedNodes.length; _i++) {
1363 var node = connectedNodes[_i];
1364 var edge = connectedBy[node.id()];
1365
1366 if (edge != null) {
1367 connectedEles.push(edge);
1368 }
1369
1370 connectedEles.push(node);
1371 }
1372
1373 return {
1374 path: cy.collection(connectedEles),
1375 found: cy.collection(found)
1376 };
1377 };
1378}; // search, spanning trees, etc
1379
1380
1381var elesfn = {
1382 breadthFirstSearch: defineSearch({
1383 bfs: true
1384 }),
1385 depthFirstSearch: defineSearch({
1386 dfs: true
1387 })
1388}; // nice, short mathemathical alias
1389
1390elesfn.bfs = elesfn.breadthFirstSearch;
1391elesfn.dfs = elesfn.depthFirstSearch;
1392
1393var dijkstraDefaults = defaults({
1394 root: null,
1395 weight: function weight(edge) {
1396 return 1;
1397 },
1398 directed: false
1399});
1400var elesfn$1 = {
1401 dijkstra: function dijkstra(options) {
1402 if (!plainObject(options)) {
1403 var args = arguments;
1404 options = {
1405 root: args[0],
1406 weight: args[1],
1407 directed: args[2]
1408 };
1409 }
1410
1411 var _dijkstraDefaults = dijkstraDefaults(options),
1412 root = _dijkstraDefaults.root,
1413 weight = _dijkstraDefaults.weight,
1414 directed = _dijkstraDefaults.directed;
1415
1416 var eles = this;
1417 var weightFn = weight;
1418 var source = string(root) ? this.filter(root)[0] : root[0];
1419 var dist = {};
1420 var prev = {};
1421 var knownDist = {};
1422
1423 var _this$byGroup = this.byGroup(),
1424 nodes = _this$byGroup.nodes,
1425 edges = _this$byGroup.edges;
1426
1427 edges.unmergeBy(function (ele) {
1428 return ele.isLoop();
1429 });
1430
1431 var getDist = function getDist(node) {
1432 return dist[node.id()];
1433 };
1434
1435 var setDist = function setDist(node, d) {
1436 dist[node.id()] = d;
1437 Q.updateItem(node);
1438 };
1439
1440 var Q = new Heap(function (a, b) {
1441 return getDist(a) - getDist(b);
1442 });
1443
1444 for (var i = 0; i < nodes.length; i++) {
1445 var node = nodes[i];
1446 dist[node.id()] = node.same(source) ? 0 : Infinity;
1447 Q.push(node);
1448 }
1449
1450 var distBetween = function distBetween(u, v) {
1451 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1452 var smallestDistance = Infinity;
1453 var smallestEdge;
1454
1455 for (var _i = 0; _i < uvs.length; _i++) {
1456 var edge = uvs[_i];
1457
1458 var _weight = weightFn(edge);
1459
1460 if (_weight < smallestDistance || !smallestEdge) {
1461 smallestDistance = _weight;
1462 smallestEdge = edge;
1463 }
1464 }
1465
1466 return {
1467 edge: smallestEdge,
1468 dist: smallestDistance
1469 };
1470 };
1471
1472 while (Q.size() > 0) {
1473 var u = Q.pop();
1474 var smalletsDist = getDist(u);
1475 var uid = u.id();
1476 knownDist[uid] = smalletsDist;
1477
1478 if (smalletsDist === Infinity) {
1479 continue;
1480 }
1481
1482 var neighbors = u.neighborhood().intersect(nodes);
1483
1484 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1485 var v = neighbors[_i2];
1486 var vid = v.id();
1487 var vDist = distBetween(u, v);
1488 var alt = smalletsDist + vDist.dist;
1489
1490 if (alt < getDist(v)) {
1491 setDist(v, alt);
1492 prev[vid] = {
1493 node: u,
1494 edge: vDist.edge
1495 };
1496 }
1497 } // for
1498
1499 } // while
1500
1501
1502 return {
1503 distanceTo: function distanceTo(node) {
1504 var target = string(node) ? nodes.filter(node)[0] : node[0];
1505 return knownDist[target.id()];
1506 },
1507 pathTo: function pathTo(node) {
1508 var target = string(node) ? nodes.filter(node)[0] : node[0];
1509 var S = [];
1510 var u = target;
1511 var uid = u.id();
1512
1513 if (target.length > 0) {
1514 S.unshift(target);
1515
1516 while (prev[uid]) {
1517 var p = prev[uid];
1518 S.unshift(p.edge);
1519 S.unshift(p.node);
1520 u = p.node;
1521 uid = u.id();
1522 }
1523 }
1524
1525 return eles.spawn(S);
1526 }
1527 };
1528 }
1529};
1530
1531var elesfn$2 = {
1532 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1533 // implemented from pseudocode from wikipedia
1534 kruskal: function kruskal(weightFn) {
1535 weightFn = weightFn || function (edge) {
1536 return 1;
1537 };
1538
1539 var _this$byGroup = this.byGroup(),
1540 nodes = _this$byGroup.nodes,
1541 edges = _this$byGroup.edges;
1542
1543 var numNodes = nodes.length;
1544 var forest = new Array(numNodes);
1545 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1546
1547 var findSetIndex = function findSetIndex(ele) {
1548 for (var i = 0; i < forest.length; i++) {
1549 var eles = forest[i];
1550
1551 if (eles.has(ele)) {
1552 return i;
1553 }
1554 }
1555 }; // start with one forest per node
1556
1557
1558 for (var i = 0; i < numNodes; i++) {
1559 forest[i] = this.spawn(nodes[i]);
1560 }
1561
1562 var S = edges.sort(function (a, b) {
1563 return weightFn(a) - weightFn(b);
1564 });
1565
1566 for (var _i = 0; _i < S.length; _i++) {
1567 var edge = S[_i];
1568 var u = edge.source()[0];
1569 var v = edge.target()[0];
1570 var setUIndex = findSetIndex(u);
1571 var setVIndex = findSetIndex(v);
1572 var setU = forest[setUIndex];
1573 var setV = forest[setVIndex];
1574
1575 if (setUIndex !== setVIndex) {
1576 A.merge(edge); // combine forests for u and v
1577
1578 setU.merge(setV);
1579 forest.splice(setVIndex, 1);
1580 }
1581 }
1582
1583 return A;
1584 }
1585};
1586
1587var aStarDefaults = defaults({
1588 root: null,
1589 goal: null,
1590 weight: function weight(edge) {
1591 return 1;
1592 },
1593 heuristic: function heuristic(edge) {
1594 return 0;
1595 },
1596 directed: false
1597});
1598var elesfn$3 = {
1599 // Implemented from pseudocode from wikipedia
1600 aStar: function aStar(options) {
1601 var cy = this.cy();
1602
1603 var _aStarDefaults = aStarDefaults(options),
1604 root = _aStarDefaults.root,
1605 goal = _aStarDefaults.goal,
1606 heuristic = _aStarDefaults.heuristic,
1607 directed = _aStarDefaults.directed,
1608 weight = _aStarDefaults.weight;
1609
1610 root = cy.collection(root)[0];
1611 goal = cy.collection(goal)[0];
1612 var sid = root.id();
1613 var tid = goal.id();
1614 var gScore = {};
1615 var fScore = {};
1616 var closedSetIds = {};
1617 var openSet = new Heap(function (a, b) {
1618 return fScore[a.id()] - fScore[b.id()];
1619 });
1620 var openSetIds = new Set$1();
1621 var cameFrom = {};
1622 var cameFromEdge = {};
1623
1624 var addToOpenSet = function addToOpenSet(ele, id) {
1625 openSet.push(ele);
1626 openSetIds.add(id);
1627 };
1628
1629 var cMin, cMinId;
1630
1631 var popFromOpenSet = function popFromOpenSet() {
1632 cMin = openSet.pop();
1633 cMinId = cMin.id();
1634 openSetIds["delete"](cMinId);
1635 };
1636
1637 var isInOpenSet = function isInOpenSet(id) {
1638 return openSetIds.has(id);
1639 };
1640
1641 addToOpenSet(root, sid);
1642 gScore[sid] = 0;
1643 fScore[sid] = heuristic(root); // Counter
1644
1645 var steps = 0; // Main loop
1646
1647 while (openSet.size() > 0) {
1648 popFromOpenSet();
1649 steps++; // If we've found our goal, then we are done
1650
1651 if (cMinId === tid) {
1652 var path = [];
1653 var pathNode = goal;
1654 var pathNodeId = tid;
1655 var pathEdge = cameFromEdge[pathNodeId];
1656
1657 for (;;) {
1658 path.unshift(pathNode);
1659
1660 if (pathEdge != null) {
1661 path.unshift(pathEdge);
1662 }
1663
1664 pathNode = cameFrom[pathNodeId];
1665
1666 if (pathNode == null) {
1667 break;
1668 }
1669
1670 pathNodeId = pathNode.id();
1671 pathEdge = cameFromEdge[pathNodeId];
1672 }
1673
1674 return {
1675 found: true,
1676 distance: gScore[cMinId],
1677 path: this.spawn(path),
1678 steps: steps
1679 };
1680 } // Add cMin to processed nodes
1681
1682
1683 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1684 // Take into account if graph is directed or not
1685
1686 var vwEdges = cMin._private.edges;
1687
1688 for (var i = 0; i < vwEdges.length; i++) {
1689 var e = vwEdges[i]; // edge must be in set of calling eles
1690
1691 if (!this.hasElementWithId(e.id())) {
1692 continue;
1693 } // cMin must be the source of edge if directed
1694
1695
1696 if (directed && e.data('source') !== cMinId) {
1697 continue;
1698 }
1699
1700 var wSrc = e.source();
1701 var wTgt = e.target();
1702 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1703 var wid = w.id(); // node must be in set of calling eles
1704
1705 if (!this.hasElementWithId(wid)) {
1706 continue;
1707 } // if node is in closedSet, ignore it
1708
1709
1710 if (closedSetIds[wid]) {
1711 continue;
1712 } // New tentative score for node w
1713
1714
1715 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1716 // w not present in openSet
1717 // OR
1718 // tentative gScore is less than previous value
1719 // w not in openSet
1720
1721 if (!isInOpenSet(wid)) {
1722 gScore[wid] = tempScore;
1723 fScore[wid] = tempScore + heuristic(w);
1724 addToOpenSet(w, wid);
1725 cameFrom[wid] = cMin;
1726 cameFromEdge[wid] = e;
1727 continue;
1728 } // w already in openSet, but with greater gScore
1729
1730
1731 if (tempScore < gScore[wid]) {
1732 gScore[wid] = tempScore;
1733 fScore[wid] = tempScore + heuristic(w);
1734 cameFrom[wid] = cMin;
1735 cameFromEdge[wid] = e;
1736 }
1737 } // End of neighbors update
1738
1739 } // End of main loop
1740 // If we've reached here, then we've not reached our goal
1741
1742
1743 return {
1744 found: false,
1745 distance: undefined,
1746 path: undefined,
1747 steps: steps
1748 };
1749 }
1750}; // elesfn
1751
1752var floydWarshallDefaults = defaults({
1753 weight: function weight(edge) {
1754 return 1;
1755 },
1756 directed: false
1757});
1758var elesfn$4 = {
1759 // Implemented from pseudocode from wikipedia
1760 floydWarshall: function floydWarshall(options) {
1761 var cy = this.cy();
1762
1763 var _floydWarshallDefault = floydWarshallDefaults(options),
1764 weight = _floydWarshallDefault.weight,
1765 directed = _floydWarshallDefault.directed;
1766
1767 var weightFn = weight;
1768
1769 var _this$byGroup = this.byGroup(),
1770 nodes = _this$byGroup.nodes,
1771 edges = _this$byGroup.edges;
1772
1773 var N = nodes.length;
1774 var Nsq = N * N;
1775
1776 var indexOf = function indexOf(node) {
1777 return nodes.indexOf(node);
1778 };
1779
1780 var atIndex = function atIndex(i) {
1781 return nodes[i];
1782 }; // Initialize distance matrix
1783
1784
1785 var dist = new Array(Nsq);
1786
1787 for (var n = 0; n < Nsq; n++) {
1788 var j = n % N;
1789 var i = (n - j) / N;
1790
1791 if (i === j) {
1792 dist[n] = 0;
1793 } else {
1794 dist[n] = Infinity;
1795 }
1796 } // Initialize matrix used for path reconstruction
1797 // Initialize distance matrix
1798
1799
1800 var next = new Array(Nsq);
1801 var edgeNext = new Array(Nsq); // Process edges
1802
1803 for (var _i = 0; _i < edges.length; _i++) {
1804 var edge = edges[_i];
1805 var src = edge.source()[0];
1806 var tgt = edge.target()[0];
1807
1808 if (src === tgt) {
1809 continue;
1810 } // exclude loops
1811
1812
1813 var s = indexOf(src);
1814 var t = indexOf(tgt);
1815 var st = s * N + t; // source to target index
1816
1817 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1818
1819
1820 if (dist[st] > _weight) {
1821 dist[st] = _weight;
1822 next[st] = t;
1823 edgeNext[st] = edge;
1824 } // If undirected graph, process 'reversed' edge
1825
1826
1827 if (!directed) {
1828 var ts = t * N + s; // target to source index
1829
1830 if (!directed && dist[ts] > _weight) {
1831 dist[ts] = _weight;
1832 next[ts] = s;
1833 edgeNext[ts] = edge;
1834 }
1835 }
1836 } // Main loop
1837
1838
1839 for (var k = 0; k < N; k++) {
1840 for (var _i2 = 0; _i2 < N; _i2++) {
1841 var ik = _i2 * N + k;
1842
1843 for (var _j = 0; _j < N; _j++) {
1844 var ij = _i2 * N + _j;
1845 var kj = k * N + _j;
1846
1847 if (dist[ik] + dist[kj] < dist[ij]) {
1848 dist[ij] = dist[ik] + dist[kj];
1849 next[ij] = next[ik];
1850 }
1851 }
1852 }
1853 }
1854
1855 var getArgEle = function getArgEle(ele) {
1856 return (string(ele) ? cy.filter(ele) : ele)[0];
1857 };
1858
1859 var indexOfArgEle = function indexOfArgEle(ele) {
1860 return indexOf(getArgEle(ele));
1861 };
1862
1863 var res = {
1864 distance: function distance(from, to) {
1865 var i = indexOfArgEle(from);
1866 var j = indexOfArgEle(to);
1867 return dist[i * N + j];
1868 },
1869 path: function path(from, to) {
1870 var i = indexOfArgEle(from);
1871 var j = indexOfArgEle(to);
1872 var fromNode = atIndex(i);
1873
1874 if (i === j) {
1875 return fromNode.collection();
1876 }
1877
1878 if (next[i * N + j] == null) {
1879 return cy.collection();
1880 }
1881
1882 var path = cy.collection();
1883 var prev = i;
1884 var edge;
1885 path.merge(fromNode);
1886
1887 while (i !== j) {
1888 prev = i;
1889 i = next[i * N + j];
1890 edge = edgeNext[prev * N + i];
1891 path.merge(edge);
1892 path.merge(atIndex(i));
1893 }
1894
1895 return path;
1896 }
1897 };
1898 return res;
1899 } // floydWarshall
1900
1901}; // elesfn
1902
1903var bellmanFordDefaults = defaults({
1904 weight: function weight(edge) {
1905 return 1;
1906 },
1907 directed: false,
1908 root: null
1909});
1910var elesfn$5 = {
1911 // Implemented from pseudocode from wikipedia
1912 bellmanFord: function bellmanFord(options) {
1913 var _this = this;
1914
1915 var _bellmanFordDefaults = bellmanFordDefaults(options),
1916 weight = _bellmanFordDefaults.weight,
1917 directed = _bellmanFordDefaults.directed,
1918 root = _bellmanFordDefaults.root;
1919
1920 var weightFn = weight;
1921 var eles = this;
1922 var cy = this.cy();
1923
1924 var _this$byGroup = this.byGroup(),
1925 edges = _this$byGroup.edges,
1926 nodes = _this$byGroup.nodes;
1927
1928 var numNodes = nodes.length;
1929 var infoMap = new Map$1();
1930 var hasNegativeWeightCycle = false;
1931 var negativeWeightCycles = [];
1932 root = cy.collection(root)[0]; // in case selector passed
1933
1934 edges.unmergeBy(function (edge) {
1935 return edge.isLoop();
1936 });
1937 var numEdges = edges.length;
1938
1939 var getInfo = function getInfo(node) {
1940 var obj = infoMap.get(node.id());
1941
1942 if (!obj) {
1943 obj = {};
1944 infoMap.set(node.id(), obj);
1945 }
1946
1947 return obj;
1948 };
1949
1950 var getNodeFromTo = function getNodeFromTo(to) {
1951 return (string(to) ? cy.$(to) : to)[0];
1952 };
1953
1954 var distanceTo = function distanceTo(to) {
1955 return getInfo(getNodeFromTo(to)).dist;
1956 };
1957
1958 var pathTo = function pathTo(to) {
1959 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1960 var end = getNodeFromTo(to);
1961 var path = [];
1962 var node = end;
1963
1964 for (;;) {
1965 if (node == null) {
1966 return _this.spawn();
1967 }
1968
1969 var _getInfo = getInfo(node),
1970 edge = _getInfo.edge,
1971 pred = _getInfo.pred;
1972
1973 path.unshift(node[0]);
1974
1975 if (node.same(thisStart) && path.length > 0) {
1976 break;
1977 }
1978
1979 if (edge != null) {
1980 path.unshift(edge);
1981 }
1982
1983 node = pred;
1984 }
1985
1986 return eles.spawn(path);
1987 }; // Initializations { dist, pred, edge }
1988
1989
1990 for (var i = 0; i < numNodes; i++) {
1991 var node = nodes[i];
1992 var info = getInfo(node);
1993
1994 if (node.same(root)) {
1995 info.dist = 0;
1996 } else {
1997 info.dist = Infinity;
1998 }
1999
2000 info.pred = null;
2001 info.edge = null;
2002 } // Edges relaxation
2003
2004
2005 var replacedEdge = false;
2006
2007 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2008 var dist = info1.dist + weight;
2009
2010 if (dist < info2.dist && !edge.same(info1.edge)) {
2011 info2.dist = dist;
2012 info2.pred = node1;
2013 info2.edge = edge;
2014 replacedEdge = true;
2015 }
2016 };
2017
2018 for (var _i = 1; _i < numNodes; _i++) {
2019 replacedEdge = false;
2020
2021 for (var e = 0; e < numEdges; e++) {
2022 var edge = edges[e];
2023 var src = edge.source();
2024 var tgt = edge.target();
2025
2026 var _weight = weightFn(edge);
2027
2028 var srcInfo = getInfo(src);
2029 var tgtInfo = getInfo(tgt);
2030 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2031
2032 if (!directed) {
2033 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2034 }
2035 }
2036
2037 if (!replacedEdge) {
2038 break;
2039 }
2040 }
2041
2042 if (replacedEdge) {
2043 // Check for negative weight cycles
2044 var negativeWeightCycleIds = [];
2045
2046 for (var _e = 0; _e < numEdges; _e++) {
2047 var _edge = edges[_e];
2048
2049 var _src = _edge.source();
2050
2051 var _tgt = _edge.target();
2052
2053 var _weight2 = weightFn(_edge);
2054
2055 var srcDist = getInfo(_src).dist;
2056 var tgtDist = getInfo(_tgt).dist;
2057
2058 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2059 if (!hasNegativeWeightCycle) {
2060 warn('Graph contains a negative weight cycle for Bellman-Ford');
2061 hasNegativeWeightCycle = true;
2062 }
2063
2064 if (options.findNegativeWeightCycles !== false) {
2065 var negativeNodes = [];
2066
2067 if (srcDist + _weight2 < tgtDist) {
2068 negativeNodes.push(_src);
2069 }
2070
2071 if (!directed && tgtDist + _weight2 < srcDist) {
2072 negativeNodes.push(_tgt);
2073 }
2074
2075 var numNegativeNodes = negativeNodes.length;
2076
2077 for (var n = 0; n < numNegativeNodes; n++) {
2078 var start = negativeNodes[n];
2079 var cycle = [start];
2080 cycle.push(getInfo(start).edge);
2081 var _node = getInfo(start).pred;
2082
2083 while (cycle.indexOf(_node) === -1) {
2084 cycle.push(_node);
2085 cycle.push(getInfo(_node).edge);
2086 _node = getInfo(_node).pred;
2087 }
2088
2089 cycle = cycle.slice(cycle.indexOf(_node));
2090 var smallestId = cycle[0].id();
2091 var smallestIndex = 0;
2092
2093 for (var c = 2; c < cycle.length; c += 2) {
2094 if (cycle[c].id() < smallestId) {
2095 smallestId = cycle[c].id();
2096 smallestIndex = c;
2097 }
2098 }
2099
2100 cycle = cycle.slice(smallestIndex).concat(cycle.slice(0, smallestIndex));
2101 cycle.push(cycle[0]);
2102 var cycleId = cycle.map(function (el) {
2103 return el.id();
2104 }).join(",");
2105
2106 if (negativeWeightCycleIds.indexOf(cycleId) === -1) {
2107 negativeWeightCycles.push(eles.spawn(cycle));
2108 negativeWeightCycleIds.push(cycleId);
2109 }
2110 }
2111 } else {
2112 break;
2113 }
2114 }
2115 }
2116 }
2117
2118 return {
2119 distanceTo: distanceTo,
2120 pathTo: pathTo,
2121 hasNegativeWeightCycle: hasNegativeWeightCycle,
2122 negativeWeightCycles: negativeWeightCycles
2123 };
2124 } // bellmanFord
2125
2126}; // elesfn
2127
2128var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2129// Updates the remaining edge lists
2130// Receives as a paramater the edge which causes the collapse
2131
2132var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2133 if (remainingEdges.length === 0) {
2134 error("Karger-Stein must be run on a connected (sub)graph");
2135 }
2136
2137 var edgeInfo = remainingEdges[edgeIndex];
2138 var sourceIn = edgeInfo[1];
2139 var targetIn = edgeInfo[2];
2140 var partition1 = nodeMap[sourceIn];
2141 var partition2 = nodeMap[targetIn];
2142 var newEdges = remainingEdges; // re-use array
2143 // Delete all edges between partition1 and partition2
2144
2145 for (var i = newEdges.length - 1; i >= 0; i--) {
2146 var edge = newEdges[i];
2147 var src = edge[1];
2148 var tgt = edge[2];
2149
2150 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2151 newEdges.splice(i, 1);
2152 }
2153 } // All edges pointing to partition2 should now point to partition1
2154
2155
2156 for (var _i = 0; _i < newEdges.length; _i++) {
2157 var _edge = newEdges[_i];
2158
2159 if (_edge[1] === partition2) {
2160 // Check source
2161 newEdges[_i] = _edge.slice(); // copy
2162
2163 newEdges[_i][1] = partition1;
2164 } else if (_edge[2] === partition2) {
2165 // Check target
2166 newEdges[_i] = _edge.slice(); // copy
2167
2168 newEdges[_i][2] = partition1;
2169 }
2170 } // Move all nodes from partition2 to partition1
2171
2172
2173 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2174 if (nodeMap[_i2] === partition2) {
2175 nodeMap[_i2] = partition1;
2176 }
2177 }
2178
2179 return newEdges;
2180}; // Contracts a graph until we reach a certain number of meta nodes
2181
2182
2183var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2184 while (size > sizeLimit) {
2185 // Choose an edge randomly
2186 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2187
2188 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2189 size--;
2190 }
2191
2192 return remainingEdges;
2193};
2194
2195var elesfn$6 = {
2196 // Computes the minimum cut of an undirected graph
2197 // Returns the correct answer with high probability
2198 kargerStein: function kargerStein() {
2199 var _this = this;
2200
2201 var _this$byGroup = this.byGroup(),
2202 nodes = _this$byGroup.nodes,
2203 edges = _this$byGroup.edges;
2204
2205 edges.unmergeBy(function (edge) {
2206 return edge.isLoop();
2207 });
2208 var numNodes = nodes.length;
2209 var numEdges = edges.length;
2210 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2211 var stopSize = Math.floor(numNodes / sqrt2);
2212
2213 if (numNodes < 2) {
2214 error('At least 2 nodes are required for Karger-Stein algorithm');
2215 return undefined;
2216 } // Now store edge destination as indexes
2217 // Format for each edge (edge index, source node index, target node index)
2218
2219
2220 var edgeIndexes = [];
2221
2222 for (var i = 0; i < numEdges; i++) {
2223 var e = edges[i];
2224 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2225 } // We will store the best cut found here
2226
2227
2228 var minCutSize = Infinity;
2229 var minCutEdgeIndexes = [];
2230 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2231
2232 var metaNodeMap = new Array(numNodes);
2233 var metaNodeMap2 = new Array(numNodes);
2234
2235 var copyNodesMap = function copyNodesMap(from, to) {
2236 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2237 to[_i3] = from[_i3];
2238 }
2239 }; // Main loop
2240
2241
2242 for (var iter = 0; iter <= numIter; iter++) {
2243 // Reset meta node partition
2244 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2245 metaNodeMap[_i4] = _i4;
2246 } // Contract until stop point (stopSize nodes)
2247
2248
2249 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2250 var edgesState2 = edgesState.slice(); // copy
2251 // Create a copy of the colapsed nodes state
2252
2253 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2254
2255 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2256 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2257
2258 if (res1.length <= res2.length && res1.length < minCutSize) {
2259 minCutSize = res1.length;
2260 minCutEdgeIndexes = res1;
2261 copyNodesMap(metaNodeMap, minCutNodeMap);
2262 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2263 minCutSize = res2.length;
2264 minCutEdgeIndexes = res2;
2265 copyNodesMap(metaNodeMap2, minCutNodeMap);
2266 }
2267 } // end of main loop
2268 // Construct result
2269
2270
2271 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2272 return edges[e[0]];
2273 }));
2274 var partition1 = this.spawn();
2275 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2276
2277 var witnessNodePartition = minCutNodeMap[0];
2278
2279 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2280 var partitionId = minCutNodeMap[_i5];
2281 var node = nodes[_i5];
2282
2283 if (partitionId === witnessNodePartition) {
2284 partition1.merge(node);
2285 } else {
2286 partition2.merge(node);
2287 }
2288 } // construct components corresponding to each disjoint subset of nodes
2289
2290
2291 var constructComponent = function constructComponent(subset) {
2292 var component = _this.spawn();
2293
2294 subset.forEach(function (node) {
2295 component.merge(node);
2296 node.connectedEdges().forEach(function (edge) {
2297 // ensure edge is within calling collection and edge is not in cut
2298 if (_this.contains(edge) && !cut.contains(edge)) {
2299 component.merge(edge);
2300 }
2301 });
2302 });
2303 return component;
2304 };
2305
2306 var components = [constructComponent(partition1), constructComponent(partition2)];
2307 var ret = {
2308 cut: cut,
2309 components: components,
2310 // n.b. partitions are included to be compatible with the old api spec
2311 // (could be removed in a future major version)
2312 partition1: partition1,
2313 partition2: partition2
2314 };
2315 return ret;
2316 }
2317}; // elesfn
2318
2319var copyPosition = function copyPosition(p) {
2320 return {
2321 x: p.x,
2322 y: p.y
2323 };
2324};
2325var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2326 return {
2327 x: p.x * zoom + pan.x,
2328 y: p.y * zoom + pan.y
2329 };
2330};
2331var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2332 return {
2333 x: (p.x - pan.x) / zoom,
2334 y: (p.y - pan.y) / zoom
2335 };
2336};
2337var array2point = function array2point(arr) {
2338 return {
2339 x: arr[0],
2340 y: arr[1]
2341 };
2342};
2343var min = function min(arr) {
2344 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2345 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2346 var min = Infinity;
2347
2348 for (var i = begin; i < end; i++) {
2349 var val = arr[i];
2350
2351 if (isFinite(val)) {
2352 min = Math.min(val, min);
2353 }
2354 }
2355
2356 return min;
2357};
2358var max = function max(arr) {
2359 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2360 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2361 var max = -Infinity;
2362
2363 for (var i = begin; i < end; i++) {
2364 var val = arr[i];
2365
2366 if (isFinite(val)) {
2367 max = Math.max(val, max);
2368 }
2369 }
2370
2371 return max;
2372};
2373var mean = function mean(arr) {
2374 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2375 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2376 var total = 0;
2377 var n = 0;
2378
2379 for (var i = begin; i < end; i++) {
2380 var val = arr[i];
2381
2382 if (isFinite(val)) {
2383 total += val;
2384 n++;
2385 }
2386 }
2387
2388 return total / n;
2389};
2390var median = function median(arr) {
2391 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2392 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2393 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2394 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2395 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2396
2397 if (copy) {
2398 arr = arr.slice(begin, end);
2399 } else {
2400 if (end < arr.length) {
2401 arr.splice(end, arr.length - end);
2402 }
2403
2404 if (begin > 0) {
2405 arr.splice(0, begin);
2406 }
2407 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2408
2409
2410 var off = 0; // offset from non-finite values
2411
2412 for (var i = arr.length - 1; i >= 0; i--) {
2413 var v = arr[i];
2414
2415 if (includeHoles) {
2416 if (!isFinite(v)) {
2417 arr[i] = -Infinity;
2418 off++;
2419 }
2420 } else {
2421 // just remove it if we don't want to consider holes
2422 arr.splice(i, 1);
2423 }
2424 }
2425
2426 if (sort) {
2427 arr.sort(function (a, b) {
2428 return a - b;
2429 }); // requires copy = true if you don't want to change the orig
2430 }
2431
2432 var len = arr.length;
2433 var mid = Math.floor(len / 2);
2434
2435 if (len % 2 !== 0) {
2436 return arr[mid + 1 + off];
2437 } else {
2438 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2439 }
2440};
2441var deg2rad = function deg2rad(deg) {
2442 return Math.PI * deg / 180;
2443};
2444var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2445 return Math.atan2(dispY, dispX) - Math.PI / 2;
2446};
2447var log2 = Math.log2 || function (n) {
2448 return Math.log(n) / Math.log(2);
2449};
2450var signum = function signum(x) {
2451 if (x > 0) {
2452 return 1;
2453 } else if (x < 0) {
2454 return -1;
2455 } else {
2456 return 0;
2457 }
2458};
2459var dist = function dist(p1, p2) {
2460 return Math.sqrt(sqdist(p1, p2));
2461};
2462var sqdist = function sqdist(p1, p2) {
2463 var dx = p2.x - p1.x;
2464 var dy = p2.y - p1.y;
2465 return dx * dx + dy * dy;
2466};
2467var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2468 var length = v.length; // First, get sum of all elements
2469
2470 var total = 0;
2471
2472 for (var i = 0; i < length; i++) {
2473 total += v[i];
2474 } // Now, divide each by the sum of all elements
2475
2476
2477 for (var _i = 0; _i < length; _i++) {
2478 v[_i] = v[_i] / total;
2479 }
2480
2481 return v;
2482};
2483
2484var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2485 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2486};
2487var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2488 return {
2489 x: qbezierAt(p0.x, p1.x, p2.x, t),
2490 y: qbezierAt(p0.y, p1.y, p2.y, t)
2491 };
2492};
2493var lineAt = function lineAt(p0, p1, t, d) {
2494 var vec = {
2495 x: p1.x - p0.x,
2496 y: p1.y - p0.y
2497 };
2498 var vecDist = dist(p0, p1);
2499 var normVec = {
2500 x: vec.x / vecDist,
2501 y: vec.y / vecDist
2502 };
2503 t = t == null ? 0 : t;
2504 d = d != null ? d : t * vecDist;
2505 return {
2506 x: p0.x + normVec.x * d,
2507 y: p0.y + normVec.y * d
2508 };
2509};
2510var bound = function bound(min, val, max) {
2511 return Math.max(min, Math.min(max, val));
2512}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2513
2514var makeBoundingBox = function makeBoundingBox(bb) {
2515 if (bb == null) {
2516 return {
2517 x1: Infinity,
2518 y1: Infinity,
2519 x2: -Infinity,
2520 y2: -Infinity,
2521 w: 0,
2522 h: 0
2523 };
2524 } else if (bb.x1 != null && bb.y1 != null) {
2525 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2526 return {
2527 x1: bb.x1,
2528 y1: bb.y1,
2529 x2: bb.x2,
2530 y2: bb.y2,
2531 w: bb.x2 - bb.x1,
2532 h: bb.y2 - bb.y1
2533 };
2534 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2535 return {
2536 x1: bb.x1,
2537 y1: bb.y1,
2538 x2: bb.x1 + bb.w,
2539 y2: bb.y1 + bb.h,
2540 w: bb.w,
2541 h: bb.h
2542 };
2543 }
2544 }
2545};
2546var copyBoundingBox = function copyBoundingBox(bb) {
2547 return {
2548 x1: bb.x1,
2549 x2: bb.x2,
2550 w: bb.w,
2551 y1: bb.y1,
2552 y2: bb.y2,
2553 h: bb.h
2554 };
2555};
2556var clearBoundingBox = function clearBoundingBox(bb) {
2557 bb.x1 = Infinity;
2558 bb.y1 = Infinity;
2559 bb.x2 = -Infinity;
2560 bb.y2 = -Infinity;
2561 bb.w = 0;
2562 bb.h = 0;
2563};
2564var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2565 // update bb1 with bb2 bounds
2566 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2567 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2568 bb1.w = bb1.x2 - bb1.x1;
2569 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2570 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2571 bb1.h = bb1.y2 - bb1.y1;
2572};
2573var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2574 bb.x1 = Math.min(bb.x1, x);
2575 bb.x2 = Math.max(bb.x2, x);
2576 bb.w = bb.x2 - bb.x1;
2577 bb.y1 = Math.min(bb.y1, y);
2578 bb.y2 = Math.max(bb.y2, y);
2579 bb.h = bb.y2 - bb.y1;
2580};
2581var expandBoundingBox = function expandBoundingBox(bb) {
2582 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2583 bb.x1 -= padding;
2584 bb.x2 += padding;
2585 bb.y1 -= padding;
2586 bb.y2 += padding;
2587 bb.w = bb.x2 - bb.x1;
2588 bb.h = bb.y2 - bb.y1;
2589 return bb;
2590};
2591var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2592 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2593 var top, right, bottom, left;
2594
2595 if (padding.length === 1) {
2596 top = right = bottom = left = padding[0];
2597 } else if (padding.length === 2) {
2598 top = bottom = padding[0];
2599 left = right = padding[1];
2600 } else if (padding.length === 4) {
2601 var _padding = _slicedToArray(padding, 4);
2602
2603 top = _padding[0];
2604 right = _padding[1];
2605 bottom = _padding[2];
2606 left = _padding[3];
2607 }
2608
2609 bb.x1 -= left;
2610 bb.x2 += right;
2611 bb.y1 -= top;
2612 bb.y2 += bottom;
2613 bb.w = bb.x2 - bb.x1;
2614 bb.h = bb.y2 - bb.y1;
2615 return bb;
2616};
2617
2618var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2619 bb1.x1 = bb2.x1;
2620 bb1.y1 = bb2.y1;
2621 bb1.x2 = bb2.x2;
2622 bb1.y2 = bb2.y2;
2623 bb1.w = bb1.x2 - bb1.x1;
2624 bb1.h = bb1.y2 - bb1.y1;
2625};
2626var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2627 // case: one bb to right of other
2628 if (bb1.x1 > bb2.x2) {
2629 return false;
2630 }
2631
2632 if (bb2.x1 > bb1.x2) {
2633 return false;
2634 } // case: one bb to left of other
2635
2636
2637 if (bb1.x2 < bb2.x1) {
2638 return false;
2639 }
2640
2641 if (bb2.x2 < bb1.x1) {
2642 return false;
2643 } // case: one bb above other
2644
2645
2646 if (bb1.y2 < bb2.y1) {
2647 return false;
2648 }
2649
2650 if (bb2.y2 < bb1.y1) {
2651 return false;
2652 } // case: one bb below other
2653
2654
2655 if (bb1.y1 > bb2.y2) {
2656 return false;
2657 }
2658
2659 if (bb2.y1 > bb1.y2) {
2660 return false;
2661 } // otherwise, must have some overlap
2662
2663
2664 return true;
2665};
2666var inBoundingBox = function inBoundingBox(bb, x, y) {
2667 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2668};
2669var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2670 return inBoundingBox(bb, pt.x, pt.y);
2671};
2672var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2673 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2674};
2675var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2676 var cornerRadius = getRoundRectangleRadius(width, height);
2677 var halfWidth = width / 2;
2678 var halfHeight = height / 2; // Check intersections with straight line segments
2679
2680 var straightLineIntersections; // Top segment, left to right
2681
2682 {
2683 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2684 var topStartY = nodeY - halfHeight - padding;
2685 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2686 var topEndY = topStartY;
2687 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2688
2689 if (straightLineIntersections.length > 0) {
2690 return straightLineIntersections;
2691 }
2692 } // Right segment, top to bottom
2693
2694 {
2695 var rightStartX = nodeX + halfWidth + padding;
2696 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2697 var rightEndX = rightStartX;
2698 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2699 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2700
2701 if (straightLineIntersections.length > 0) {
2702 return straightLineIntersections;
2703 }
2704 } // Bottom segment, left to right
2705
2706 {
2707 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2708 var bottomStartY = nodeY + halfHeight + padding;
2709 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2710 var bottomEndY = bottomStartY;
2711 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2712
2713 if (straightLineIntersections.length > 0) {
2714 return straightLineIntersections;
2715 }
2716 } // Left segment, top to bottom
2717
2718 {
2719 var leftStartX = nodeX - halfWidth - padding;
2720 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2721 var leftEndX = leftStartX;
2722 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2723 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2724
2725 if (straightLineIntersections.length > 0) {
2726 return straightLineIntersections;
2727 }
2728 } // Check intersections with arc segments
2729
2730 var arcIntersections; // Top Left
2731
2732 {
2733 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2734 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2735 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2736
2737 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2738 return [arcIntersections[0], arcIntersections[1]];
2739 }
2740 } // Top Right
2741
2742 {
2743 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2744 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2745 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2746
2747 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2748 return [arcIntersections[0], arcIntersections[1]];
2749 }
2750 } // Bottom Right
2751
2752 {
2753 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2754 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2755 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2756
2757 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2758 return [arcIntersections[0], arcIntersections[1]];
2759 }
2760 } // Bottom Left
2761
2762 {
2763 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2764 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2765 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2766
2767 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2768 return [arcIntersections[0], arcIntersections[1]];
2769 }
2770 }
2771 return []; // if nothing
2772};
2773var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2774 var t = tolerance;
2775 var x1 = Math.min(lx1, lx2);
2776 var x2 = Math.max(lx1, lx2);
2777 var y1 = Math.min(ly1, ly2);
2778 var y2 = Math.max(ly1, ly2);
2779 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2780};
2781var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2782 var bb = {
2783 x1: Math.min(x1, x3, x2) - tolerance,
2784 x2: Math.max(x1, x3, x2) + tolerance,
2785 y1: Math.min(y1, y3, y2) - tolerance,
2786 y2: Math.max(y1, y3, y2) + tolerance
2787 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2788
2789 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2790 // console.log('bezier out of rough bb')
2791 return false;
2792 } else {
2793 // console.log('do more expensive check');
2794 return true;
2795 }
2796};
2797var solveQuadratic = function solveQuadratic(a, b, c, val) {
2798 c -= val;
2799 var r = b * b - 4 * a * c;
2800
2801 if (r < 0) {
2802 return [];
2803 }
2804
2805 var sqrtR = Math.sqrt(r);
2806 var denom = 2 * a;
2807 var root1 = (-b + sqrtR) / denom;
2808 var root2 = (-b - sqrtR) / denom;
2809 return [root1, root2];
2810};
2811var solveCubic = function solveCubic(a, b, c, d, result) {
2812 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2813 // r is the real component, i is the imaginary component
2814 // An implementation of the Cardano method from the year 1545
2815 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2816 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2817
2818 if (a === 0) {
2819 a = epsilon;
2820 }
2821
2822 b /= a;
2823 c /= a;
2824 d /= a;
2825 var discriminant, q, r, dum1, s, t, term1, r13;
2826 q = (3.0 * c - b * b) / 9.0;
2827 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2828 r /= 54.0;
2829 discriminant = q * q * q + r * r;
2830 result[1] = 0;
2831 term1 = b / 3.0;
2832
2833 if (discriminant > 0) {
2834 s = r + Math.sqrt(discriminant);
2835 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2836 t = r - Math.sqrt(discriminant);
2837 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2838 result[0] = -term1 + s + t;
2839 term1 += (s + t) / 2.0;
2840 result[4] = result[2] = -term1;
2841 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2842 result[3] = term1;
2843 result[5] = -term1;
2844 return;
2845 }
2846
2847 result[5] = result[3] = 0;
2848
2849 if (discriminant === 0) {
2850 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2851 result[0] = -term1 + 2.0 * r13;
2852 result[4] = result[2] = -(r13 + term1);
2853 return;
2854 }
2855
2856 q = -q;
2857 dum1 = q * q * q;
2858 dum1 = Math.acos(r / Math.sqrt(dum1));
2859 r13 = 2.0 * Math.sqrt(q);
2860 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2861 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2862 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2863 return;
2864};
2865var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2866 // Find minimum distance by using the minimum of the distance
2867 // function between the given point and the curve
2868 // This gives the coefficients of the resulting cubic equation
2869 // whose roots tell us where a possible minimum is
2870 // (Coefficients are divided by 4)
2871 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;
2872 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;
2873 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;
2874 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);
2875
2876 var roots = []; // Use the cubic solving algorithm
2877
2878 solveCubic(a, b, c, d, roots);
2879 var zeroThreshold = 0.0000001;
2880 var params = [];
2881
2882 for (var index = 0; index < 6; index += 2) {
2883 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2884 params.push(roots[index]);
2885 }
2886 }
2887
2888 params.push(1.0);
2889 params.push(0.0);
2890 var minDistanceSquared = -1;
2891 var curX, curY, distSquared;
2892
2893 for (var i = 0; i < params.length; i++) {
2894 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2895 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2896 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2897
2898 if (minDistanceSquared >= 0) {
2899 if (distSquared < minDistanceSquared) {
2900 minDistanceSquared = distSquared;
2901 }
2902 } else {
2903 minDistanceSquared = distSquared;
2904 }
2905 }
2906
2907 return minDistanceSquared;
2908};
2909var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2910 var offset = [x - x1, y - y1];
2911 var line = [x2 - x1, y2 - y1];
2912 var lineSq = line[0] * line[0] + line[1] * line[1];
2913 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2914 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2915 var adjSq = dotProduct * dotProduct / lineSq;
2916
2917 if (dotProduct < 0) {
2918 return hypSq;
2919 }
2920
2921 if (adjSq > lineSq) {
2922 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2923 }
2924
2925 return hypSq - adjSq;
2926};
2927var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2928 var x1, y1, x2, y2;
2929 var y3; // Intersect with vertical line through (x, y)
2930
2931 var up = 0; // let down = 0;
2932
2933 for (var i = 0; i < points.length / 2; i++) {
2934 x1 = points[i * 2];
2935 y1 = points[i * 2 + 1];
2936
2937 if (i + 1 < points.length / 2) {
2938 x2 = points[(i + 1) * 2];
2939 y2 = points[(i + 1) * 2 + 1];
2940 } else {
2941 x2 = points[(i + 1 - points.length / 2) * 2];
2942 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2943 }
2944
2945 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2946 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2947
2948 if (y3 > y) {
2949 up++;
2950 } // if( y3 < y ){
2951 // down++;
2952 // }
2953
2954 } else {
2955 continue;
2956 }
2957 }
2958
2959 if (up % 2 === 0) {
2960 return false;
2961 } else {
2962 return true;
2963 }
2964};
2965var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2966 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2967
2968 var angle;
2969
2970 if (direction[0] != null) {
2971 angle = Math.atan(direction[1] / direction[0]);
2972
2973 if (direction[0] < 0) {
2974 angle = angle + Math.PI / 2;
2975 } else {
2976 angle = -angle - Math.PI / 2;
2977 }
2978 } else {
2979 angle = direction;
2980 }
2981
2982 var cos = Math.cos(-angle);
2983 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2984
2985 for (var i = 0; i < transformedPoints.length / 2; i++) {
2986 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2987 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2988 transformedPoints[i * 2] += centerX;
2989 transformedPoints[i * 2 + 1] += centerY;
2990 }
2991
2992 var points;
2993
2994 if (padding > 0) {
2995 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2996 points = joinLines(expandedLineSet);
2997 } else {
2998 points = transformedPoints;
2999 }
3000
3001 return pointInsidePolygonPoints(x, y, points);
3002};
3003var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
3004 var cutPolygonPoints = new Array(basePoints.length);
3005 var halfW = width / 2;
3006 var halfH = height / 2;
3007 var cornerRadius = getRoundPolygonRadius(width, height);
3008 var squaredCornerRadius = cornerRadius * cornerRadius;
3009
3010 for (var i = 0; i < basePoints.length / 4; i++) {
3011 var sourceUv = void 0,
3012 destUv = void 0;
3013
3014 if (i === 0) {
3015 sourceUv = basePoints.length - 2;
3016 } else {
3017 sourceUv = i * 4 - 2;
3018 }
3019
3020 destUv = i * 4 + 2;
3021 var px = centerX + halfW * basePoints[i * 4];
3022 var py = centerY + halfH * basePoints[i * 4 + 1];
3023 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3024 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3025 var cp0x = px - offset * basePoints[sourceUv];
3026 var cp0y = py - offset * basePoints[sourceUv + 1];
3027 var cp1x = px + offset * basePoints[destUv];
3028 var cp1y = py + offset * basePoints[destUv + 1];
3029 cutPolygonPoints[i * 4] = cp0x;
3030 cutPolygonPoints[i * 4 + 1] = cp0y;
3031 cutPolygonPoints[i * 4 + 2] = cp1x;
3032 cutPolygonPoints[i * 4 + 3] = cp1y;
3033 var orthx = basePoints[sourceUv + 1];
3034 var orthy = -basePoints[sourceUv];
3035 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3036
3037 if (cosAlpha < 0) {
3038 orthx *= -1;
3039 orthy *= -1;
3040 }
3041
3042 var cx = cp0x + orthx * cornerRadius;
3043 var cy = cp0y + orthy * cornerRadius;
3044 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
3045
3046 if (squaredDistance <= squaredCornerRadius) {
3047 return true;
3048 }
3049 }
3050
3051 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3052};
3053var joinLines = function joinLines(lineSet) {
3054 var vertices = new Array(lineSet.length / 2);
3055 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3056 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3057
3058 for (var i = 0; i < lineSet.length / 4; i++) {
3059 currentLineStartX = lineSet[i * 4];
3060 currentLineStartY = lineSet[i * 4 + 1];
3061 currentLineEndX = lineSet[i * 4 + 2];
3062 currentLineEndY = lineSet[i * 4 + 3];
3063
3064 if (i < lineSet.length / 4 - 1) {
3065 nextLineStartX = lineSet[(i + 1) * 4];
3066 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3067 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3068 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3069 } else {
3070 nextLineStartX = lineSet[0];
3071 nextLineStartY = lineSet[1];
3072 nextLineEndX = lineSet[2];
3073 nextLineEndY = lineSet[3];
3074 }
3075
3076 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3077 vertices[i * 2] = intersection[0];
3078 vertices[i * 2 + 1] = intersection[1];
3079 }
3080
3081 return vertices;
3082};
3083var expandPolygon = function expandPolygon(points, pad) {
3084 var expandedLineSet = new Array(points.length * 2);
3085 var currentPointX, currentPointY, nextPointX, nextPointY;
3086
3087 for (var i = 0; i < points.length / 2; i++) {
3088 currentPointX = points[i * 2];
3089 currentPointY = points[i * 2 + 1];
3090
3091 if (i < points.length / 2 - 1) {
3092 nextPointX = points[(i + 1) * 2];
3093 nextPointY = points[(i + 1) * 2 + 1];
3094 } else {
3095 nextPointX = points[0];
3096 nextPointY = points[1];
3097 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3098 // Assume CCW polygon winding
3099
3100
3101 var offsetX = nextPointY - currentPointY;
3102 var offsetY = -(nextPointX - currentPointX); // Normalize
3103
3104 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3105 var normalizedOffsetX = offsetX / offsetLength;
3106 var normalizedOffsetY = offsetY / offsetLength;
3107 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3108 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3109 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3110 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3111 }
3112
3113 return expandedLineSet;
3114};
3115var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3116 var dispX = centerX - x;
3117 var dispY = centerY - y;
3118 dispX /= ellipseWradius;
3119 dispY /= ellipseHradius;
3120 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3121 var newLength = len - 1;
3122
3123 if (newLength < 0) {
3124 return [];
3125 }
3126
3127 var lenProportion = newLength / len;
3128 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3129};
3130var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3131 x -= centerX;
3132 y -= centerY;
3133 x /= width / 2 + padding;
3134 y /= height / 2 + padding;
3135 return x * x + y * y <= 1;
3136}; // Returns intersections of increasing distance from line's start point
3137
3138var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3139 // Calculate d, direction vector of line
3140 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3141
3142 var f = [x1 - centerX, y1 - centerY];
3143 var a = d[0] * d[0] + d[1] * d[1];
3144 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3145 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3146 var discriminant = b * b - 4 * a * c;
3147
3148 if (discriminant < 0) {
3149 return [];
3150 }
3151
3152 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3153 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3154 var tMin = Math.min(t1, t2);
3155 var tMax = Math.max(t1, t2);
3156 var inRangeParams = [];
3157
3158 if (tMin >= 0 && tMin <= 1) {
3159 inRangeParams.push(tMin);
3160 }
3161
3162 if (tMax >= 0 && tMax <= 1) {
3163 inRangeParams.push(tMax);
3164 }
3165
3166 if (inRangeParams.length === 0) {
3167 return [];
3168 }
3169
3170 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3171 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3172
3173 if (inRangeParams.length > 1) {
3174 if (inRangeParams[0] == inRangeParams[1]) {
3175 return [nearIntersectionX, nearIntersectionY];
3176 } else {
3177 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3178 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3179 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3180 }
3181 } else {
3182 return [nearIntersectionX, nearIntersectionY];
3183 }
3184};
3185var midOfThree = function midOfThree(a, b, c) {
3186 if (b <= a && a <= c || c <= a && a <= b) {
3187 return a;
3188 } else if (a <= b && b <= c || c <= b && b <= a) {
3189 return b;
3190 } else {
3191 return c;
3192 }
3193}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3194
3195var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3196 var dx13 = x1 - x3;
3197 var dx21 = x2 - x1;
3198 var dx43 = x4 - x3;
3199 var dy13 = y1 - y3;
3200 var dy21 = y2 - y1;
3201 var dy43 = y4 - y3;
3202 var ua_t = dx43 * dy13 - dy43 * dx13;
3203 var ub_t = dx21 * dy13 - dy21 * dx13;
3204 var u_b = dy43 * dx21 - dx43 * dy21;
3205
3206 if (u_b !== 0) {
3207 var ua = ua_t / u_b;
3208 var ub = ub_t / u_b;
3209 var flptThreshold = 0.001;
3210
3211 var _min = 0 - flptThreshold;
3212
3213 var _max = 1 + flptThreshold;
3214
3215 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3216 return [x1 + ua * dx21, y1 + ua * dy21];
3217 } else {
3218 if (!infiniteLines) {
3219 return [];
3220 } else {
3221 return [x1 + ua * dx21, y1 + ua * dy21];
3222 }
3223 }
3224 } else {
3225 if (ua_t === 0 || ub_t === 0) {
3226 // Parallel, coincident lines. Check if overlap
3227 // Check endpoint of second line
3228 if (midOfThree(x1, x2, x4) === x4) {
3229 return [x4, y4];
3230 } // Check start point of second line
3231
3232
3233 if (midOfThree(x1, x2, x3) === x3) {
3234 return [x3, y3];
3235 } // Endpoint of first line
3236
3237
3238 if (midOfThree(x3, x4, x2) === x2) {
3239 return [x2, y2];
3240 }
3241
3242 return [];
3243 } else {
3244 // Parallel, non-coincident
3245 return [];
3246 }
3247 }
3248}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3249// intersect a node polygon (pts transformed)
3250//
3251// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3252// intersect the points (no transform)
3253
3254var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3255 var intersections = [];
3256 var intersection;
3257 var transformedPoints = new Array(basePoints.length);
3258 var doTransform = true;
3259
3260 if (width == null) {
3261 doTransform = false;
3262 }
3263
3264 var points;
3265
3266 if (doTransform) {
3267 for (var i = 0; i < transformedPoints.length / 2; i++) {
3268 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3269 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3270 }
3271
3272 if (padding > 0) {
3273 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3274 points = joinLines(expandedLineSet);
3275 } else {
3276 points = transformedPoints;
3277 }
3278 } else {
3279 points = basePoints;
3280 }
3281
3282 var currentX, currentY, nextX, nextY;
3283
3284 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3285 currentX = points[_i2 * 2];
3286 currentY = points[_i2 * 2 + 1];
3287
3288 if (_i2 < points.length / 2 - 1) {
3289 nextX = points[(_i2 + 1) * 2];
3290 nextY = points[(_i2 + 1) * 2 + 1];
3291 } else {
3292 nextX = points[0];
3293 nextY = points[1];
3294 }
3295
3296 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3297
3298 if (intersection.length !== 0) {
3299 intersections.push(intersection[0], intersection[1]);
3300 }
3301 }
3302
3303 return intersections;
3304};
3305var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3306 var intersections = [];
3307 var intersection;
3308 var lines = new Array(basePoints.length);
3309 var halfW = width / 2;
3310 var halfH = height / 2;
3311 var cornerRadius = getRoundPolygonRadius(width, height);
3312
3313 for (var i = 0; i < basePoints.length / 4; i++) {
3314 var sourceUv = void 0,
3315 destUv = void 0;
3316
3317 if (i === 0) {
3318 sourceUv = basePoints.length - 2;
3319 } else {
3320 sourceUv = i * 4 - 2;
3321 }
3322
3323 destUv = i * 4 + 2;
3324 var px = centerX + halfW * basePoints[i * 4];
3325 var py = centerY + halfH * basePoints[i * 4 + 1];
3326 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3327 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3328 var cp0x = px - offset * basePoints[sourceUv];
3329 var cp0y = py - offset * basePoints[sourceUv + 1];
3330 var cp1x = px + offset * basePoints[destUv];
3331 var cp1y = py + offset * basePoints[destUv + 1];
3332
3333 if (i === 0) {
3334 lines[basePoints.length - 2] = cp0x;
3335 lines[basePoints.length - 1] = cp0y;
3336 } else {
3337 lines[i * 4 - 2] = cp0x;
3338 lines[i * 4 - 1] = cp0y;
3339 }
3340
3341 lines[i * 4] = cp1x;
3342 lines[i * 4 + 1] = cp1y;
3343 var orthx = basePoints[sourceUv + 1];
3344 var orthy = -basePoints[sourceUv];
3345 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3346
3347 if (cosAlpha < 0) {
3348 orthx *= -1;
3349 orthy *= -1;
3350 }
3351
3352 var cx = cp0x + orthx * cornerRadius;
3353 var cy = cp0y + orthy * cornerRadius;
3354 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3355
3356 if (intersection.length !== 0) {
3357 intersections.push(intersection[0], intersection[1]);
3358 }
3359 }
3360
3361 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3362 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3363
3364 if (intersection.length !== 0) {
3365 intersections.push(intersection[0], intersection[1]);
3366 }
3367 }
3368
3369 if (intersections.length > 2) {
3370 var lowestIntersection = [intersections[0], intersections[1]];
3371 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3372
3373 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3374 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3375
3376 if (squaredDistance <= lowestSquaredDistance) {
3377 lowestIntersection[0] = intersections[_i4 * 2];
3378 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3379 lowestSquaredDistance = squaredDistance;
3380 }
3381 }
3382
3383 return lowestIntersection;
3384 }
3385
3386 return intersections;
3387};
3388var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3389 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3390 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3391 var lenRatio = (length - amount) / length;
3392
3393 if (lenRatio < 0) {
3394 lenRatio = 0.00001;
3395 }
3396
3397 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3398};
3399var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3400 var points = generateUnitNgonPoints(sides, rotationRadians);
3401 points = fitPolygonToSquare(points);
3402 return points;
3403};
3404var fitPolygonToSquare = function fitPolygonToSquare(points) {
3405 var x, y;
3406 var sides = points.length / 2;
3407 var minX = Infinity,
3408 minY = Infinity,
3409 maxX = -Infinity,
3410 maxY = -Infinity;
3411
3412 for (var i = 0; i < sides; i++) {
3413 x = points[2 * i];
3414 y = points[2 * i + 1];
3415 minX = Math.min(minX, x);
3416 maxX = Math.max(maxX, x);
3417 minY = Math.min(minY, y);
3418 maxY = Math.max(maxY, y);
3419 } // stretch factors
3420
3421
3422 var sx = 2 / (maxX - minX);
3423 var sy = 2 / (maxY - minY);
3424
3425 for (var _i5 = 0; _i5 < sides; _i5++) {
3426 x = points[2 * _i5] = points[2 * _i5] * sx;
3427 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3428 minX = Math.min(minX, x);
3429 maxX = Math.max(maxX, x);
3430 minY = Math.min(minY, y);
3431 maxY = Math.max(maxY, y);
3432 }
3433
3434 if (minY < -1) {
3435 for (var _i6 = 0; _i6 < sides; _i6++) {
3436 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3437 }
3438 }
3439
3440 return points;
3441};
3442var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3443 var increment = 1.0 / sides * 2 * Math.PI;
3444 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3445 startAngle += rotationRadians;
3446 var points = new Array(sides * 2);
3447 var currentAngle;
3448
3449 for (var i = 0; i < sides; i++) {
3450 currentAngle = i * increment + startAngle;
3451 points[2 * i] = Math.cos(currentAngle); // x
3452
3453 points[2 * i + 1] = Math.sin(-currentAngle); // y
3454 }
3455
3456 return points;
3457}; // Set the default radius, unless half of width or height is smaller than default
3458
3459var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3460 return Math.min(width / 4, height / 4, 8);
3461}; // Set the default radius
3462
3463var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3464 return Math.min(width / 10, height / 10, 8);
3465};
3466var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3467 return 8;
3468};
3469var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3470 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3471}; // get curve width, height, and control point position offsets as a percentage of node height / width
3472
3473var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3474 return {
3475 heightOffset: Math.min(15, 0.05 * height),
3476 widthOffset: Math.min(100, 0.25 * width),
3477 ctrlPtOffsetPct: 0.05
3478 };
3479};
3480
3481var pageRankDefaults = defaults({
3482 dampingFactor: 0.8,
3483 precision: 0.000001,
3484 iterations: 200,
3485 weight: function weight(edge) {
3486 return 1;
3487 }
3488});
3489var elesfn$7 = {
3490 pageRank: function pageRank(options) {
3491 var _pageRankDefaults = pageRankDefaults(options),
3492 dampingFactor = _pageRankDefaults.dampingFactor,
3493 precision = _pageRankDefaults.precision,
3494 iterations = _pageRankDefaults.iterations,
3495 weight = _pageRankDefaults.weight;
3496
3497 var cy = this._private.cy;
3498
3499 var _this$byGroup = this.byGroup(),
3500 nodes = _this$byGroup.nodes,
3501 edges = _this$byGroup.edges;
3502
3503 var numNodes = nodes.length;
3504 var numNodesSqd = numNodes * numNodes;
3505 var numEdges = edges.length; // Construct transposed adjacency matrix
3506 // First lets have a zeroed matrix of the right size
3507 // We'll also keep track of the sum of each column
3508
3509 var matrix = new Array(numNodesSqd);
3510 var columnSum = new Array(numNodes);
3511 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3512
3513 for (var i = 0; i < numNodes; i++) {
3514 for (var j = 0; j < numNodes; j++) {
3515 var n = i * numNodes + j;
3516 matrix[n] = 0;
3517 }
3518
3519 columnSum[i] = 0;
3520 } // Now, process edges
3521
3522
3523 for (var _i = 0; _i < numEdges; _i++) {
3524 var edge = edges[_i];
3525 var srcId = edge.data('source');
3526 var tgtId = edge.data('target'); // Don't include loops in the matrix
3527
3528 if (srcId === tgtId) {
3529 continue;
3530 }
3531
3532 var s = nodes.indexOfId(srcId);
3533 var t = nodes.indexOfId(tgtId);
3534 var w = weight(edge);
3535
3536 var _n = t * numNodes + s; // Update matrix
3537
3538
3539 matrix[_n] += w; // Update column sum
3540
3541 columnSum[s] += w;
3542 } // Add additional probability based on damping factor
3543 // Also, take into account columns that have sum = 0
3544
3545
3546 var p = 1.0 / numNodes + additionalProb; // Shorthand
3547 // Traverse matrix, column by column
3548
3549 for (var _j = 0; _j < numNodes; _j++) {
3550 if (columnSum[_j] === 0) {
3551 // No 'links' out from node jth, assume equal probability for each possible node
3552 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3553 var _n2 = _i2 * numNodes + _j;
3554
3555 matrix[_n2] = p;
3556 }
3557 } else {
3558 // Node jth has outgoing link, compute normalized probabilities
3559 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3560 var _n3 = _i3 * numNodes + _j;
3561
3562 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3563 }
3564 }
3565 } // Compute dominant eigenvector using power method
3566
3567
3568 var eigenvector = new Array(numNodes);
3569 var temp = new Array(numNodes);
3570 var previous; // Start with a vector of all 1's
3571 // Also, initialize a null vector which will be used as shorthand
3572
3573 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3574 eigenvector[_i4] = 1;
3575 }
3576
3577 for (var iter = 0; iter < iterations; iter++) {
3578 // Temp array with all 0's
3579 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3580 temp[_i5] = 0;
3581 } // Multiply matrix with previous result
3582
3583
3584 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3585 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3586 var _n4 = _i6 * numNodes + _j2;
3587
3588 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3589 }
3590 }
3591
3592 inPlaceSumNormalize(temp);
3593 previous = eigenvector;
3594 eigenvector = temp;
3595 temp = previous;
3596 var diff = 0; // Compute difference (squared module) of both vectors
3597
3598 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3599 var delta = previous[_i7] - eigenvector[_i7];
3600 diff += delta * delta;
3601 } // If difference is less than the desired threshold, stop iterating
3602
3603
3604 if (diff < precision) {
3605 break;
3606 }
3607 } // Construct result
3608
3609
3610 var res = {
3611 rank: function rank(node) {
3612 node = cy.collection(node)[0];
3613 return eigenvector[nodes.indexOf(node)];
3614 }
3615 };
3616 return res;
3617 } // pageRank
3618
3619}; // elesfn
3620
3621var defaults$1 = defaults({
3622 root: null,
3623 weight: function weight(edge) {
3624 return 1;
3625 },
3626 directed: false,
3627 alpha: 0
3628});
3629var elesfn$8 = {
3630 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3631 options = defaults$1(options);
3632 var cy = this.cy();
3633 var nodes = this.nodes();
3634 var numNodes = nodes.length;
3635
3636 if (!options.directed) {
3637 var degrees = {};
3638 var maxDegree = 0;
3639
3640 for (var i = 0; i < numNodes; i++) {
3641 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3642
3643 options.root = node;
3644 var currDegree = this.degreeCentrality(options);
3645
3646 if (maxDegree < currDegree.degree) {
3647 maxDegree = currDegree.degree;
3648 }
3649
3650 degrees[node.id()] = currDegree.degree;
3651 }
3652
3653 return {
3654 degree: function degree(node) {
3655 if (maxDegree === 0) {
3656 return 0;
3657 }
3658
3659 if (string(node)) {
3660 // from is a selector string
3661 node = cy.filter(node);
3662 }
3663
3664 return degrees[node.id()] / maxDegree;
3665 }
3666 };
3667 } else {
3668 var indegrees = {};
3669 var outdegrees = {};
3670 var maxIndegree = 0;
3671 var maxOutdegree = 0;
3672
3673 for (var _i = 0; _i < numNodes; _i++) {
3674 var _node = nodes[_i];
3675
3676 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3677
3678
3679 options.root = _node;
3680
3681 var _currDegree = this.degreeCentrality(options);
3682
3683 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3684 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3685 indegrees[id] = _currDegree.indegree;
3686 outdegrees[id] = _currDegree.outdegree;
3687 }
3688
3689 return {
3690 indegree: function indegree(node) {
3691 if (maxIndegree == 0) {
3692 return 0;
3693 }
3694
3695 if (string(node)) {
3696 // from is a selector string
3697 node = cy.filter(node);
3698 }
3699
3700 return indegrees[node.id()] / maxIndegree;
3701 },
3702 outdegree: function outdegree(node) {
3703 if (maxOutdegree === 0) {
3704 return 0;
3705 }
3706
3707 if (string(node)) {
3708 // from is a selector string
3709 node = cy.filter(node);
3710 }
3711
3712 return outdegrees[node.id()] / maxOutdegree;
3713 }
3714 };
3715 }
3716 },
3717 // degreeCentralityNormalized
3718 // Implemented from the algorithm in Opsahl's paper
3719 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3720 // check the heading 2 "Degree"
3721 degreeCentrality: function degreeCentrality(options) {
3722 options = defaults$1(options);
3723 var cy = this.cy();
3724 var callingEles = this;
3725 var _options = options,
3726 root = _options.root,
3727 weight = _options.weight,
3728 directed = _options.directed,
3729 alpha = _options.alpha;
3730 root = cy.collection(root)[0];
3731
3732 if (!directed) {
3733 var connEdges = root.connectedEdges().intersection(callingEles);
3734 var k = connEdges.length;
3735 var s = 0; // Now, sum edge weights
3736
3737 for (var i = 0; i < connEdges.length; i++) {
3738 s += weight(connEdges[i]);
3739 }
3740
3741 return {
3742 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3743 };
3744 } else {
3745 var edges = root.connectedEdges();
3746 var incoming = edges.filter(function (edge) {
3747 return edge.target().same(root) && callingEles.has(edge);
3748 });
3749 var outgoing = edges.filter(function (edge) {
3750 return edge.source().same(root) && callingEles.has(edge);
3751 });
3752 var k_in = incoming.length;
3753 var k_out = outgoing.length;
3754 var s_in = 0;
3755 var s_out = 0; // Now, sum incoming edge weights
3756
3757 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3758 s_in += weight(incoming[_i2]);
3759 } // Now, sum outgoing edge weights
3760
3761
3762 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3763 s_out += weight(outgoing[_i3]);
3764 }
3765
3766 return {
3767 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3768 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3769 };
3770 }
3771 } // degreeCentrality
3772
3773}; // elesfn
3774// nice, short mathemathical alias
3775
3776elesfn$8.dc = elesfn$8.degreeCentrality;
3777elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3778
3779var defaults$2 = defaults({
3780 harmonic: true,
3781 weight: function weight() {
3782 return 1;
3783 },
3784 directed: false,
3785 root: null
3786});
3787var elesfn$9 = {
3788 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3789 var _defaults = defaults$2(options),
3790 harmonic = _defaults.harmonic,
3791 weight = _defaults.weight,
3792 directed = _defaults.directed;
3793
3794 var cy = this.cy();
3795 var closenesses = {};
3796 var maxCloseness = 0;
3797 var nodes = this.nodes();
3798 var fw = this.floydWarshall({
3799 weight: weight,
3800 directed: directed
3801 }); // Compute closeness for every node and find the maximum closeness
3802
3803 for (var i = 0; i < nodes.length; i++) {
3804 var currCloseness = 0;
3805 var node_i = nodes[i];
3806
3807 for (var j = 0; j < nodes.length; j++) {
3808 if (i !== j) {
3809 var d = fw.distance(node_i, nodes[j]);
3810
3811 if (harmonic) {
3812 currCloseness += 1 / d;
3813 } else {
3814 currCloseness += d;
3815 }
3816 }
3817 }
3818
3819 if (!harmonic) {
3820 currCloseness = 1 / currCloseness;
3821 }
3822
3823 if (maxCloseness < currCloseness) {
3824 maxCloseness = currCloseness;
3825 }
3826
3827 closenesses[node_i.id()] = currCloseness;
3828 }
3829
3830 return {
3831 closeness: function closeness(node) {
3832 if (maxCloseness == 0) {
3833 return 0;
3834 }
3835
3836 if (string(node)) {
3837 // from is a selector string
3838 node = cy.filter(node)[0].id();
3839 } else {
3840 // from is a node
3841 node = node.id();
3842 }
3843
3844 return closenesses[node] / maxCloseness;
3845 }
3846 };
3847 },
3848 // Implemented from pseudocode from wikipedia
3849 closenessCentrality: function closenessCentrality(options) {
3850 var _defaults2 = defaults$2(options),
3851 root = _defaults2.root,
3852 weight = _defaults2.weight,
3853 directed = _defaults2.directed,
3854 harmonic = _defaults2.harmonic;
3855
3856 root = this.filter(root)[0]; // we need distance from this node to every other node
3857
3858 var dijkstra = this.dijkstra({
3859 root: root,
3860 weight: weight,
3861 directed: directed
3862 });
3863 var totalDistance = 0;
3864 var nodes = this.nodes();
3865
3866 for (var i = 0; i < nodes.length; i++) {
3867 var n = nodes[i];
3868
3869 if (!n.same(root)) {
3870 var d = dijkstra.distanceTo(n);
3871
3872 if (harmonic) {
3873 totalDistance += 1 / d;
3874 } else {
3875 totalDistance += d;
3876 }
3877 }
3878 }
3879
3880 return harmonic ? totalDistance : 1 / totalDistance;
3881 } // closenessCentrality
3882
3883}; // elesfn
3884// nice, short mathemathical alias
3885
3886elesfn$9.cc = elesfn$9.closenessCentrality;
3887elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3888
3889var defaults$3 = defaults({
3890 weight: null,
3891 directed: false
3892});
3893var elesfn$a = {
3894 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3895 betweennessCentrality: function betweennessCentrality(options) {
3896 var _defaults = defaults$3(options),
3897 directed = _defaults.directed,
3898 weight = _defaults.weight;
3899
3900 var weighted = weight != null;
3901 var cy = this.cy(); // starting
3902
3903 var V = this.nodes();
3904 var A = {};
3905 var _C = {};
3906 var max = 0;
3907 var C = {
3908 set: function set(key, val) {
3909 _C[key] = val;
3910
3911 if (val > max) {
3912 max = val;
3913 }
3914 },
3915 get: function get(key) {
3916 return _C[key];
3917 }
3918 }; // A contains the neighborhoods of every node
3919
3920 for (var i = 0; i < V.length; i++) {
3921 var v = V[i];
3922 var vid = v.id();
3923
3924 if (directed) {
3925 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3926 } else {
3927 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3928 }
3929
3930 C.set(vid, 0);
3931 }
3932
3933 var _loop = function _loop(s) {
3934 var sid = V[s].id();
3935 var S = []; // stack
3936
3937 var P = {};
3938 var g = {};
3939 var d = {};
3940 var Q = new Heap(function (a, b) {
3941 return d[a] - d[b];
3942 }); // queue
3943 // init dictionaries
3944
3945 for (var _i = 0; _i < V.length; _i++) {
3946 var _vid = V[_i].id();
3947
3948 P[_vid] = [];
3949 g[_vid] = 0;
3950 d[_vid] = Infinity;
3951 }
3952
3953 g[sid] = 1; // sigma
3954
3955 d[sid] = 0; // distance to s
3956
3957 Q.push(sid);
3958
3959 while (!Q.empty()) {
3960 var _v = Q.pop();
3961
3962 S.push(_v);
3963
3964 if (weighted) {
3965 for (var j = 0; j < A[_v].length; j++) {
3966 var w = A[_v][j];
3967 var vEle = cy.getElementById(_v);
3968 var edge = void 0;
3969
3970 if (vEle.edgesTo(w).length > 0) {
3971 edge = vEle.edgesTo(w)[0];
3972 } else {
3973 edge = w.edgesTo(vEle)[0];
3974 }
3975
3976 var edgeWeight = weight(edge);
3977 w = w.id();
3978
3979 if (d[w] > d[_v] + edgeWeight) {
3980 d[w] = d[_v] + edgeWeight;
3981
3982 if (Q.nodes.indexOf(w) < 0) {
3983 //if w is not in Q
3984 Q.push(w);
3985 } else {
3986 // update position if w is in Q
3987 Q.updateItem(w);
3988 }
3989
3990 g[w] = 0;
3991 P[w] = [];
3992 }
3993
3994 if (d[w] == d[_v] + edgeWeight) {
3995 g[w] = g[w] + g[_v];
3996 P[w].push(_v);
3997 }
3998 }
3999 } else {
4000 for (var _j = 0; _j < A[_v].length; _j++) {
4001 var _w = A[_v][_j].id();
4002
4003 if (d[_w] == Infinity) {
4004 Q.push(_w);
4005 d[_w] = d[_v] + 1;
4006 }
4007
4008 if (d[_w] == d[_v] + 1) {
4009 g[_w] = g[_w] + g[_v];
4010
4011 P[_w].push(_v);
4012 }
4013 }
4014 }
4015 }
4016
4017 var e = {};
4018
4019 for (var _i2 = 0; _i2 < V.length; _i2++) {
4020 e[V[_i2].id()] = 0;
4021 }
4022
4023 while (S.length > 0) {
4024 var _w2 = S.pop();
4025
4026 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4027 var _v2 = P[_w2][_j2];
4028 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4029 }
4030
4031 if (_w2 != V[s].id()) {
4032 C.set(_w2, C.get(_w2) + e[_w2]);
4033 }
4034 }
4035 };
4036
4037 for (var s = 0; s < V.length; s++) {
4038 _loop(s);
4039 }
4040
4041 var ret = {
4042 betweenness: function betweenness(node) {
4043 var id = cy.collection(node).id();
4044 return C.get(id);
4045 },
4046 betweennessNormalized: function betweennessNormalized(node) {
4047 if (max == 0) {
4048 return 0;
4049 }
4050
4051 var id = cy.collection(node).id();
4052 return C.get(id) / max;
4053 }
4054 }; // alias
4055
4056 ret.betweennessNormalised = ret.betweennessNormalized;
4057 return ret;
4058 } // betweennessCentrality
4059
4060}; // elesfn
4061// nice, short mathemathical alias
4062
4063elesfn$a.bc = elesfn$a.betweennessCentrality;
4064
4065// Implemented by Zoe Xi @zoexi for GSOC 2016
4066/* eslint-disable no-unused-vars */
4067
4068var defaults$4 = defaults({
4069 expandFactor: 2,
4070 // affects time of computation and cluster granularity to some extent: M * M
4071 inflateFactor: 2,
4072 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4073 multFactor: 1,
4074 // optional self loops for each node. Use a neutral value to improve cluster computations.
4075 maxIterations: 20,
4076 // maximum number of iterations of the MCL algorithm in a single run
4077 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4078 function (edge) {
4079 return 1;
4080 }]
4081});
4082/* eslint-enable */
4083
4084var setOptions = function setOptions(options) {
4085 return defaults$4(options);
4086};
4087/* eslint-enable */
4088
4089
4090var getSimilarity = function getSimilarity(edge, attributes) {
4091 var total = 0;
4092
4093 for (var i = 0; i < attributes.length; i++) {
4094 total += attributes[i](edge);
4095 }
4096
4097 return total;
4098};
4099
4100var addLoops = function addLoops(M, n, val) {
4101 for (var i = 0; i < n; i++) {
4102 M[i * n + i] = val;
4103 }
4104};
4105
4106var normalize = function normalize(M, n) {
4107 var sum;
4108
4109 for (var col = 0; col < n; col++) {
4110 sum = 0;
4111
4112 for (var row = 0; row < n; row++) {
4113 sum += M[row * n + col];
4114 }
4115
4116 for (var _row = 0; _row < n; _row++) {
4117 M[_row * n + col] = M[_row * n + col] / sum;
4118 }
4119 }
4120}; // TODO: blocked matrix multiplication?
4121
4122
4123var mmult = function mmult(A, B, n) {
4124 var C = new Array(n * n);
4125
4126 for (var i = 0; i < n; i++) {
4127 for (var j = 0; j < n; j++) {
4128 C[i * n + j] = 0;
4129 }
4130
4131 for (var k = 0; k < n; k++) {
4132 for (var _j = 0; _j < n; _j++) {
4133 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4134 }
4135 }
4136 }
4137
4138 return C;
4139};
4140
4141var expand = function expand(M, n, expandFactor
4142/** power **/
4143) {
4144 var _M = M.slice(0);
4145
4146 for (var p = 1; p < expandFactor; p++) {
4147 M = mmult(M, _M, n);
4148 }
4149
4150 return M;
4151};
4152
4153var inflate = function inflate(M, n, inflateFactor
4154/** r **/
4155) {
4156 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4157
4158
4159 for (var i = 0; i < n * n; i++) {
4160 _M[i] = Math.pow(M[i], inflateFactor);
4161 }
4162
4163 normalize(_M, n);
4164 return _M;
4165};
4166
4167var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4168 // Check that both matrices have the same elements (i,j)
4169 for (var i = 0; i < n2; i++) {
4170 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4171
4172 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4173
4174 if (v1 !== v2) {
4175 return false;
4176 }
4177 }
4178
4179 return true;
4180};
4181
4182var assign = function assign(M, n, nodes, cy) {
4183 var clusters = [];
4184
4185 for (var i = 0; i < n; i++) {
4186 var cluster = [];
4187
4188 for (var j = 0; j < n; j++) {
4189 // Row-wise attractors and elements that they attract belong in same cluster
4190 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4191 cluster.push(nodes[j]);
4192 }
4193 }
4194
4195 if (cluster.length !== 0) {
4196 clusters.push(cy.collection(cluster));
4197 }
4198 }
4199
4200 return clusters;
4201};
4202
4203var isDuplicate = function isDuplicate(c1, c2) {
4204 for (var i = 0; i < c1.length; i++) {
4205 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4206 return false;
4207 }
4208 }
4209
4210 return true;
4211};
4212
4213var removeDuplicates = function removeDuplicates(clusters) {
4214 for (var i = 0; i < clusters.length; i++) {
4215 for (var j = 0; j < clusters.length; j++) {
4216 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4217 clusters.splice(j, 1);
4218 }
4219 }
4220 }
4221
4222 return clusters;
4223};
4224
4225var markovClustering = function markovClustering(options) {
4226 var nodes = this.nodes();
4227 var edges = this.edges();
4228 var cy = this.cy(); // Set parameters of algorithm:
4229
4230 var opts = setOptions(options); // Map each node to its position in node array
4231
4232 var id2position = {};
4233
4234 for (var i = 0; i < nodes.length; i++) {
4235 id2position[nodes[i].id()] = i;
4236 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4237
4238
4239 var n = nodes.length,
4240 n2 = n * n;
4241
4242 var M = new Array(n2),
4243 _M;
4244
4245 for (var _i = 0; _i < n2; _i++) {
4246 M[_i] = 0;
4247 }
4248
4249 for (var e = 0; e < edges.length; e++) {
4250 var edge = edges[e];
4251 var _i2 = id2position[edge.source().id()];
4252 var j = id2position[edge.target().id()];
4253 var sim = getSimilarity(edge, opts.attributes);
4254 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4255
4256 M[j * n + _i2] += sim;
4257 } // Begin Markov cluster algorithm
4258 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4259
4260
4261 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4262
4263 normalize(M, n);
4264 var isStillMoving = true;
4265 var iterations = 0;
4266
4267 while (isStillMoving && iterations < opts.maxIterations) {
4268 isStillMoving = false; // Step 3:
4269
4270 _M = expand(M, n, opts.expandFactor); // Step 4:
4271
4272 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4273
4274 if (!hasConverged(M, _M, n2, 4)) {
4275 isStillMoving = true;
4276 }
4277
4278 iterations++;
4279 } // Build clusters from matrix
4280
4281
4282 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4283
4284 clusters = removeDuplicates(clusters);
4285 return clusters;
4286};
4287
4288var markovClustering$1 = {
4289 markovClustering: markovClustering,
4290 mcl: markovClustering
4291};
4292
4293// Common distance metrics for clustering algorithms
4294
4295var identity = function identity(x) {
4296 return x;
4297};
4298
4299var absDiff = function absDiff(p, q) {
4300 return Math.abs(q - p);
4301};
4302
4303var addAbsDiff = function addAbsDiff(total, p, q) {
4304 return total + absDiff(p, q);
4305};
4306
4307var addSquaredDiff = function addSquaredDiff(total, p, q) {
4308 return total + Math.pow(q - p, 2);
4309};
4310
4311var sqrt = function sqrt(x) {
4312 return Math.sqrt(x);
4313};
4314
4315var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4316 return Math.max(currentMax, absDiff(p, q));
4317};
4318
4319var getDistance = function getDistance(length, getP, getQ, init, visit) {
4320 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4321 var ret = init;
4322 var p, q;
4323
4324 for (var dim = 0; dim < length; dim++) {
4325 p = getP(dim);
4326 q = getQ(dim);
4327 ret = visit(ret, p, q);
4328 }
4329
4330 return post(ret);
4331};
4332
4333var distances = {
4334 euclidean: function euclidean(length, getP, getQ) {
4335 if (length >= 2) {
4336 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4337 } else {
4338 // for single attr case, more efficient to avoid sqrt
4339 return getDistance(length, getP, getQ, 0, addAbsDiff);
4340 }
4341 },
4342 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4343 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4344 },
4345 manhattan: function manhattan(length, getP, getQ) {
4346 return getDistance(length, getP, getQ, 0, addAbsDiff);
4347 },
4348 max: function max(length, getP, getQ) {
4349 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4350 }
4351}; // in case the user accidentally doesn't use camel case
4352
4353distances['squared-euclidean'] = distances['squaredEuclidean'];
4354distances['squaredeuclidean'] = distances['squaredEuclidean'];
4355function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4356 var impl;
4357
4358 if (fn(method)) {
4359 impl = method;
4360 } else {
4361 impl = distances[method] || distances.euclidean;
4362 }
4363
4364 if (length === 0 && fn(method)) {
4365 return impl(nodeP, nodeQ);
4366 } else {
4367 return impl(length, getP, getQ, nodeP, nodeQ);
4368 }
4369}
4370
4371var defaults$5 = defaults({
4372 k: 2,
4373 m: 2,
4374 sensitivityThreshold: 0.0001,
4375 distance: 'euclidean',
4376 maxIterations: 10,
4377 attributes: [],
4378 testMode: false,
4379 testCentroids: null
4380});
4381
4382var setOptions$1 = function setOptions(options) {
4383 return defaults$5(options);
4384};
4385/* eslint-enable */
4386
4387
4388var getDist = function getDist(type, node, centroid, attributes, mode) {
4389 var noNodeP = mode !== 'kMedoids';
4390 var getP = noNodeP ? function (i) {
4391 return centroid[i];
4392 } : function (i) {
4393 return attributes[i](centroid);
4394 };
4395
4396 var getQ = function getQ(i) {
4397 return attributes[i](node);
4398 };
4399
4400 var nodeP = centroid;
4401 var nodeQ = node;
4402 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4403};
4404
4405var randomCentroids = function randomCentroids(nodes, k, attributes) {
4406 var ndim = attributes.length;
4407 var min = new Array(ndim);
4408 var max = new Array(ndim);
4409 var centroids = new Array(k);
4410 var centroid = null; // Find min, max values for each attribute dimension
4411
4412 for (var i = 0; i < ndim; i++) {
4413 min[i] = nodes.min(attributes[i]).value;
4414 max[i] = nodes.max(attributes[i]).value;
4415 } // Build k centroids, each represented as an n-dim feature vector
4416
4417
4418 for (var c = 0; c < k; c++) {
4419 centroid = [];
4420
4421 for (var _i = 0; _i < ndim; _i++) {
4422 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4423 }
4424
4425 centroids[c] = centroid;
4426 }
4427
4428 return centroids;
4429};
4430
4431var classify = function classify(node, centroids, distance, attributes, type) {
4432 var min = Infinity;
4433 var index = 0;
4434
4435 for (var i = 0; i < centroids.length; i++) {
4436 var dist = getDist(distance, node, centroids[i], attributes, type);
4437
4438 if (dist < min) {
4439 min = dist;
4440 index = i;
4441 }
4442 }
4443
4444 return index;
4445};
4446
4447var buildCluster = function buildCluster(centroid, nodes, assignment) {
4448 var cluster = [];
4449 var node = null;
4450
4451 for (var n = 0; n < nodes.length; n++) {
4452 node = nodes[n];
4453
4454 if (assignment[node.id()] === centroid) {
4455 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4456 cluster.push(node);
4457 }
4458 }
4459
4460 return cluster;
4461};
4462
4463var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4464 return Math.abs(v2 - v1) <= sensitivityThreshold;
4465};
4466
4467var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4468 for (var i = 0; i < v1.length; i++) {
4469 for (var j = 0; j < v1[i].length; j++) {
4470 var diff = Math.abs(v1[i][j] - v2[i][j]);
4471
4472 if (diff > sensitivityThreshold) {
4473 return false;
4474 }
4475 }
4476 }
4477
4478 return true;
4479};
4480
4481var seenBefore = function seenBefore(node, medoids, n) {
4482 for (var i = 0; i < n; i++) {
4483 if (node === medoids[i]) return true;
4484 }
4485
4486 return false;
4487};
4488
4489var randomMedoids = function randomMedoids(nodes, k) {
4490 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4491 // so we need to check to see if we've already seen or chose this node before.
4492
4493 if (nodes.length < 50) {
4494 // Randomly select k medoids from the n nodes
4495 for (var i = 0; i < k; i++) {
4496 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).
4497 // Instead choose a different random node.
4498
4499 while (seenBefore(node, medoids, i)) {
4500 node = nodes[Math.floor(Math.random() * nodes.length)];
4501 }
4502
4503 medoids[i] = node;
4504 }
4505 } else {
4506 // Relatively large data set, so pretty safe to not check and just select random nodes
4507 for (var _i2 = 0; _i2 < k; _i2++) {
4508 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4509 }
4510 }
4511
4512 return medoids;
4513};
4514
4515var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4516 var cost = 0;
4517
4518 for (var n = 0; n < cluster.length; n++) {
4519 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4520 }
4521
4522 return cost;
4523};
4524
4525var kMeans = function kMeans(options) {
4526 var cy = this.cy();
4527 var nodes = this.nodes();
4528 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4529
4530 var opts = setOptions$1(options); // Begin k-means algorithm
4531
4532 var clusters = new Array(opts.k);
4533 var assignment = {};
4534 var centroids; // Step 1: Initialize centroid positions
4535
4536 if (opts.testMode) {
4537 if (typeof opts.testCentroids === 'number') {
4538 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4539 } else if (_typeof(opts.testCentroids) === 'object') {
4540 centroids = opts.testCentroids;
4541 } else {
4542 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4543 }
4544 } else {
4545 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4546 }
4547
4548 var isStillMoving = true;
4549 var iterations = 0;
4550
4551 while (isStillMoving && iterations < opts.maxIterations) {
4552 // Step 2: Assign nodes to the nearest centroid
4553 for (var n = 0; n < nodes.length; n++) {
4554 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4555
4556 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4557 } // Step 3: For each of the k clusters, update its centroid
4558
4559
4560 isStillMoving = false;
4561
4562 for (var c = 0; c < opts.k; c++) {
4563 // Get all nodes that belong to this cluster
4564 var cluster = buildCluster(c, nodes, assignment);
4565
4566 if (cluster.length === 0) {
4567 // If cluster is empty, break out early & move to next cluster
4568 continue;
4569 } // Update centroids by calculating avg of all nodes within the cluster.
4570
4571
4572 var ndim = opts.attributes.length;
4573 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4574
4575 var newCentroid = new Array(ndim);
4576 var sum = new Array(ndim);
4577
4578 for (var d = 0; d < ndim; d++) {
4579 sum[d] = 0.0;
4580
4581 for (var i = 0; i < cluster.length; i++) {
4582 node = cluster[i];
4583 sum[d] += opts.attributes[d](node);
4584 }
4585
4586 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4587
4588 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4589 isStillMoving = true;
4590 }
4591 }
4592
4593 centroids[c] = newCentroid;
4594 clusters[c] = cy.collection(cluster);
4595 }
4596
4597 iterations++;
4598 }
4599
4600 return clusters;
4601};
4602
4603var kMedoids = function kMedoids(options) {
4604 var cy = this.cy();
4605 var nodes = this.nodes();
4606 var node = null;
4607 var opts = setOptions$1(options); // Begin k-medoids algorithm
4608
4609 var clusters = new Array(opts.k);
4610 var medoids;
4611 var assignment = {};
4612 var curCost;
4613 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4614 // Step 1: Initialize k medoids
4615
4616 if (opts.testMode) {
4617 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4618 medoids = opts.testCentroids;
4619 } else {
4620 medoids = randomMedoids(nodes, opts.k);
4621 }
4622 } else {
4623 medoids = randomMedoids(nodes, opts.k);
4624 }
4625
4626 var isStillMoving = true;
4627 var iterations = 0;
4628
4629 while (isStillMoving && iterations < opts.maxIterations) {
4630 // Step 2: Assign nodes to the nearest medoid
4631 for (var n = 0; n < nodes.length; n++) {
4632 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4633
4634 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4635 }
4636
4637 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4638 // select the node with the lowest configuration cost as new medoid.
4639
4640 for (var m = 0; m < medoids.length; m++) {
4641 // Get all nodes that belong to this medoid
4642 var cluster = buildCluster(m, nodes, assignment);
4643
4644 if (cluster.length === 0) {
4645 // If cluster is empty, break out early & move to next cluster
4646 continue;
4647 }
4648
4649 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4650 // Select different medoid if its configuration has the lowest cost
4651
4652 for (var _n = 0; _n < cluster.length; _n++) {
4653 curCost = findCost(cluster[_n], cluster, opts.attributes);
4654
4655 if (curCost < minCosts[m]) {
4656 minCosts[m] = curCost;
4657 medoids[m] = cluster[_n];
4658 isStillMoving = true;
4659 }
4660 }
4661
4662 clusters[m] = cy.collection(cluster);
4663 }
4664
4665 iterations++;
4666 }
4667
4668 return clusters;
4669};
4670
4671var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4672 var numerator, denominator;
4673
4674 for (var n = 0; n < nodes.length; n++) {
4675 for (var c = 0; c < centroids.length; c++) {
4676 weight[n][c] = Math.pow(U[n][c], opts.m);
4677 }
4678 }
4679
4680 for (var _c = 0; _c < centroids.length; _c++) {
4681 for (var dim = 0; dim < opts.attributes.length; dim++) {
4682 numerator = 0;
4683 denominator = 0;
4684
4685 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4686 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4687 denominator += weight[_n2][_c];
4688 }
4689
4690 centroids[_c][dim] = numerator / denominator;
4691 }
4692 }
4693};
4694
4695var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4696 // Save previous step
4697 for (var i = 0; i < U.length; i++) {
4698 _U[i] = U[i].slice();
4699 }
4700
4701 var sum, numerator, denominator;
4702 var pow = 2 / (opts.m - 1);
4703
4704 for (var c = 0; c < centroids.length; c++) {
4705 for (var n = 0; n < nodes.length; n++) {
4706 sum = 0;
4707
4708 for (var k = 0; k < centroids.length; k++) {
4709 // against all other centroids
4710 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4711 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4712 sum += Math.pow(numerator / denominator, pow);
4713 }
4714
4715 U[n][c] = 1 / sum;
4716 }
4717 }
4718};
4719
4720var assign$1 = function assign(nodes, U, opts, cy) {
4721 var clusters = new Array(opts.k);
4722
4723 for (var c = 0; c < clusters.length; c++) {
4724 clusters[c] = [];
4725 }
4726
4727 var max;
4728 var index;
4729
4730 for (var n = 0; n < U.length; n++) {
4731 // for each node (U is N x C matrix)
4732 max = -Infinity;
4733 index = -1; // Determine which cluster the node is most likely to belong in
4734
4735 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4736 if (U[n][_c2] > max) {
4737 max = U[n][_c2];
4738 index = _c2;
4739 }
4740 }
4741
4742 clusters[index].push(nodes[n]);
4743 } // Turn every array into a collection of nodes
4744
4745
4746 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4747 clusters[_c3] = cy.collection(clusters[_c3]);
4748 }
4749
4750 return clusters;
4751};
4752
4753var fuzzyCMeans = function fuzzyCMeans(options) {
4754 var cy = this.cy();
4755 var nodes = this.nodes();
4756 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4757
4758 var clusters;
4759 var centroids;
4760 var U;
4761
4762 var _U;
4763
4764 var weight; // Step 1: Initialize letiables.
4765
4766 _U = new Array(nodes.length);
4767
4768 for (var i = 0; i < nodes.length; i++) {
4769 // N x C matrix
4770 _U[i] = new Array(opts.k);
4771 }
4772
4773 U = new Array(nodes.length);
4774
4775 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4776 // N x C matrix
4777 U[_i3] = new Array(opts.k);
4778 }
4779
4780 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4781 var total = 0;
4782
4783 for (var j = 0; j < opts.k; j++) {
4784 U[_i4][j] = Math.random();
4785 total += U[_i4][j];
4786 }
4787
4788 for (var _j = 0; _j < opts.k; _j++) {
4789 U[_i4][_j] = U[_i4][_j] / total;
4790 }
4791 }
4792
4793 centroids = new Array(opts.k);
4794
4795 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4796 centroids[_i5] = new Array(opts.attributes.length);
4797 }
4798
4799 weight = new Array(nodes.length);
4800
4801 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4802 // N x C matrix
4803 weight[_i6] = new Array(opts.k);
4804 } // end init FCM
4805
4806
4807 var isStillMoving = true;
4808 var iterations = 0;
4809
4810 while (isStillMoving && iterations < opts.maxIterations) {
4811 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4812
4813 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4814
4815 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4816
4817 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4818 isStillMoving = true;
4819 }
4820
4821 iterations++;
4822 } // Assign nodes to clusters with highest probability.
4823
4824
4825 clusters = assign$1(nodes, U, opts, cy);
4826 return {
4827 clusters: clusters,
4828 degreeOfMembership: U
4829 };
4830};
4831
4832var kClustering = {
4833 kMeans: kMeans,
4834 kMedoids: kMedoids,
4835 fuzzyCMeans: fuzzyCMeans,
4836 fcm: fuzzyCMeans
4837};
4838
4839// Implemented by Zoe Xi @zoexi for GSOC 2016
4840var defaults$6 = defaults({
4841 distance: 'euclidean',
4842 // distance metric to compare nodes
4843 linkage: 'min',
4844 // linkage criterion : how to determine the distance between clusters of nodes
4845 mode: 'threshold',
4846 // mode:'threshold' => clusters must be threshold distance apart
4847 threshold: Infinity,
4848 // the distance threshold
4849 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4850 addDendrogram: false,
4851 // whether to add the dendrogram to the graph for viz
4852 dendrogramDepth: 0,
4853 // depth at which dendrogram branches are merged into the returned clusters
4854 attributes: [] // array of attr functions
4855
4856});
4857var linkageAliases = {
4858 'single': 'min',
4859 'complete': 'max'
4860};
4861
4862var setOptions$2 = function setOptions(options) {
4863 var opts = defaults$6(options);
4864 var preferredAlias = linkageAliases[opts.linkage];
4865
4866 if (preferredAlias != null) {
4867 opts.linkage = preferredAlias;
4868 }
4869
4870 return opts;
4871};
4872
4873var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4874 // Find two closest clusters from cached mins
4875 var minKey = 0;
4876 var min = Infinity;
4877 var dist;
4878 var attrs = opts.attributes;
4879
4880 var getDist = function getDist(n1, n2) {
4881 return clusteringDistance(opts.distance, attrs.length, function (i) {
4882 return attrs[i](n1);
4883 }, function (i) {
4884 return attrs[i](n2);
4885 }, n1, n2);
4886 };
4887
4888 for (var i = 0; i < clusters.length; i++) {
4889 var key = clusters[i].key;
4890 var _dist = dists[key][mins[key]];
4891
4892 if (_dist < min) {
4893 minKey = key;
4894 min = _dist;
4895 }
4896 }
4897
4898 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4899 return false;
4900 }
4901
4902 var c1 = index[minKey];
4903 var c2 = index[mins[minKey]];
4904 var merged; // Merge two closest clusters
4905
4906 if (opts.mode === 'dendrogram') {
4907 merged = {
4908 left: c1,
4909 right: c2,
4910 key: c1.key
4911 };
4912 } else {
4913 merged = {
4914 value: c1.value.concat(c2.value),
4915 key: c1.key
4916 };
4917 }
4918
4919 clusters[c1.index] = merged;
4920 clusters.splice(c2.index, 1);
4921 index[c1.key] = merged; // Update distances with new merged cluster
4922
4923 for (var _i = 0; _i < clusters.length; _i++) {
4924 var cur = clusters[_i];
4925
4926 if (c1.key === cur.key) {
4927 dist = Infinity;
4928 } else if (opts.linkage === 'min') {
4929 dist = dists[c1.key][cur.key];
4930
4931 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4932 dist = dists[c2.key][cur.key];
4933 }
4934 } else if (opts.linkage === 'max') {
4935 dist = dists[c1.key][cur.key];
4936
4937 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4938 dist = dists[c2.key][cur.key];
4939 }
4940 } else if (opts.linkage === 'mean') {
4941 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4942 } else {
4943 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4944 }
4945
4946 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4947 } // Update cached mins
4948
4949
4950 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4951 var key1 = clusters[_i2].key;
4952
4953 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4954 var _min = key1;
4955
4956 for (var j = 0; j < clusters.length; j++) {
4957 var key2 = clusters[j].key;
4958
4959 if (dists[key1][key2] < dists[key1][_min]) {
4960 _min = key2;
4961 }
4962 }
4963
4964 mins[key1] = _min;
4965 }
4966
4967 clusters[_i2].index = _i2;
4968 } // Clean up meta data used for clustering
4969
4970
4971 c1.key = c2.key = c1.index = c2.index = null;
4972 return true;
4973};
4974
4975var getAllChildren = function getAllChildren(root, arr, cy) {
4976 if (!root) return;
4977
4978 if (root.value) {
4979 arr.push(root.value);
4980 } else {
4981 if (root.left) getAllChildren(root.left, arr);
4982 if (root.right) getAllChildren(root.right, arr);
4983 }
4984};
4985
4986var buildDendrogram = function buildDendrogram(root, cy) {
4987 if (!root) return '';
4988
4989 if (root.left && root.right) {
4990 var leftStr = buildDendrogram(root.left, cy);
4991 var rightStr = buildDendrogram(root.right, cy);
4992 var node = cy.add({
4993 group: 'nodes',
4994 data: {
4995 id: leftStr + ',' + rightStr
4996 }
4997 });
4998 cy.add({
4999 group: 'edges',
5000 data: {
5001 source: leftStr,
5002 target: node.id()
5003 }
5004 });
5005 cy.add({
5006 group: 'edges',
5007 data: {
5008 source: rightStr,
5009 target: node.id()
5010 }
5011 });
5012 return node.id();
5013 } else if (root.value) {
5014 return root.value.id();
5015 }
5016};
5017
5018var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5019 if (!root) return [];
5020 var left = [],
5021 right = [],
5022 leaves = [];
5023
5024 if (k === 0) {
5025 // don't cut tree, simply return all nodes as 1 single cluster
5026 if (root.left) getAllChildren(root.left, left);
5027 if (root.right) getAllChildren(root.right, right);
5028 leaves = left.concat(right);
5029 return [cy.collection(leaves)];
5030 } else if (k === 1) {
5031 // cut at root
5032 if (root.value) {
5033 // leaf node
5034 return [cy.collection(root.value)];
5035 } else {
5036 if (root.left) getAllChildren(root.left, left);
5037 if (root.right) getAllChildren(root.right, right);
5038 return [cy.collection(left), cy.collection(right)];
5039 }
5040 } else {
5041 if (root.value) {
5042 return [cy.collection(root.value)];
5043 } else {
5044 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5045 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5046 return left.concat(right);
5047 }
5048 }
5049};
5050/* eslint-enable */
5051
5052
5053var hierarchicalClustering = function hierarchicalClustering(options) {
5054 var cy = this.cy();
5055 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5056
5057 var opts = setOptions$2(options);
5058 var attrs = opts.attributes;
5059
5060 var getDist = function getDist(n1, n2) {
5061 return clusteringDistance(opts.distance, attrs.length, function (i) {
5062 return attrs[i](n1);
5063 }, function (i) {
5064 return attrs[i](n2);
5065 }, n1, n2);
5066 }; // Begin hierarchical algorithm
5067
5068
5069 var clusters = [];
5070 var dists = []; // distances between each pair of clusters
5071
5072 var mins = []; // closest cluster for each cluster
5073
5074 var index = []; // hash of all clusters by key
5075 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5076
5077 for (var n = 0; n < nodes.length; n++) {
5078 var cluster = {
5079 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5080 key: n,
5081 index: n
5082 };
5083 clusters[n] = cluster;
5084 index[n] = cluster;
5085 dists[n] = [];
5086 mins[n] = 0;
5087 } // Calculate the distance between each pair of clusters
5088
5089
5090 for (var i = 0; i < clusters.length; i++) {
5091 for (var j = 0; j <= i; j++) {
5092 var dist = void 0;
5093
5094 if (opts.mode === 'dendrogram') {
5095 // modes store cluster values differently
5096 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5097 } else {
5098 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5099 }
5100
5101 dists[i][j] = dist;
5102 dists[j][i] = dist;
5103
5104 if (dist < dists[i][mins[i]]) {
5105 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5106 }
5107 }
5108 } // Find the closest pair of clusters and merge them into a single cluster.
5109 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5110
5111
5112 var merged = mergeClosest(clusters, index, dists, mins, opts);
5113
5114 while (merged) {
5115 merged = mergeClosest(clusters, index, dists, mins, opts);
5116 }
5117
5118 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5119 // in addition to returning the clusters.
5120
5121 if (opts.mode === 'dendrogram') {
5122 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5123 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5124 } else {
5125 // Regular mode simply returns the clusters
5126 retClusters = new Array(clusters.length);
5127 clusters.forEach(function (cluster, i) {
5128 // Clean up meta data used for clustering
5129 cluster.key = cluster.index = null;
5130 retClusters[i] = cy.collection(cluster.value);
5131 });
5132 }
5133
5134 return retClusters;
5135};
5136
5137var hierarchicalClustering$1 = {
5138 hierarchicalClustering: hierarchicalClustering,
5139 hca: hierarchicalClustering
5140};
5141
5142// Implemented by Zoe Xi @zoexi for GSOC 2016
5143var defaults$7 = defaults({
5144 distance: 'euclidean',
5145 // distance metric to compare attributes between two nodes
5146 preference: 'median',
5147 // suitability of a data point to serve as an exemplar
5148 damping: 0.8,
5149 // damping factor between [0.5, 1)
5150 maxIterations: 1000,
5151 // max number of iterations to run
5152 minIterations: 100,
5153 // min number of iterations to run in order for clustering to stop
5154 attributes: [// functions to quantify the similarity between any two points
5155 // e.g. node => node.data('weight')
5156 ]
5157});
5158
5159var setOptions$3 = function setOptions(options) {
5160 var dmp = options.damping;
5161 var pref = options.preference;
5162
5163 if (!(0.5 <= dmp && dmp < 1)) {
5164 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5165 }
5166
5167 var validPrefs = ['median', 'mean', 'min', 'max'];
5168
5169 if (!(validPrefs.some(function (v) {
5170 return v === pref;
5171 }) || number(pref))) {
5172 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5173 return "'".concat(p, "'");
5174 }).join(', '), "] or a number. Got: ").concat(pref));
5175 }
5176
5177 return defaults$7(options);
5178};
5179/* eslint-enable */
5180
5181
5182var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5183 var attr = function attr(n, i) {
5184 return attributes[i](n);
5185 }; // nb negative because similarity should have an inverse relationship to distance
5186
5187
5188 return -clusteringDistance(type, attributes.length, function (i) {
5189 return attr(n1, i);
5190 }, function (i) {
5191 return attr(n2, i);
5192 }, n1, n2);
5193};
5194
5195var getPreference = function getPreference(S, preference) {
5196 // larger preference = greater # of clusters
5197 var p = null;
5198
5199 if (preference === 'median') {
5200 p = median(S);
5201 } else if (preference === 'mean') {
5202 p = mean(S);
5203 } else if (preference === 'min') {
5204 p = min(S);
5205 } else if (preference === 'max') {
5206 p = max(S);
5207 } else {
5208 // Custom preference number, as set by user
5209 p = preference;
5210 }
5211
5212 return p;
5213};
5214
5215var findExemplars = function findExemplars(n, R, A) {
5216 var indices = [];
5217
5218 for (var i = 0; i < n; i++) {
5219 if (R[i * n + i] + A[i * n + i] > 0) {
5220 indices.push(i);
5221 }
5222 }
5223
5224 return indices;
5225};
5226
5227var assignClusters = function assignClusters(n, S, exemplars) {
5228 var clusters = [];
5229
5230 for (var i = 0; i < n; i++) {
5231 var index = -1;
5232 var max = -Infinity;
5233
5234 for (var ei = 0; ei < exemplars.length; ei++) {
5235 var e = exemplars[ei];
5236
5237 if (S[i * n + e] > max) {
5238 index = e;
5239 max = S[i * n + e];
5240 }
5241 }
5242
5243 if (index > 0) {
5244 clusters.push(index);
5245 }
5246 }
5247
5248 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5249 clusters[exemplars[_ei]] = exemplars[_ei];
5250 }
5251
5252 return clusters;
5253};
5254
5255var assign$2 = function assign(n, S, exemplars) {
5256 var clusters = assignClusters(n, S, exemplars);
5257
5258 for (var ei = 0; ei < exemplars.length; ei++) {
5259 var ii = [];
5260
5261 for (var c = 0; c < clusters.length; c++) {
5262 if (clusters[c] === exemplars[ei]) {
5263 ii.push(c);
5264 }
5265 }
5266
5267 var maxI = -1;
5268 var maxSum = -Infinity;
5269
5270 for (var i = 0; i < ii.length; i++) {
5271 var sum = 0;
5272
5273 for (var j = 0; j < ii.length; j++) {
5274 sum += S[ii[j] * n + ii[i]];
5275 }
5276
5277 if (sum > maxSum) {
5278 maxI = i;
5279 maxSum = sum;
5280 }
5281 }
5282
5283 exemplars[ei] = ii[maxI];
5284 }
5285
5286 clusters = assignClusters(n, S, exemplars);
5287 return clusters;
5288};
5289
5290var affinityPropagation = function affinityPropagation(options) {
5291 var cy = this.cy();
5292 var nodes = this.nodes();
5293 var opts = setOptions$3(options); // Map each node to its position in node array
5294
5295 var id2position = {};
5296
5297 for (var i = 0; i < nodes.length; i++) {
5298 id2position[nodes[i].id()] = i;
5299 } // Begin affinity propagation algorithm
5300
5301
5302 var n; // number of data points
5303
5304 var n2; // size of matrices
5305
5306 var S; // similarity matrix (1D array)
5307
5308 var p; // preference/suitability of a data point to serve as an exemplar
5309
5310 var R; // responsibility matrix (1D array)
5311
5312 var A; // availability matrix (1D array)
5313
5314 n = nodes.length;
5315 n2 = n * n; // Initialize and build S similarity matrix
5316
5317 S = new Array(n2);
5318
5319 for (var _i = 0; _i < n2; _i++) {
5320 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5321 }
5322
5323 for (var _i2 = 0; _i2 < n; _i2++) {
5324 for (var j = 0; j < n; j++) {
5325 if (_i2 !== j) {
5326 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5327 }
5328 }
5329 } // Place preferences on the diagonal of S
5330
5331
5332 p = getPreference(S, opts.preference);
5333
5334 for (var _i3 = 0; _i3 < n; _i3++) {
5335 S[_i3 * n + _i3] = p;
5336 } // Initialize R responsibility matrix
5337
5338
5339 R = new Array(n2);
5340
5341 for (var _i4 = 0; _i4 < n2; _i4++) {
5342 R[_i4] = 0.0;
5343 } // Initialize A availability matrix
5344
5345
5346 A = new Array(n2);
5347
5348 for (var _i5 = 0; _i5 < n2; _i5++) {
5349 A[_i5] = 0.0;
5350 }
5351
5352 var old = new Array(n);
5353 var Rp = new Array(n);
5354 var se = new Array(n);
5355
5356 for (var _i6 = 0; _i6 < n; _i6++) {
5357 old[_i6] = 0.0;
5358 Rp[_i6] = 0.0;
5359 se[_i6] = 0;
5360 }
5361
5362 var e = new Array(n * opts.minIterations);
5363
5364 for (var _i7 = 0; _i7 < e.length; _i7++) {
5365 e[_i7] = 0;
5366 }
5367
5368 var iter;
5369
5370 for (iter = 0; iter < opts.maxIterations; iter++) {
5371 // main algorithmic loop
5372 // Update R responsibility matrix
5373 for (var _i8 = 0; _i8 < n; _i8++) {
5374 var max = -Infinity,
5375 max2 = -Infinity,
5376 maxI = -1,
5377 AS = 0.0;
5378
5379 for (var _j = 0; _j < n; _j++) {
5380 old[_j] = R[_i8 * n + _j];
5381 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5382
5383 if (AS >= max) {
5384 max2 = max;
5385 max = AS;
5386 maxI = _j;
5387 } else if (AS > max2) {
5388 max2 = AS;
5389 }
5390 }
5391
5392 for (var _j2 = 0; _j2 < n; _j2++) {
5393 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5394 }
5395
5396 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5397 } // Update A availability matrix
5398
5399
5400 for (var _i9 = 0; _i9 < n; _i9++) {
5401 var sum = 0;
5402
5403 for (var _j3 = 0; _j3 < n; _j3++) {
5404 old[_j3] = A[_j3 * n + _i9];
5405 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5406 sum += Rp[_j3];
5407 }
5408
5409 sum -= Rp[_i9];
5410 Rp[_i9] = R[_i9 * n + _i9];
5411 sum += Rp[_i9];
5412
5413 for (var _j4 = 0; _j4 < n; _j4++) {
5414 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5415 }
5416
5417 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5418 } // Check for convergence
5419
5420
5421 var K = 0;
5422
5423 for (var _i10 = 0; _i10 < n; _i10++) {
5424 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5425 e[iter % opts.minIterations * n + _i10] = E;
5426 K += E;
5427 }
5428
5429 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5430 var _sum = 0;
5431
5432 for (var _i11 = 0; _i11 < n; _i11++) {
5433 se[_i11] = 0;
5434
5435 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5436 se[_i11] += e[_j5 * n + _i11];
5437 }
5438
5439 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5440 _sum++;
5441 }
5442 }
5443
5444 if (_sum === n) {
5445 // then we have convergence
5446 break;
5447 }
5448 }
5449 } // Identify exemplars (cluster centers)
5450
5451
5452 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5453
5454 var clusterIndices = assign$2(n, S, exemplarsIndices);
5455 var clusters = {};
5456
5457 for (var c = 0; c < exemplarsIndices.length; c++) {
5458 clusters[exemplarsIndices[c]] = [];
5459 }
5460
5461 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5462 var pos = id2position[nodes[_i12].id()];
5463
5464 var clusterIndex = clusterIndices[pos];
5465
5466 if (clusterIndex != null) {
5467 // the node may have not been assigned a cluster if no valid attributes were specified
5468 clusters[clusterIndex].push(nodes[_i12]);
5469 }
5470 }
5471
5472 var retClusters = new Array(exemplarsIndices.length);
5473
5474 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5475 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5476 }
5477
5478 return retClusters;
5479};
5480
5481var affinityPropagation$1 = {
5482 affinityPropagation: affinityPropagation,
5483 ap: affinityPropagation
5484};
5485
5486var hierholzerDefaults = defaults({
5487 root: undefined,
5488 directed: false
5489});
5490var elesfn$b = {
5491 hierholzer: function hierholzer(options) {
5492 if (!plainObject(options)) {
5493 var args = arguments;
5494 options = {
5495 root: args[0],
5496 directed: args[1]
5497 };
5498 }
5499
5500 var _hierholzerDefaults = hierholzerDefaults(options),
5501 root = _hierholzerDefaults.root,
5502 directed = _hierholzerDefaults.directed;
5503
5504 var eles = this;
5505 var dflag = false;
5506 var oddIn;
5507 var oddOut;
5508 var startVertex;
5509 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5510 var nodes = {};
5511 var edges = {};
5512
5513 if (directed) {
5514 eles.forEach(function (ele) {
5515 var id = ele.id();
5516
5517 if (ele.isNode()) {
5518 var ind = ele.indegree(true);
5519 var outd = ele.outdegree(true);
5520 var d1 = ind - outd;
5521 var d2 = outd - ind;
5522
5523 if (d1 == 1) {
5524 if (oddIn) dflag = true;else oddIn = id;
5525 } else if (d2 == 1) {
5526 if (oddOut) dflag = true;else oddOut = id;
5527 } else if (d2 > 1 || d1 > 1) {
5528 dflag = true;
5529 }
5530
5531 nodes[id] = [];
5532 ele.outgoers().forEach(function (e) {
5533 if (e.isEdge()) nodes[id].push(e.id());
5534 });
5535 } else {
5536 edges[id] = [undefined, ele.target().id()];
5537 }
5538 });
5539 } else {
5540 eles.forEach(function (ele) {
5541 var id = ele.id();
5542
5543 if (ele.isNode()) {
5544 var d = ele.degree(true);
5545
5546 if (d % 2) {
5547 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5548 }
5549
5550 nodes[id] = [];
5551 ele.connectedEdges().forEach(function (e) {
5552 return nodes[id].push(e.id());
5553 });
5554 } else {
5555 edges[id] = [ele.source().id(), ele.target().id()];
5556 }
5557 });
5558 }
5559
5560 var result = {
5561 found: false,
5562 trail: undefined
5563 };
5564 if (dflag) return result;else if (oddOut && oddIn) {
5565 if (directed) {
5566 if (startVertex && oddOut != startVertex) {
5567 return result;
5568 }
5569
5570 startVertex = oddOut;
5571 } else {
5572 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5573 return result;
5574 } else if (!startVertex) {
5575 startVertex = oddOut;
5576 }
5577 }
5578 } else {
5579 if (!startVertex) startVertex = eles[0].id();
5580 }
5581
5582 var walk = function walk(v) {
5583 var currentNode = v;
5584 var subtour = [v];
5585 var adj, adjTail, adjHead;
5586
5587 while (nodes[currentNode].length) {
5588 adj = nodes[currentNode].shift();
5589 adjTail = edges[adj][0];
5590 adjHead = edges[adj][1];
5591
5592 if (currentNode != adjHead) {
5593 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5594 return e != adj;
5595 });
5596 currentNode = adjHead;
5597 } else if (!directed && currentNode != adjTail) {
5598 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5599 return e != adj;
5600 });
5601 currentNode = adjTail;
5602 }
5603
5604 subtour.unshift(adj);
5605 subtour.unshift(currentNode);
5606 }
5607
5608 return subtour;
5609 };
5610
5611 var trail = [];
5612 var subtour = [];
5613 subtour = walk(startVertex);
5614
5615 while (subtour.length != 1) {
5616 if (nodes[subtour[0]].length == 0) {
5617 trail.unshift(eles.getElementById(subtour.shift()));
5618 trail.unshift(eles.getElementById(subtour.shift()));
5619 } else {
5620 subtour = walk(subtour.shift()).concat(subtour);
5621 }
5622 }
5623
5624 trail.unshift(eles.getElementById(subtour.shift())); // final node
5625
5626 for (var d in nodes) {
5627 if (nodes[d].length) {
5628 return result;
5629 }
5630 }
5631
5632 result.found = true;
5633 result.trail = this.spawn(trail, true);
5634 return result;
5635 }
5636};
5637
5638var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5639 var eles = this;
5640 var nodes = {};
5641 var id = 0;
5642 var edgeCount = 0;
5643 var components = [];
5644 var stack = [];
5645 var visitedEdges = {};
5646
5647 var buildComponent = function buildComponent(x, y) {
5648 var i = stack.length - 1;
5649 var cutset = [];
5650 var component = eles.spawn();
5651
5652 while (stack[i].x != x || stack[i].y != y) {
5653 cutset.push(stack.pop().edge);
5654 i--;
5655 }
5656
5657 cutset.push(stack.pop().edge);
5658 cutset.forEach(function (edge) {
5659 var connectedNodes = edge.connectedNodes().intersection(eles);
5660 component.merge(edge);
5661 connectedNodes.forEach(function (node) {
5662 var nodeId = node.id();
5663 var connectedEdges = node.connectedEdges().intersection(eles);
5664 component.merge(node);
5665
5666 if (!nodes[nodeId].cutVertex) {
5667 component.merge(connectedEdges);
5668 } else {
5669 component.merge(connectedEdges.filter(function (edge) {
5670 return edge.isLoop();
5671 }));
5672 }
5673 });
5674 });
5675 components.push(component);
5676 };
5677
5678 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5679 if (root === parent) edgeCount += 1;
5680 nodes[currentNode] = {
5681 id: id,
5682 low: id++,
5683 cutVertex: false
5684 };
5685 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5686
5687 if (edges.size() === 0) {
5688 components.push(eles.spawn(eles.getElementById(currentNode)));
5689 } else {
5690 var sourceId, targetId, otherNodeId, edgeId;
5691 edges.forEach(function (edge) {
5692 sourceId = edge.source().id();
5693 targetId = edge.target().id();
5694 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5695
5696 if (otherNodeId !== parent) {
5697 edgeId = edge.id();
5698
5699 if (!visitedEdges[edgeId]) {
5700 visitedEdges[edgeId] = true;
5701 stack.push({
5702 x: currentNode,
5703 y: otherNodeId,
5704 edge: edge
5705 });
5706 }
5707
5708 if (!(otherNodeId in nodes)) {
5709 biconnectedSearch(root, otherNodeId, currentNode);
5710 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5711
5712 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5713 nodes[currentNode].cutVertex = true;
5714 buildComponent(currentNode, otherNodeId);
5715 }
5716 } else {
5717 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5718 }
5719 }
5720 });
5721 }
5722 };
5723
5724 eles.forEach(function (ele) {
5725 if (ele.isNode()) {
5726 var nodeId = ele.id();
5727
5728 if (!(nodeId in nodes)) {
5729 edgeCount = 0;
5730 biconnectedSearch(nodeId, nodeId);
5731 nodes[nodeId].cutVertex = edgeCount > 1;
5732 }
5733 }
5734 });
5735 var cutVertices = Object.keys(nodes).filter(function (id) {
5736 return nodes[id].cutVertex;
5737 }).map(function (id) {
5738 return eles.getElementById(id);
5739 });
5740 return {
5741 cut: eles.spawn(cutVertices),
5742 components: components
5743 };
5744};
5745
5746var hopcroftTarjanBiconnected$1 = {
5747 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5748 htbc: hopcroftTarjanBiconnected,
5749 htb: hopcroftTarjanBiconnected,
5750 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5751};
5752
5753var tarjanStronglyConnected = function tarjanStronglyConnected() {
5754 var eles = this;
5755 var nodes = {};
5756 var index = 0;
5757 var components = [];
5758 var stack = [];
5759 var cut = eles.spawn(eles);
5760
5761 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5762 stack.push(sourceNodeId);
5763 nodes[sourceNodeId] = {
5764 index: index,
5765 low: index++,
5766 explored: false
5767 };
5768 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5769 connectedEdges.forEach(function (edge) {
5770 var targetNodeId = edge.target().id();
5771
5772 if (targetNodeId !== sourceNodeId) {
5773 if (!(targetNodeId in nodes)) {
5774 stronglyConnectedSearch(targetNodeId);
5775 }
5776
5777 if (!nodes[targetNodeId].explored) {
5778 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5779 }
5780 }
5781 });
5782
5783 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5784 var componentNodes = eles.spawn();
5785
5786 for (;;) {
5787 var nodeId = stack.pop();
5788 componentNodes.merge(eles.getElementById(nodeId));
5789 nodes[nodeId].low = nodes[sourceNodeId].index;
5790 nodes[nodeId].explored = true;
5791
5792 if (nodeId === sourceNodeId) {
5793 break;
5794 }
5795 }
5796
5797 var componentEdges = componentNodes.edgesWith(componentNodes);
5798 var component = componentNodes.merge(componentEdges);
5799 components.push(component);
5800 cut = cut.difference(component);
5801 }
5802 };
5803
5804 eles.forEach(function (ele) {
5805 if (ele.isNode()) {
5806 var nodeId = ele.id();
5807
5808 if (!(nodeId in nodes)) {
5809 stronglyConnectedSearch(nodeId);
5810 }
5811 }
5812 });
5813 return {
5814 cut: cut,
5815 components: components
5816 };
5817};
5818
5819var tarjanStronglyConnected$1 = {
5820 tarjanStronglyConnected: tarjanStronglyConnected,
5821 tsc: tarjanStronglyConnected,
5822 tscc: tarjanStronglyConnected,
5823 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5824};
5825
5826var elesfn$c = {};
5827[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) {
5828 extend(elesfn$c, props);
5829});
5830
5831/*!
5832Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5833Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5834Licensed under The MIT License (http://opensource.org/licenses/MIT)
5835*/
5836
5837/* promise states [Promises/A+ 2.1] */
5838var STATE_PENDING = 0;
5839/* [Promises/A+ 2.1.1] */
5840
5841var STATE_FULFILLED = 1;
5842/* [Promises/A+ 2.1.2] */
5843
5844var STATE_REJECTED = 2;
5845/* [Promises/A+ 2.1.3] */
5846
5847/* promise object constructor */
5848
5849var api = function api(executor) {
5850 /* optionally support non-constructor/plain-function call */
5851 if (!(this instanceof api)) return new api(executor);
5852 /* initialize object */
5853
5854 this.id = 'Thenable/1.0.7';
5855 this.state = STATE_PENDING;
5856 /* initial state */
5857
5858 this.fulfillValue = undefined;
5859 /* initial value */
5860
5861 /* [Promises/A+ 1.3, 2.1.2.2] */
5862
5863 this.rejectReason = undefined;
5864 /* initial reason */
5865
5866 /* [Promises/A+ 1.5, 2.1.3.2] */
5867
5868 this.onFulfilled = [];
5869 /* initial handlers */
5870
5871 this.onRejected = [];
5872 /* initial handlers */
5873
5874 /* provide optional information-hiding proxy */
5875
5876 this.proxy = {
5877 then: this.then.bind(this)
5878 };
5879 /* support optional executor function */
5880
5881 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5882};
5883/* promise API methods */
5884
5885
5886api.prototype = {
5887 /* promise resolving methods */
5888 fulfill: function fulfill(value) {
5889 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5890 },
5891 reject: function reject(value) {
5892 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5893 },
5894
5895 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5896 then: function then(onFulfilled, onRejected) {
5897 var curr = this;
5898 var next = new api();
5899 /* [Promises/A+ 2.2.7] */
5900
5901 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5902 /* [Promises/A+ 2.2.2/2.2.6] */
5903
5904 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5905 /* [Promises/A+ 2.2.3/2.2.6] */
5906
5907 execute(curr);
5908 return next.proxy;
5909 /* [Promises/A+ 2.2.7, 3.3] */
5910 }
5911};
5912/* deliver an action */
5913
5914var deliver = function deliver(curr, state, name, value) {
5915 if (curr.state === STATE_PENDING) {
5916 curr.state = state;
5917 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5918
5919 curr[name] = value;
5920 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5921
5922 execute(curr);
5923 }
5924
5925 return curr;
5926};
5927/* execute all handlers */
5928
5929
5930var execute = function execute(curr) {
5931 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5932};
5933/* execute particular set of handlers */
5934
5935
5936var execute_handlers = function execute_handlers(curr, name, value) {
5937 /* global setImmediate: true */
5938
5939 /* global setTimeout: true */
5940
5941 /* short-circuit processing */
5942 if (curr[name].length === 0) return;
5943 /* iterate over all handlers, exactly once */
5944
5945 var handlers = curr[name];
5946 curr[name] = [];
5947 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5948
5949 var func = function func() {
5950 for (var i = 0; i < handlers.length; i++) {
5951 handlers[i](value);
5952 }
5953 /* [Promises/A+ 2.2.5] */
5954
5955 };
5956 /* execute procedure asynchronously */
5957
5958 /* [Promises/A+ 2.2.4, 3.1] */
5959
5960
5961 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5962};
5963/* generate a resolver function */
5964
5965
5966var resolver = function resolver(cb, next, method) {
5967 return function (value) {
5968 if (typeof cb !== 'function')
5969 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5970 next[method].call(next, value);
5971 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5972 else {
5973 var result;
5974
5975 try {
5976 result = cb(value);
5977 }
5978 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5979 catch (e) {
5980 next.reject(e);
5981 /* [Promises/A+ 2.2.7.2] */
5982
5983 return;
5984 }
5985
5986 resolve(next, result);
5987 /* [Promises/A+ 2.2.7.1] */
5988 }
5989 };
5990};
5991/* "Promise Resolution Procedure" */
5992
5993/* [Promises/A+ 2.3] */
5994
5995
5996var resolve = function resolve(promise, x) {
5997 /* sanity check arguments */
5998
5999 /* [Promises/A+ 2.3.1] */
6000 if (promise === x || promise.proxy === x) {
6001 promise.reject(new TypeError('cannot resolve promise with itself'));
6002 return;
6003 }
6004 /* surgically check for a "then" method
6005 (mainly to just call the "getter" of "then" only once) */
6006
6007
6008 var then;
6009
6010 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6011 try {
6012 then = x.then;
6013 }
6014 /* [Promises/A+ 2.3.3.1, 3.5] */
6015 catch (e) {
6016 promise.reject(e);
6017 /* [Promises/A+ 2.3.3.2] */
6018
6019 return;
6020 }
6021 }
6022 /* handle own Thenables [Promises/A+ 2.3.2]
6023 and similar "thenables" [Promises/A+ 2.3.3] */
6024
6025
6026 if (typeof then === 'function') {
6027 var resolved = false;
6028
6029 try {
6030 /* call retrieved "then" method */
6031
6032 /* [Promises/A+ 2.3.3.3] */
6033 then.call(x,
6034 /* resolvePromise */
6035
6036 /* [Promises/A+ 2.3.3.3.1] */
6037 function (y) {
6038 if (resolved) return;
6039 resolved = true;
6040 /* [Promises/A+ 2.3.3.3.3] */
6041
6042 if (y === x)
6043 /* [Promises/A+ 3.6] */
6044 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6045 },
6046 /* rejectPromise */
6047
6048 /* [Promises/A+ 2.3.3.3.2] */
6049 function (r) {
6050 if (resolved) return;
6051 resolved = true;
6052 /* [Promises/A+ 2.3.3.3.3] */
6053
6054 promise.reject(r);
6055 });
6056 } catch (e) {
6057 if (!resolved)
6058 /* [Promises/A+ 2.3.3.3.3] */
6059 promise.reject(e);
6060 /* [Promises/A+ 2.3.3.3.4] */
6061 }
6062
6063 return;
6064 }
6065 /* handle other values */
6066
6067
6068 promise.fulfill(x);
6069 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6070}; // so we always have Promise.all()
6071
6072
6073api.all = function (ps) {
6074 return new api(function (resolveAll, rejectAll) {
6075 var vals = new Array(ps.length);
6076 var doneCount = 0;
6077
6078 var fulfill = function fulfill(i, val) {
6079 vals[i] = val;
6080 doneCount++;
6081
6082 if (doneCount === ps.length) {
6083 resolveAll(vals);
6084 }
6085 };
6086
6087 for (var i = 0; i < ps.length; i++) {
6088 (function (i) {
6089 var p = ps[i];
6090 var isPromise = p != null && p.then != null;
6091
6092 if (isPromise) {
6093 p.then(function (val) {
6094 fulfill(i, val);
6095 }, function (err) {
6096 rejectAll(err);
6097 });
6098 } else {
6099 var val = p;
6100 fulfill(i, val);
6101 }
6102 })(i);
6103 }
6104 });
6105};
6106
6107api.resolve = function (val) {
6108 return new api(function (resolve, reject) {
6109 resolve(val);
6110 });
6111};
6112
6113api.reject = function (val) {
6114 return new api(function (resolve, reject) {
6115 reject(val);
6116 });
6117};
6118
6119var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6120
6121var Animation = function Animation(target, opts, opts2) {
6122 var isCore = core(target);
6123 var isEle = !isCore;
6124
6125 var _p = this._private = extend({
6126 duration: 1000
6127 }, opts, opts2);
6128
6129 _p.target = target;
6130 _p.style = _p.style || _p.css;
6131 _p.started = false;
6132 _p.playing = false;
6133 _p.hooked = false;
6134 _p.applying = false;
6135 _p.progress = 0;
6136 _p.completes = [];
6137 _p.frames = [];
6138
6139 if (_p.complete && fn(_p.complete)) {
6140 _p.completes.push(_p.complete);
6141 }
6142
6143 if (isEle) {
6144 var pos = target.position();
6145 _p.startPosition = _p.startPosition || {
6146 x: pos.x,
6147 y: pos.y
6148 };
6149 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6150 }
6151
6152 if (isCore) {
6153 var pan = target.pan();
6154 _p.startPan = {
6155 x: pan.x,
6156 y: pan.y
6157 };
6158 _p.startZoom = target.zoom();
6159 } // for future timeline/animations impl
6160
6161
6162 this.length = 1;
6163 this[0] = this;
6164};
6165
6166var anifn = Animation.prototype;
6167extend(anifn, {
6168 instanceString: function instanceString() {
6169 return 'animation';
6170 },
6171 hook: function hook() {
6172 var _p = this._private;
6173
6174 if (!_p.hooked) {
6175 // add to target's animation queue
6176 var q;
6177 var tAni = _p.target._private.animation;
6178
6179 if (_p.queue) {
6180 q = tAni.queue;
6181 } else {
6182 q = tAni.current;
6183 }
6184
6185 q.push(this); // add to the animation loop pool
6186
6187 if (elementOrCollection(_p.target)) {
6188 _p.target.cy().addToAnimationPool(_p.target);
6189 }
6190
6191 _p.hooked = true;
6192 }
6193
6194 return this;
6195 },
6196 play: function play() {
6197 var _p = this._private; // autorewind
6198
6199 if (_p.progress === 1) {
6200 _p.progress = 0;
6201 }
6202
6203 _p.playing = true;
6204 _p.started = false; // needs to be started by animation loop
6205
6206 _p.stopped = false;
6207 this.hook(); // the animation loop will start the animation...
6208
6209 return this;
6210 },
6211 playing: function playing() {
6212 return this._private.playing;
6213 },
6214 apply: function apply() {
6215 var _p = this._private;
6216 _p.applying = true;
6217 _p.started = false; // needs to be started by animation loop
6218
6219 _p.stopped = false;
6220 this.hook(); // the animation loop will apply the animation at this progress
6221
6222 return this;
6223 },
6224 applying: function applying() {
6225 return this._private.applying;
6226 },
6227 pause: function pause() {
6228 var _p = this._private;
6229 _p.playing = false;
6230 _p.started = false;
6231 return this;
6232 },
6233 stop: function stop() {
6234 var _p = this._private;
6235 _p.playing = false;
6236 _p.started = false;
6237 _p.stopped = true; // to be removed from animation queues
6238
6239 return this;
6240 },
6241 rewind: function rewind() {
6242 return this.progress(0);
6243 },
6244 fastforward: function fastforward() {
6245 return this.progress(1);
6246 },
6247 time: function time(t) {
6248 var _p = this._private;
6249
6250 if (t === undefined) {
6251 return _p.progress * _p.duration;
6252 } else {
6253 return this.progress(t / _p.duration);
6254 }
6255 },
6256 progress: function progress(p) {
6257 var _p = this._private;
6258 var wasPlaying = _p.playing;
6259
6260 if (p === undefined) {
6261 return _p.progress;
6262 } else {
6263 if (wasPlaying) {
6264 this.pause();
6265 }
6266
6267 _p.progress = p;
6268 _p.started = false;
6269
6270 if (wasPlaying) {
6271 this.play();
6272 }
6273 }
6274
6275 return this;
6276 },
6277 completed: function completed() {
6278 return this._private.progress === 1;
6279 },
6280 reverse: function reverse() {
6281 var _p = this._private;
6282 var wasPlaying = _p.playing;
6283
6284 if (wasPlaying) {
6285 this.pause();
6286 }
6287
6288 _p.progress = 1 - _p.progress;
6289 _p.started = false;
6290
6291 var swap = function swap(a, b) {
6292 var _pa = _p[a];
6293
6294 if (_pa == null) {
6295 return;
6296 }
6297
6298 _p[a] = _p[b];
6299 _p[b] = _pa;
6300 };
6301
6302 swap('zoom', 'startZoom');
6303 swap('pan', 'startPan');
6304 swap('position', 'startPosition'); // swap styles
6305
6306 if (_p.style) {
6307 for (var i = 0; i < _p.style.length; i++) {
6308 var prop = _p.style[i];
6309 var name = prop.name;
6310 var startStyleProp = _p.startStyle[name];
6311 _p.startStyle[name] = prop;
6312 _p.style[i] = startStyleProp;
6313 }
6314 }
6315
6316 if (wasPlaying) {
6317 this.play();
6318 }
6319
6320 return this;
6321 },
6322 promise: function promise(type) {
6323 var _p = this._private;
6324 var arr;
6325
6326 switch (type) {
6327 case 'frame':
6328 arr = _p.frames;
6329 break;
6330
6331 default:
6332 case 'complete':
6333 case 'completed':
6334 arr = _p.completes;
6335 }
6336
6337 return new Promise$1(function (resolve, reject) {
6338 arr.push(function () {
6339 resolve();
6340 });
6341 });
6342 }
6343});
6344anifn.complete = anifn.completed;
6345anifn.run = anifn.play;
6346anifn.running = anifn.playing;
6347
6348var define = {
6349 animated: function animated() {
6350 return function animatedImpl() {
6351 var self = this;
6352 var selfIsArrayLike = self.length !== undefined;
6353 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6354
6355 var cy = this._private.cy || this;
6356
6357 if (!cy.styleEnabled()) {
6358 return false;
6359 }
6360
6361 var ele = all[0];
6362
6363 if (ele) {
6364 return ele._private.animation.current.length > 0;
6365 }
6366 };
6367 },
6368 // animated
6369 clearQueue: function clearQueue() {
6370 return function clearQueueImpl() {
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
6377 if (!cy.styleEnabled()) {
6378 return this;
6379 }
6380
6381 for (var i = 0; i < all.length; i++) {
6382 var ele = all[i];
6383 ele._private.animation.queue = [];
6384 }
6385
6386 return this;
6387 };
6388 },
6389 // clearQueue
6390 delay: function delay() {
6391 return function delayImpl(time, complete) {
6392 var cy = this._private.cy || this;
6393
6394 if (!cy.styleEnabled()) {
6395 return this;
6396 }
6397
6398 return this.animate({
6399 delay: time,
6400 duration: time,
6401 complete: complete
6402 });
6403 };
6404 },
6405 // delay
6406 delayAnimation: function delayAnimation() {
6407 return function delayAnimationImpl(time, complete) {
6408 var cy = this._private.cy || this;
6409
6410 if (!cy.styleEnabled()) {
6411 return this;
6412 }
6413
6414 return this.animation({
6415 delay: time,
6416 duration: time,
6417 complete: complete
6418 });
6419 };
6420 },
6421 // delay
6422 animation: function animation() {
6423 return function animationImpl(properties, params) {
6424 var self = this;
6425 var selfIsArrayLike = self.length !== undefined;
6426 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6427
6428 var cy = this._private.cy || this;
6429 var isCore = !selfIsArrayLike;
6430 var isEles = !isCore;
6431
6432 if (!cy.styleEnabled()) {
6433 return this;
6434 }
6435
6436 var style = cy.style();
6437 properties = extend({}, properties, params);
6438 var propertiesEmpty = Object.keys(properties).length === 0;
6439
6440 if (propertiesEmpty) {
6441 return new Animation(all[0], properties); // nothing to animate
6442 }
6443
6444 if (properties.duration === undefined) {
6445 properties.duration = 400;
6446 }
6447
6448 switch (properties.duration) {
6449 case 'slow':
6450 properties.duration = 600;
6451 break;
6452
6453 case 'fast':
6454 properties.duration = 200;
6455 break;
6456 }
6457
6458 if (isEles) {
6459 properties.style = style.getPropsList(properties.style || properties.css);
6460 properties.css = undefined;
6461 }
6462
6463 if (isEles && properties.renderedPosition != null) {
6464 var rpos = properties.renderedPosition;
6465 var pan = cy.pan();
6466 var zoom = cy.zoom();
6467 properties.position = renderedToModelPosition(rpos, zoom, pan);
6468 } // override pan w/ panBy if set
6469
6470
6471 if (isCore && properties.panBy != null) {
6472 var panBy = properties.panBy;
6473 var cyPan = cy.pan();
6474 properties.pan = {
6475 x: cyPan.x + panBy.x,
6476 y: cyPan.y + panBy.y
6477 };
6478 } // override pan w/ center if set
6479
6480
6481 var center = properties.center || properties.centre;
6482
6483 if (isCore && center != null) {
6484 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6485
6486 if (centerPan != null) {
6487 properties.pan = centerPan;
6488 }
6489 } // override pan & zoom w/ fit if set
6490
6491
6492 if (isCore && properties.fit != null) {
6493 var fit = properties.fit;
6494 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6495
6496 if (fitVp != null) {
6497 properties.pan = fitVp.pan;
6498 properties.zoom = fitVp.zoom;
6499 }
6500 } // override zoom (& potentially pan) w/ zoom obj if set
6501
6502
6503 if (isCore && plainObject(properties.zoom)) {
6504 var vp = cy.getZoomedViewport(properties.zoom);
6505
6506 if (vp != null) {
6507 if (vp.zoomed) {
6508 properties.zoom = vp.zoom;
6509 }
6510
6511 if (vp.panned) {
6512 properties.pan = vp.pan;
6513 }
6514 } else {
6515 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6516 }
6517 }
6518
6519 return new Animation(all[0], properties);
6520 };
6521 },
6522 // animate
6523 animate: function animate() {
6524 return function animateImpl(properties, params) {
6525 var self = this;
6526 var selfIsArrayLike = self.length !== undefined;
6527 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6528
6529 var cy = this._private.cy || this;
6530
6531 if (!cy.styleEnabled()) {
6532 return this;
6533 }
6534
6535 if (params) {
6536 properties = extend({}, properties, params);
6537 } // manually hook and run the animation
6538
6539
6540 for (var i = 0; i < all.length; i++) {
6541 var ele = all[i];
6542 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6543 var ani = ele.animation(properties, queue ? {
6544 queue: true
6545 } : undefined);
6546 ani.play();
6547 }
6548
6549 return this; // chaining
6550 };
6551 },
6552 // animate
6553 stop: function stop() {
6554 return function stopImpl(clearQueue, jumpToEnd) {
6555 var self = this;
6556 var selfIsArrayLike = self.length !== undefined;
6557 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6558
6559 var cy = this._private.cy || this;
6560
6561 if (!cy.styleEnabled()) {
6562 return this;
6563 }
6564
6565 for (var i = 0; i < all.length; i++) {
6566 var ele = all[i];
6567 var _p = ele._private;
6568 var anis = _p.animation.current;
6569
6570 for (var j = 0; j < anis.length; j++) {
6571 var ani = anis[j];
6572 var ani_p = ani._private;
6573
6574 if (jumpToEnd) {
6575 // next iteration of the animation loop, the animation
6576 // will go straight to the end and be removed
6577 ani_p.duration = 0;
6578 }
6579 } // clear the queue of future animations
6580
6581
6582 if (clearQueue) {
6583 _p.animation.queue = [];
6584 }
6585
6586 if (!jumpToEnd) {
6587 _p.animation.current = [];
6588 }
6589 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6590
6591
6592 cy.notify('draw');
6593 return this;
6594 };
6595 } // stop
6596
6597}; // define
6598
6599var define$1 = {
6600 // access data field
6601 data: function data(params) {
6602 var defaults = {
6603 field: 'data',
6604 bindingEvent: 'data',
6605 allowBinding: false,
6606 allowSetting: false,
6607 allowGetting: false,
6608 settingEvent: 'data',
6609 settingTriggersEvent: false,
6610 triggerFnName: 'trigger',
6611 immutableKeys: {},
6612 // key => true if immutable
6613 updateStyle: false,
6614 beforeGet: function beforeGet(self) {},
6615 beforeSet: function beforeSet(self, obj) {},
6616 onSet: function onSet(self) {},
6617 canSet: function canSet(self) {
6618 return true;
6619 }
6620 };
6621 params = extend({}, defaults, params);
6622 return function dataImpl(name, value) {
6623 var p = params;
6624 var self = this;
6625 var selfIsArrayLike = self.length !== undefined;
6626 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6627
6628 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6629
6630 if (string(name)) {
6631 // set or get property
6632 var isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
6633
6634 var path = isPathLike && toPath(name); // .data('foo')
6635
6636 if (p.allowGetting && value === undefined) {
6637 // get
6638 var ret;
6639
6640 if (single) {
6641 p.beforeGet(single); // check if it's path and a field with the same name doesn't exist
6642
6643 if (path && single._private[p.field][name] === undefined) {
6644 ret = get(single._private[p.field], path);
6645 } else {
6646 ret = single._private[p.field][name];
6647 }
6648 }
6649
6650 return ret; // .data('foo', 'bar')
6651 } else if (p.allowSetting && value !== undefined) {
6652 // set
6653 var valid = !p.immutableKeys[name];
6654
6655 if (valid) {
6656 var change = _defineProperty({}, name, value);
6657
6658 p.beforeSet(self, change);
6659
6660 for (var i = 0, l = all.length; i < l; i++) {
6661 var ele = all[i];
6662
6663 if (p.canSet(ele)) {
6664 if (path && single._private[p.field][name] === undefined) {
6665 set(ele._private[p.field], path, value);
6666 } else {
6667 ele._private[p.field][name] = value;
6668 }
6669 }
6670 } // update mappers if asked
6671
6672
6673 if (p.updateStyle) {
6674 self.updateStyle();
6675 } // call onSet callback
6676
6677
6678 p.onSet(self);
6679
6680 if (p.settingTriggersEvent) {
6681 self[p.triggerFnName](p.settingEvent);
6682 }
6683 }
6684 } // .data({ 'foo': 'bar' })
6685
6686 } else if (p.allowSetting && plainObject(name)) {
6687 // extend
6688 var obj = name;
6689 var k, v;
6690 var keys = Object.keys(obj);
6691 p.beforeSet(self, obj);
6692
6693 for (var _i = 0; _i < keys.length; _i++) {
6694 k = keys[_i];
6695 v = obj[k];
6696
6697 var _valid = !p.immutableKeys[k];
6698
6699 if (_valid) {
6700 for (var j = 0; j < all.length; j++) {
6701 var _ele = all[j];
6702
6703 if (p.canSet(_ele)) {
6704 _ele._private[p.field][k] = v;
6705 }
6706 }
6707 }
6708 } // update mappers if asked
6709
6710
6711 if (p.updateStyle) {
6712 self.updateStyle();
6713 } // call onSet callback
6714
6715
6716 p.onSet(self);
6717
6718 if (p.settingTriggersEvent) {
6719 self[p.triggerFnName](p.settingEvent);
6720 } // .data(function(){ ... })
6721
6722 } else if (p.allowBinding && fn(name)) {
6723 // bind to event
6724 var fn$1 = name;
6725 self.on(p.bindingEvent, fn$1); // .data()
6726 } else if (p.allowGetting && name === undefined) {
6727 // get whole object
6728 var _ret;
6729
6730 if (single) {
6731 p.beforeGet(single);
6732 _ret = single._private[p.field];
6733 }
6734
6735 return _ret;
6736 }
6737
6738 return self; // maintain chainability
6739 }; // function
6740 },
6741 // data
6742 // remove data field
6743 removeData: function removeData(params) {
6744 var defaults = {
6745 field: 'data',
6746 event: 'data',
6747 triggerFnName: 'trigger',
6748 triggerEvent: false,
6749 immutableKeys: {} // key => true if immutable
6750
6751 };
6752 params = extend({}, defaults, params);
6753 return function removeDataImpl(names) {
6754 var p = params;
6755 var self = this;
6756 var selfIsArrayLike = self.length !== undefined;
6757 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6758 // .removeData('foo bar')
6759
6760 if (string(names)) {
6761 // then get the list of keys, and delete them
6762 var keys = names.split(/\s+/);
6763 var l = keys.length;
6764
6765 for (var i = 0; i < l; i++) {
6766 // delete each non-empty key
6767 var key = keys[i];
6768
6769 if (emptyString(key)) {
6770 continue;
6771 }
6772
6773 var valid = !p.immutableKeys[key]; // not valid if immutable
6774
6775 if (valid) {
6776 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6777 all[i_a]._private[p.field][key] = undefined;
6778 }
6779 }
6780 }
6781
6782 if (p.triggerEvent) {
6783 self[p.triggerFnName](p.event);
6784 } // .removeData()
6785
6786 } else if (names === undefined) {
6787 // then delete all keys
6788 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6789 var _privateFields = all[_i_a]._private[p.field];
6790
6791 var _keys = Object.keys(_privateFields);
6792
6793 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6794 var _key = _keys[_i2];
6795 var validKeyToDelete = !p.immutableKeys[_key];
6796
6797 if (validKeyToDelete) {
6798 _privateFields[_key] = undefined;
6799 }
6800 }
6801 }
6802
6803 if (p.triggerEvent) {
6804 self[p.triggerFnName](p.event);
6805 }
6806 }
6807
6808 return self; // maintain chaining
6809 }; // function
6810 } // removeData
6811
6812}; // define
6813
6814var define$2 = {
6815 eventAliasesOn: function eventAliasesOn(proto) {
6816 var p = proto;
6817 p.addListener = p.listen = p.bind = p.on;
6818 p.unlisten = p.unbind = p.off = p.removeListener;
6819 p.trigger = p.emit; // this is just a wrapper alias of .on()
6820
6821 p.pon = p.promiseOn = function (events, selector) {
6822 var self = this;
6823 var args = Array.prototype.slice.call(arguments, 0);
6824 return new Promise$1(function (resolve, reject) {
6825 var callback = function callback(e) {
6826 self.off.apply(self, offArgs);
6827 resolve(e);
6828 };
6829
6830 var onArgs = args.concat([callback]);
6831 var offArgs = onArgs.concat([]);
6832 self.on.apply(self, onArgs);
6833 });
6834 };
6835 }
6836}; // define
6837
6838// use this module to cherry pick functions into your prototype
6839var define$3 = {};
6840[define, define$1, define$2].forEach(function (m) {
6841 extend(define$3, m);
6842});
6843
6844var elesfn$d = {
6845 animate: define$3.animate(),
6846 animation: define$3.animation(),
6847 animated: define$3.animated(),
6848 clearQueue: define$3.clearQueue(),
6849 delay: define$3.delay(),
6850 delayAnimation: define$3.delayAnimation(),
6851 stop: define$3.stop()
6852};
6853
6854var elesfn$e = {
6855 classes: function classes(_classes) {
6856 var self = this;
6857
6858 if (_classes === undefined) {
6859 var ret = [];
6860
6861 self[0]._private.classes.forEach(function (cls) {
6862 return ret.push(cls);
6863 });
6864
6865 return ret;
6866 } else if (!array(_classes)) {
6867 // extract classes from string
6868 _classes = (_classes || '').match(/\S+/g) || [];
6869 }
6870
6871 var changed = [];
6872 var classesSet = new Set$1(_classes); // check and update each ele
6873
6874 for (var j = 0; j < self.length; j++) {
6875 var ele = self[j];
6876 var _p = ele._private;
6877 var eleClasses = _p.classes;
6878 var changedEle = false; // check if ele has all of the passed classes
6879
6880 for (var i = 0; i < _classes.length; i++) {
6881 var cls = _classes[i];
6882 var eleHasClass = eleClasses.has(cls);
6883
6884 if (!eleHasClass) {
6885 changedEle = true;
6886 break;
6887 }
6888 } // check if ele has classes outside of those passed
6889
6890
6891 if (!changedEle) {
6892 changedEle = eleClasses.size !== _classes.length;
6893 }
6894
6895 if (changedEle) {
6896 _p.classes = classesSet;
6897 changed.push(ele);
6898 }
6899 } // trigger update style on those eles that had class changes
6900
6901
6902 if (changed.length > 0) {
6903 this.spawn(changed).updateStyle().emit('class');
6904 }
6905
6906 return self;
6907 },
6908 addClass: function addClass(classes) {
6909 return this.toggleClass(classes, true);
6910 },
6911 hasClass: function hasClass(className) {
6912 var ele = this[0];
6913 return ele != null && ele._private.classes.has(className);
6914 },
6915 toggleClass: function toggleClass(classes, toggle) {
6916 if (!array(classes)) {
6917 // extract classes from string
6918 classes = classes.match(/\S+/g) || [];
6919 }
6920
6921 var self = this;
6922 var toggleUndefd = toggle === undefined;
6923 var changed = []; // eles who had classes changed
6924
6925 for (var i = 0, il = self.length; i < il; i++) {
6926 var ele = self[i];
6927 var eleClasses = ele._private.classes;
6928 var changedEle = false;
6929
6930 for (var j = 0; j < classes.length; j++) {
6931 var cls = classes[j];
6932 var hasClass = eleClasses.has(cls);
6933 var changedNow = false;
6934
6935 if (toggle || toggleUndefd && !hasClass) {
6936 eleClasses.add(cls);
6937 changedNow = true;
6938 } else if (!toggle || toggleUndefd && hasClass) {
6939 eleClasses["delete"](cls);
6940 changedNow = true;
6941 }
6942
6943 if (!changedEle && changedNow) {
6944 changed.push(ele);
6945 changedEle = true;
6946 }
6947 } // for j classes
6948
6949 } // for i eles
6950 // trigger update style on those eles that had class changes
6951
6952
6953 if (changed.length > 0) {
6954 this.spawn(changed).updateStyle().emit('class');
6955 }
6956
6957 return self;
6958 },
6959 removeClass: function removeClass(classes) {
6960 return this.toggleClass(classes, false);
6961 },
6962 flashClass: function flashClass(classes, duration) {
6963 var self = this;
6964
6965 if (duration == null) {
6966 duration = 250;
6967 } else if (duration === 0) {
6968 return self; // nothing to do really
6969 }
6970
6971 self.addClass(classes);
6972 setTimeout(function () {
6973 self.removeClass(classes);
6974 }, duration);
6975 return self;
6976 }
6977};
6978elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6979
6980var tokens = {
6981 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6982 // chars we need to escape in let names, etc
6983 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6984 // binary comparison op (used in data selectors)
6985 boolOp: '\\?|\\!|\\^',
6986 // boolean (unary) operators (used in data selectors)
6987 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6988 // string literals (used in data selectors) -- doublequotes | singlequotes
6989 number: number$1,
6990 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6991 meta: 'degree|indegree|outdegree',
6992 // allowed metadata fields (i.e. allowed functions to use from Collection)
6993 separator: '\\s*,\\s*',
6994 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6995 descendant: '\\s+',
6996 child: '\\s+>\\s+',
6997 subject: '\\$',
6998 group: 'node|edge|\\*',
6999 directedEdge: '\\s+->\\s+',
7000 undirectedEdge: '\\s+<->\\s+'
7001};
7002tokens.variable = '(?:[\\w-.]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name can have letters, numbers, dashes, and periods
7003
7004tokens.className = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a class name has the same rules as a variable except it can't have a '.' in the name
7005
7006tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
7007
7008tokens.id = tokens.variable; // an element id (follows variable conventions)
7009
7010(function () {
7011 var ops, op, i; // add @ variants to comparatorOp
7012
7013 ops = tokens.comparatorOp.split('|');
7014
7015 for (i = 0; i < ops.length; i++) {
7016 op = ops[i];
7017 tokens.comparatorOp += '|@' + op;
7018 } // add ! variants to comparatorOp
7019
7020
7021 ops = tokens.comparatorOp.split('|');
7022
7023 for (i = 0; i < ops.length; i++) {
7024 op = ops[i];
7025
7026 if (op.indexOf('!') >= 0) {
7027 continue;
7028 } // skip ops that explicitly contain !
7029
7030
7031 if (op === '=') {
7032 continue;
7033 } // skip = b/c != is explicitly defined
7034
7035
7036 tokens.comparatorOp += '|\\!' + op;
7037 }
7038})();
7039
7040/**
7041 * Make a new query object
7042 *
7043 * @prop type {Type} The type enum (int) of the query
7044 * @prop checks List of checks to make against an ele to test for a match
7045 */
7046var newQuery = function newQuery() {
7047 return {
7048 checks: []
7049 };
7050};
7051
7052/**
7053 * A check type enum-like object. Uses integer values for fast match() lookup.
7054 * The ordering does not matter as long as the ints are unique.
7055 */
7056var Type = {
7057 /** E.g. node */
7058 GROUP: 0,
7059
7060 /** A collection of elements */
7061 COLLECTION: 1,
7062
7063 /** A filter(ele) function */
7064 FILTER: 2,
7065
7066 /** E.g. [foo > 1] */
7067 DATA_COMPARE: 3,
7068
7069 /** E.g. [foo] */
7070 DATA_EXIST: 4,
7071
7072 /** E.g. [?foo] */
7073 DATA_BOOL: 5,
7074
7075 /** E.g. [[degree > 2]] */
7076 META_COMPARE: 6,
7077
7078 /** E.g. :selected */
7079 STATE: 7,
7080
7081 /** E.g. #foo */
7082 ID: 8,
7083
7084 /** E.g. .foo */
7085 CLASS: 9,
7086
7087 /** E.g. #foo <-> #bar */
7088 UNDIRECTED_EDGE: 10,
7089
7090 /** E.g. #foo -> #bar */
7091 DIRECTED_EDGE: 11,
7092
7093 /** E.g. $#foo -> #bar */
7094 NODE_SOURCE: 12,
7095
7096 /** E.g. #foo -> $#bar */
7097 NODE_TARGET: 13,
7098
7099 /** E.g. $#foo <-> #bar */
7100 NODE_NEIGHBOR: 14,
7101
7102 /** E.g. #foo > #bar */
7103 CHILD: 15,
7104
7105 /** E.g. #foo #bar */
7106 DESCENDANT: 16,
7107
7108 /** E.g. $#foo > #bar */
7109 PARENT: 17,
7110
7111 /** E.g. $#foo #bar */
7112 ANCESTOR: 18,
7113
7114 /** E.g. #foo > $bar > #baz */
7115 COMPOUND_SPLIT: 19,
7116
7117 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7118 TRUE: 20
7119};
7120
7121var stateSelectors = [{
7122 selector: ':selected',
7123 matches: function matches(ele) {
7124 return ele.selected();
7125 }
7126}, {
7127 selector: ':unselected',
7128 matches: function matches(ele) {
7129 return !ele.selected();
7130 }
7131}, {
7132 selector: ':selectable',
7133 matches: function matches(ele) {
7134 return ele.selectable();
7135 }
7136}, {
7137 selector: ':unselectable',
7138 matches: function matches(ele) {
7139 return !ele.selectable();
7140 }
7141}, {
7142 selector: ':locked',
7143 matches: function matches(ele) {
7144 return ele.locked();
7145 }
7146}, {
7147 selector: ':unlocked',
7148 matches: function matches(ele) {
7149 return !ele.locked();
7150 }
7151}, {
7152 selector: ':visible',
7153 matches: function matches(ele) {
7154 return ele.visible();
7155 }
7156}, {
7157 selector: ':hidden',
7158 matches: function matches(ele) {
7159 return !ele.visible();
7160 }
7161}, {
7162 selector: ':transparent',
7163 matches: function matches(ele) {
7164 return ele.transparent();
7165 }
7166}, {
7167 selector: ':grabbed',
7168 matches: function matches(ele) {
7169 return ele.grabbed();
7170 }
7171}, {
7172 selector: ':free',
7173 matches: function matches(ele) {
7174 return !ele.grabbed();
7175 }
7176}, {
7177 selector: ':removed',
7178 matches: function matches(ele) {
7179 return ele.removed();
7180 }
7181}, {
7182 selector: ':inside',
7183 matches: function matches(ele) {
7184 return !ele.removed();
7185 }
7186}, {
7187 selector: ':grabbable',
7188 matches: function matches(ele) {
7189 return ele.grabbable();
7190 }
7191}, {
7192 selector: ':ungrabbable',
7193 matches: function matches(ele) {
7194 return !ele.grabbable();
7195 }
7196}, {
7197 selector: ':animated',
7198 matches: function matches(ele) {
7199 return ele.animated();
7200 }
7201}, {
7202 selector: ':unanimated',
7203 matches: function matches(ele) {
7204 return !ele.animated();
7205 }
7206}, {
7207 selector: ':parent',
7208 matches: function matches(ele) {
7209 return ele.isParent();
7210 }
7211}, {
7212 selector: ':childless',
7213 matches: function matches(ele) {
7214 return ele.isChildless();
7215 }
7216}, {
7217 selector: ':child',
7218 matches: function matches(ele) {
7219 return ele.isChild();
7220 }
7221}, {
7222 selector: ':orphan',
7223 matches: function matches(ele) {
7224 return ele.isOrphan();
7225 }
7226}, {
7227 selector: ':nonorphan',
7228 matches: function matches(ele) {
7229 return ele.isChild();
7230 }
7231}, {
7232 selector: ':compound',
7233 matches: function matches(ele) {
7234 if (ele.isNode()) {
7235 return ele.isParent();
7236 } else {
7237 return ele.source().isParent() || ele.target().isParent();
7238 }
7239 }
7240}, {
7241 selector: ':loop',
7242 matches: function matches(ele) {
7243 return ele.isLoop();
7244 }
7245}, {
7246 selector: ':simple',
7247 matches: function matches(ele) {
7248 return ele.isSimple();
7249 }
7250}, {
7251 selector: ':active',
7252 matches: function matches(ele) {
7253 return ele.active();
7254 }
7255}, {
7256 selector: ':inactive',
7257 matches: function matches(ele) {
7258 return !ele.active();
7259 }
7260}, {
7261 selector: ':backgrounding',
7262 matches: function matches(ele) {
7263 return ele.backgrounding();
7264 }
7265}, {
7266 selector: ':nonbackgrounding',
7267 matches: function matches(ele) {
7268 return !ele.backgrounding();
7269 }
7270}].sort(function (a, b) {
7271 // n.b. selectors that are starting substrings of others must have the longer ones first
7272 return descending(a.selector, b.selector);
7273});
7274
7275var lookup = function () {
7276 var selToFn = {};
7277 var s;
7278
7279 for (var i = 0; i < stateSelectors.length; i++) {
7280 s = stateSelectors[i];
7281 selToFn[s.selector] = s.matches;
7282 }
7283
7284 return selToFn;
7285}();
7286
7287var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7288 return lookup[sel](ele);
7289};
7290var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7291 return s.selector;
7292}).join('|') + ')';
7293
7294// so that values get compared properly in Selector.filter()
7295
7296var cleanMetaChars = function cleanMetaChars(str) {
7297 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7298 return $1;
7299 });
7300};
7301
7302var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7303 selector[selector.length - 1] = replacementQuery;
7304}; // NOTE: add new expression syntax here to have it recognised by the parser;
7305// - a query contains all adjacent (i.e. no separator in between) expressions;
7306// - the current query is stored in selector[i]
7307// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7308
7309
7310var exprs = [{
7311 name: 'group',
7312 // just used for identifying when debugging
7313 query: true,
7314 regex: '(' + tokens.group + ')',
7315 populate: function populate(selector, query, _ref) {
7316 var _ref2 = _slicedToArray(_ref, 1),
7317 group = _ref2[0];
7318
7319 query.checks.push({
7320 type: Type.GROUP,
7321 value: group === '*' ? group : group + 's'
7322 });
7323 }
7324}, {
7325 name: 'state',
7326 query: true,
7327 regex: stateSelectorRegex,
7328 populate: function populate(selector, query, _ref3) {
7329 var _ref4 = _slicedToArray(_ref3, 1),
7330 state = _ref4[0];
7331
7332 query.checks.push({
7333 type: Type.STATE,
7334 value: state
7335 });
7336 }
7337}, {
7338 name: 'id',
7339 query: true,
7340 regex: '\\#(' + tokens.id + ')',
7341 populate: function populate(selector, query, _ref5) {
7342 var _ref6 = _slicedToArray(_ref5, 1),
7343 id = _ref6[0];
7344
7345 query.checks.push({
7346 type: Type.ID,
7347 value: cleanMetaChars(id)
7348 });
7349 }
7350}, {
7351 name: 'className',
7352 query: true,
7353 regex: '\\.(' + tokens.className + ')',
7354 populate: function populate(selector, query, _ref7) {
7355 var _ref8 = _slicedToArray(_ref7, 1),
7356 className = _ref8[0];
7357
7358 query.checks.push({
7359 type: Type.CLASS,
7360 value: cleanMetaChars(className)
7361 });
7362 }
7363}, {
7364 name: 'dataExists',
7365 query: true,
7366 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7367 populate: function populate(selector, query, _ref9) {
7368 var _ref10 = _slicedToArray(_ref9, 1),
7369 variable = _ref10[0];
7370
7371 query.checks.push({
7372 type: Type.DATA_EXIST,
7373 field: cleanMetaChars(variable)
7374 });
7375 }
7376}, {
7377 name: 'dataCompare',
7378 query: true,
7379 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7380 populate: function populate(selector, query, _ref11) {
7381 var _ref12 = _slicedToArray(_ref11, 3),
7382 variable = _ref12[0],
7383 comparatorOp = _ref12[1],
7384 value = _ref12[2];
7385
7386 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7387
7388 if (valueIsString) {
7389 value = value.substring(1, value.length - 1);
7390 } else {
7391 value = parseFloat(value);
7392 }
7393
7394 query.checks.push({
7395 type: Type.DATA_COMPARE,
7396 field: cleanMetaChars(variable),
7397 operator: comparatorOp,
7398 value: value
7399 });
7400 }
7401}, {
7402 name: 'dataBool',
7403 query: true,
7404 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7405 populate: function populate(selector, query, _ref13) {
7406 var _ref14 = _slicedToArray(_ref13, 2),
7407 boolOp = _ref14[0],
7408 variable = _ref14[1];
7409
7410 query.checks.push({
7411 type: Type.DATA_BOOL,
7412 field: cleanMetaChars(variable),
7413 operator: boolOp
7414 });
7415 }
7416}, {
7417 name: 'metaCompare',
7418 query: true,
7419 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7420 populate: function populate(selector, query, _ref15) {
7421 var _ref16 = _slicedToArray(_ref15, 3),
7422 meta = _ref16[0],
7423 comparatorOp = _ref16[1],
7424 number = _ref16[2];
7425
7426 query.checks.push({
7427 type: Type.META_COMPARE,
7428 field: cleanMetaChars(meta),
7429 operator: comparatorOp,
7430 value: parseFloat(number)
7431 });
7432 }
7433}, {
7434 name: 'nextQuery',
7435 separator: true,
7436 regex: tokens.separator,
7437 populate: function populate(selector, query) {
7438 var currentSubject = selector.currentSubject;
7439 var edgeCount = selector.edgeCount;
7440 var compoundCount = selector.compoundCount;
7441 var lastQ = selector[selector.length - 1];
7442
7443 if (currentSubject != null) {
7444 lastQ.subject = currentSubject;
7445 selector.currentSubject = null;
7446 }
7447
7448 lastQ.edgeCount = edgeCount;
7449 lastQ.compoundCount = compoundCount;
7450 selector.edgeCount = 0;
7451 selector.compoundCount = 0; // go on to next query
7452
7453 var nextQuery = selector[selector.length++] = newQuery();
7454 return nextQuery; // this is the new query to be filled by the following exprs
7455 }
7456}, {
7457 name: 'directedEdge',
7458 separator: true,
7459 regex: tokens.directedEdge,
7460 populate: function populate(selector, query) {
7461 if (selector.currentSubject == null) {
7462 // undirected edge
7463 var edgeQuery = newQuery();
7464 var source = query;
7465 var target = newQuery();
7466 edgeQuery.checks.push({
7467 type: Type.DIRECTED_EDGE,
7468 source: source,
7469 target: target
7470 }); // the query in the selector should be the edge rather than the source
7471
7472 replaceLastQuery(selector, query, edgeQuery);
7473 selector.edgeCount++; // we're now populating the target query with expressions that follow
7474
7475 return target;
7476 } else {
7477 // source/target
7478 var srcTgtQ = newQuery();
7479 var _source = query;
7480
7481 var _target = newQuery();
7482
7483 srcTgtQ.checks.push({
7484 type: Type.NODE_SOURCE,
7485 source: _source,
7486 target: _target
7487 }); // the query in the selector should be the neighbourhood rather than the node
7488
7489 replaceLastQuery(selector, query, srcTgtQ);
7490 selector.edgeCount++;
7491 return _target; // now populating the target with the following expressions
7492 }
7493 }
7494}, {
7495 name: 'undirectedEdge',
7496 separator: true,
7497 regex: tokens.undirectedEdge,
7498 populate: function populate(selector, query) {
7499 if (selector.currentSubject == null) {
7500 // undirected edge
7501 var edgeQuery = newQuery();
7502 var source = query;
7503 var target = newQuery();
7504 edgeQuery.checks.push({
7505 type: Type.UNDIRECTED_EDGE,
7506 nodes: [source, target]
7507 }); // the query in the selector should be the edge rather than the source
7508
7509 replaceLastQuery(selector, query, edgeQuery);
7510 selector.edgeCount++; // we're now populating the target query with expressions that follow
7511
7512 return target;
7513 } else {
7514 // neighbourhood
7515 var nhoodQ = newQuery();
7516 var node = query;
7517 var neighbor = newQuery();
7518 nhoodQ.checks.push({
7519 type: Type.NODE_NEIGHBOR,
7520 node: node,
7521 neighbor: neighbor
7522 }); // the query in the selector should be the neighbourhood rather than the node
7523
7524 replaceLastQuery(selector, query, nhoodQ);
7525 return neighbor; // now populating the neighbor with following expressions
7526 }
7527 }
7528}, {
7529 name: 'child',
7530 separator: true,
7531 regex: tokens.child,
7532 populate: function populate(selector, query) {
7533 if (selector.currentSubject == null) {
7534 // default: child query
7535 var parentChildQuery = newQuery();
7536 var child = newQuery();
7537 var parent = selector[selector.length - 1];
7538 parentChildQuery.checks.push({
7539 type: Type.CHILD,
7540 parent: parent,
7541 child: child
7542 }); // the query in the selector should be the '>' itself
7543
7544 replaceLastQuery(selector, query, parentChildQuery);
7545 selector.compoundCount++; // we're now populating the child query with expressions that follow
7546
7547 return child;
7548 } else if (selector.currentSubject === query) {
7549 // compound split query
7550 var compound = newQuery();
7551 var left = selector[selector.length - 1];
7552 var right = newQuery();
7553 var subject = newQuery();
7554
7555 var _child = newQuery();
7556
7557 var _parent = newQuery(); // set up the root compound q
7558
7559
7560 compound.checks.push({
7561 type: Type.COMPOUND_SPLIT,
7562 left: left,
7563 right: right,
7564 subject: subject
7565 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7566
7567 subject.checks = query.checks; // take the checks from the left
7568
7569 query.checks = [{
7570 type: Type.TRUE
7571 }]; // checks under left refs the subject implicitly
7572 // set up the right q
7573
7574 _parent.checks.push({
7575 type: Type.TRUE
7576 }); // parent implicitly refs the subject
7577
7578
7579 right.checks.push({
7580 type: Type.PARENT,
7581 // type is swapped on right side queries
7582 parent: _parent,
7583 child: _child // empty for now
7584
7585 });
7586 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7587
7588 selector.currentSubject = subject;
7589 selector.compoundCount++;
7590 return _child; // now populating the right side's child
7591 } else {
7592 // parent query
7593 // info for parent query
7594 var _parent2 = newQuery();
7595
7596 var _child2 = newQuery();
7597
7598 var pcQChecks = [{
7599 type: Type.PARENT,
7600 parent: _parent2,
7601 child: _child2
7602 }]; // the parent-child query takes the place of the query previously being populated
7603
7604 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7605
7606 query.checks = pcQChecks; // pc query takes over
7607
7608 selector.compoundCount++;
7609 return _child2; // we're now populating the child
7610 }
7611 }
7612}, {
7613 name: 'descendant',
7614 separator: true,
7615 regex: tokens.descendant,
7616 populate: function populate(selector, query) {
7617 if (selector.currentSubject == null) {
7618 // default: descendant query
7619 var ancChQuery = newQuery();
7620 var descendant = newQuery();
7621 var ancestor = selector[selector.length - 1];
7622 ancChQuery.checks.push({
7623 type: Type.DESCENDANT,
7624 ancestor: ancestor,
7625 descendant: descendant
7626 }); // the query in the selector should be the '>' itself
7627
7628 replaceLastQuery(selector, query, ancChQuery);
7629 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7630
7631 return descendant;
7632 } else if (selector.currentSubject === query) {
7633 // compound split query
7634 var compound = newQuery();
7635 var left = selector[selector.length - 1];
7636 var right = newQuery();
7637 var subject = newQuery();
7638
7639 var _descendant = newQuery();
7640
7641 var _ancestor = newQuery(); // set up the root compound q
7642
7643
7644 compound.checks.push({
7645 type: Type.COMPOUND_SPLIT,
7646 left: left,
7647 right: right,
7648 subject: subject
7649 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7650
7651 subject.checks = query.checks; // take the checks from the left
7652
7653 query.checks = [{
7654 type: Type.TRUE
7655 }]; // checks under left refs the subject implicitly
7656 // set up the right q
7657
7658 _ancestor.checks.push({
7659 type: Type.TRUE
7660 }); // ancestor implicitly refs the subject
7661
7662
7663 right.checks.push({
7664 type: Type.ANCESTOR,
7665 // type is swapped on right side queries
7666 ancestor: _ancestor,
7667 descendant: _descendant // empty for now
7668
7669 });
7670 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7671
7672 selector.currentSubject = subject;
7673 selector.compoundCount++;
7674 return _descendant; // now populating the right side's descendant
7675 } else {
7676 // ancestor query
7677 // info for parent query
7678 var _ancestor2 = newQuery();
7679
7680 var _descendant2 = newQuery();
7681
7682 var adQChecks = [{
7683 type: Type.ANCESTOR,
7684 ancestor: _ancestor2,
7685 descendant: _descendant2
7686 }]; // the parent-child query takes the place of the query previously being populated
7687
7688 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7689
7690 query.checks = adQChecks; // pc query takes over
7691
7692 selector.compoundCount++;
7693 return _descendant2; // we're now populating the child
7694 }
7695 }
7696}, {
7697 name: 'subject',
7698 modifier: true,
7699 regex: tokens.subject,
7700 populate: function populate(selector, query) {
7701 if (selector.currentSubject != null && selector.currentSubject !== query) {
7702 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7703 return false;
7704 }
7705
7706 selector.currentSubject = query;
7707 var topQ = selector[selector.length - 1];
7708 var topChk = topQ.checks[0];
7709 var topType = topChk == null ? null : topChk.type;
7710
7711 if (topType === Type.DIRECTED_EDGE) {
7712 // directed edge with subject on the target
7713 // change to target node check
7714 topChk.type = Type.NODE_TARGET;
7715 } else if (topType === Type.UNDIRECTED_EDGE) {
7716 // undirected edge with subject on the second node
7717 // change to neighbor check
7718 topChk.type = Type.NODE_NEIGHBOR;
7719 topChk.node = topChk.nodes[1]; // second node is subject
7720
7721 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7722
7723 topChk.nodes = null;
7724 }
7725 }
7726}];
7727exprs.forEach(function (e) {
7728 return e.regexObj = new RegExp('^' + e.regex);
7729});
7730
7731/**
7732 * Of all the expressions, find the first match in the remaining text.
7733 * @param {string} remaining The remaining text to parse
7734 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7735 */
7736
7737var consumeExpr = function consumeExpr(remaining) {
7738 var expr;
7739 var match;
7740 var name;
7741
7742 for (var j = 0; j < exprs.length; j++) {
7743 var e = exprs[j];
7744 var n = e.name;
7745 var m = remaining.match(e.regexObj);
7746
7747 if (m != null) {
7748 match = m;
7749 expr = e;
7750 name = n;
7751 var consumed = m[0];
7752 remaining = remaining.substring(consumed.length);
7753 break; // we've consumed one expr, so we can return now
7754 }
7755 }
7756
7757 return {
7758 expr: expr,
7759 match: match,
7760 name: name,
7761 remaining: remaining
7762 };
7763};
7764/**
7765 * Consume all the leading whitespace
7766 * @param {string} remaining The text to consume
7767 * @returns The text with the leading whitespace removed
7768 */
7769
7770
7771var consumeWhitespace = function consumeWhitespace(remaining) {
7772 var match = remaining.match(/^\s+/);
7773
7774 if (match) {
7775 var consumed = match[0];
7776 remaining = remaining.substring(consumed.length);
7777 }
7778
7779 return remaining;
7780};
7781/**
7782 * Parse the string and store the parsed representation in the Selector.
7783 * @param {string} selector The selector string
7784 * @returns `true` if the selector was successfully parsed, `false` otherwise
7785 */
7786
7787
7788var parse = function parse(selector) {
7789 var self = this;
7790 var remaining = self.inputText = selector;
7791 var currentQuery = self[0] = newQuery();
7792 self.length = 1;
7793 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7794
7795 for (;;) {
7796 var exprInfo = consumeExpr(remaining);
7797
7798 if (exprInfo.expr == null) {
7799 warn('The selector `' + selector + '`is invalid');
7800 return false;
7801 } else {
7802 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7803
7804 var ret = exprInfo.expr.populate(self, currentQuery, args);
7805
7806 if (ret === false) {
7807 return false; // exit if population failed
7808 } else if (ret != null) {
7809 currentQuery = ret; // change the current query to be filled if the expr specifies
7810 }
7811 }
7812
7813 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7814
7815 if (remaining.match(/^\s*$/)) {
7816 break;
7817 }
7818 }
7819
7820 var lastQ = self[self.length - 1];
7821
7822 if (self.currentSubject != null) {
7823 lastQ.subject = self.currentSubject;
7824 }
7825
7826 lastQ.edgeCount = self.edgeCount;
7827 lastQ.compoundCount = self.compoundCount;
7828
7829 for (var i = 0; i < self.length; i++) {
7830 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7831
7832 if (q.compoundCount > 0 && q.edgeCount > 0) {
7833 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7834 return false;
7835 }
7836
7837 if (q.edgeCount > 1) {
7838 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7839 return false;
7840 } else if (q.edgeCount === 1) {
7841 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.');
7842 }
7843 }
7844
7845 return true; // success
7846};
7847/**
7848 * Get the selector represented as a string. This value uses default formatting,
7849 * so things like spacing may differ from the input text passed to the constructor.
7850 * @returns {string} The selector string
7851 */
7852
7853
7854var toString = function toString() {
7855 if (this.toStringCache != null) {
7856 return this.toStringCache;
7857 }
7858
7859 var clean = function clean(obj) {
7860 if (obj == null) {
7861 return '';
7862 } else {
7863 return obj;
7864 }
7865 };
7866
7867 var cleanVal = function cleanVal(val) {
7868 if (string(val)) {
7869 return '"' + val + '"';
7870 } else {
7871 return clean(val);
7872 }
7873 };
7874
7875 var space = function space(val) {
7876 return ' ' + val + ' ';
7877 };
7878
7879 var checkToString = function checkToString(check, subject) {
7880 var type = check.type,
7881 value = check.value;
7882
7883 switch (type) {
7884 case Type.GROUP:
7885 {
7886 var group = clean(value);
7887 return group.substring(0, group.length - 1);
7888 }
7889
7890 case Type.DATA_COMPARE:
7891 {
7892 var field = check.field,
7893 operator = check.operator;
7894 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7895 }
7896
7897 case Type.DATA_BOOL:
7898 {
7899 var _operator = check.operator,
7900 _field = check.field;
7901 return '[' + clean(_operator) + _field + ']';
7902 }
7903
7904 case Type.DATA_EXIST:
7905 {
7906 var _field2 = check.field;
7907 return '[' + _field2 + ']';
7908 }
7909
7910 case Type.META_COMPARE:
7911 {
7912 var _operator2 = check.operator,
7913 _field3 = check.field;
7914 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7915 }
7916
7917 case Type.STATE:
7918 {
7919 return value;
7920 }
7921
7922 case Type.ID:
7923 {
7924 return '#' + value;
7925 }
7926
7927 case Type.CLASS:
7928 {
7929 return '.' + value;
7930 }
7931
7932 case Type.PARENT:
7933 case Type.CHILD:
7934 {
7935 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7936 }
7937
7938 case Type.ANCESTOR:
7939 case Type.DESCENDANT:
7940 {
7941 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7942 }
7943
7944 case Type.COMPOUND_SPLIT:
7945 {
7946 var lhs = queryToString(check.left, subject);
7947 var sub = queryToString(check.subject, subject);
7948 var rhs = queryToString(check.right, subject);
7949 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7950 }
7951
7952 case Type.TRUE:
7953 {
7954 return '';
7955 }
7956 }
7957 };
7958
7959 var queryToString = function queryToString(query, subject) {
7960 return query.checks.reduce(function (str, chk, i) {
7961 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7962 }, '');
7963 };
7964
7965 var str = '';
7966
7967 for (var i = 0; i < this.length; i++) {
7968 var query = this[i];
7969 str += queryToString(query, query.subject);
7970
7971 if (this.length > 1 && i < this.length - 1) {
7972 str += ', ';
7973 }
7974 }
7975
7976 this.toStringCache = str;
7977 return str;
7978};
7979var parse$1 = {
7980 parse: parse,
7981 toString: toString
7982};
7983
7984var valCmp = function valCmp(fieldVal, operator, value) {
7985 var matches;
7986 var isFieldStr = string(fieldVal);
7987 var isFieldNum = number(fieldVal);
7988 var isValStr = string(value);
7989 var fieldStr, valStr;
7990 var caseInsensitive = false;
7991 var notExpr = false;
7992 var isIneqCmp = false;
7993
7994 if (operator.indexOf('!') >= 0) {
7995 operator = operator.replace('!', '');
7996 notExpr = true;
7997 }
7998
7999 if (operator.indexOf('@') >= 0) {
8000 operator = operator.replace('@', '');
8001 caseInsensitive = true;
8002 }
8003
8004 if (isFieldStr || isValStr || caseInsensitive) {
8005 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
8006 valStr = '' + value;
8007 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
8008 // even if we're comparing numbers
8009
8010
8011 if (caseInsensitive) {
8012 fieldVal = fieldStr = fieldStr.toLowerCase();
8013 value = valStr = valStr.toLowerCase();
8014 }
8015
8016 switch (operator) {
8017 case '*=':
8018 matches = fieldStr.indexOf(valStr) >= 0;
8019 break;
8020
8021 case '$=':
8022 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
8023 break;
8024
8025 case '^=':
8026 matches = fieldStr.indexOf(valStr) === 0;
8027 break;
8028
8029 case '=':
8030 matches = fieldVal === value;
8031 break;
8032
8033 case '>':
8034 isIneqCmp = true;
8035 matches = fieldVal > value;
8036 break;
8037
8038 case '>=':
8039 isIneqCmp = true;
8040 matches = fieldVal >= value;
8041 break;
8042
8043 case '<':
8044 isIneqCmp = true;
8045 matches = fieldVal < value;
8046 break;
8047
8048 case '<=':
8049 isIneqCmp = true;
8050 matches = fieldVal <= value;
8051 break;
8052
8053 default:
8054 matches = false;
8055 break;
8056 } // apply the not op, but null vals for inequalities should always stay non-matching
8057
8058
8059 if (notExpr && (fieldVal != null || !isIneqCmp)) {
8060 matches = !matches;
8061 }
8062
8063 return matches;
8064};
8065var boolCmp = function boolCmp(fieldVal, operator) {
8066 switch (operator) {
8067 case '?':
8068 return fieldVal ? true : false;
8069
8070 case '!':
8071 return fieldVal ? false : true;
8072
8073 case '^':
8074 return fieldVal === undefined;
8075 }
8076};
8077var existCmp = function existCmp(fieldVal) {
8078 return fieldVal !== undefined;
8079};
8080var data = function data(ele, field) {
8081 return ele.data(field);
8082};
8083var meta = function meta(ele, field) {
8084 return ele[field]();
8085};
8086
8087/** A lookup of `match(check, ele)` functions by `Type` int */
8088
8089var match = [];
8090/**
8091 * Returns whether the query matches for the element
8092 * @param query The `{ type, value, ... }` query object
8093 * @param ele The element to compare against
8094*/
8095
8096var matches = function matches(query, ele) {
8097 return query.checks.every(function (chk) {
8098 return match[chk.type](chk, ele);
8099 });
8100};
8101
8102match[Type.GROUP] = function (check, ele) {
8103 var group = check.value;
8104 return group === '*' || group === ele.group();
8105};
8106
8107match[Type.STATE] = function (check, ele) {
8108 var stateSelector = check.value;
8109 return stateSelectorMatches(stateSelector, ele);
8110};
8111
8112match[Type.ID] = function (check, ele) {
8113 var id = check.value;
8114 return ele.id() === id;
8115};
8116
8117match[Type.CLASS] = function (check, ele) {
8118 var cls = check.value;
8119 return ele.hasClass(cls);
8120};
8121
8122match[Type.META_COMPARE] = function (check, ele) {
8123 var field = check.field,
8124 operator = check.operator,
8125 value = check.value;
8126 return valCmp(meta(ele, field), operator, value);
8127};
8128
8129match[Type.DATA_COMPARE] = function (check, ele) {
8130 var field = check.field,
8131 operator = check.operator,
8132 value = check.value;
8133 return valCmp(data(ele, field), operator, value);
8134};
8135
8136match[Type.DATA_BOOL] = function (check, ele) {
8137 var field = check.field,
8138 operator = check.operator;
8139 return boolCmp(data(ele, field), operator);
8140};
8141
8142match[Type.DATA_EXIST] = function (check, ele) {
8143 var field = check.field,
8144 operator = check.operator;
8145 return existCmp(data(ele, field));
8146};
8147
8148match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8149 var qA = check.nodes[0];
8150 var qB = check.nodes[1];
8151 var src = ele.source();
8152 var tgt = ele.target();
8153 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8154};
8155
8156match[Type.NODE_NEIGHBOR] = function (check, ele) {
8157 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8158 return n.isNode() && matches(check.neighbor, n);
8159 });
8160};
8161
8162match[Type.DIRECTED_EDGE] = function (check, ele) {
8163 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8164};
8165
8166match[Type.NODE_SOURCE] = function (check, ele) {
8167 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8168 return n.isNode() && matches(check.target, n);
8169 });
8170};
8171
8172match[Type.NODE_TARGET] = function (check, ele) {
8173 return matches(check.target, ele) && ele.incomers().some(function (n) {
8174 return n.isNode() && matches(check.source, n);
8175 });
8176};
8177
8178match[Type.CHILD] = function (check, ele) {
8179 return matches(check.child, ele) && matches(check.parent, ele.parent());
8180};
8181
8182match[Type.PARENT] = function (check, ele) {
8183 return matches(check.parent, ele) && ele.children().some(function (c) {
8184 return matches(check.child, c);
8185 });
8186};
8187
8188match[Type.DESCENDANT] = function (check, ele) {
8189 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8190 return matches(check.ancestor, a);
8191 });
8192};
8193
8194match[Type.ANCESTOR] = function (check, ele) {
8195 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8196 return matches(check.descendant, d);
8197 });
8198};
8199
8200match[Type.COMPOUND_SPLIT] = function (check, ele) {
8201 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8202};
8203
8204match[Type.TRUE] = function () {
8205 return true;
8206};
8207
8208match[Type.COLLECTION] = function (check, ele) {
8209 var collection = check.value;
8210 return collection.has(ele);
8211};
8212
8213match[Type.FILTER] = function (check, ele) {
8214 var filter = check.value;
8215 return filter(ele);
8216};
8217
8218var filter = function filter(collection) {
8219 var self = this; // for 1 id #foo queries, just get the element
8220
8221 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8222 return collection.getElementById(self[0].checks[0].value).collection();
8223 }
8224
8225 var selectorFunction = function selectorFunction(element) {
8226 for (var j = 0; j < self.length; j++) {
8227 var query = self[j];
8228
8229 if (matches(query, element)) {
8230 return true;
8231 }
8232 }
8233
8234 return false;
8235 };
8236
8237 if (self.text() == null) {
8238 selectorFunction = function selectorFunction() {
8239 return true;
8240 };
8241 }
8242
8243 return collection.filter(selectorFunction);
8244}; // filter
8245// does selector match a single element?
8246
8247
8248var matches$1 = function matches$1(ele) {
8249 var self = this;
8250
8251 for (var j = 0; j < self.length; j++) {
8252 var query = self[j];
8253
8254 if (matches(query, ele)) {
8255 return true;
8256 }
8257 }
8258
8259 return false;
8260}; // matches
8261
8262
8263var matching = {
8264 matches: matches$1,
8265 filter: filter
8266};
8267
8268var Selector = function Selector(selector) {
8269 this.inputText = selector;
8270 this.currentSubject = null;
8271 this.compoundCount = 0;
8272 this.edgeCount = 0;
8273 this.length = 0;
8274
8275 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8276 this.addQuery({
8277 checks: [{
8278 type: Type.COLLECTION,
8279 value: selector.collection()
8280 }]
8281 });
8282 } else if (fn(selector)) {
8283 this.addQuery({
8284 checks: [{
8285 type: Type.FILTER,
8286 value: selector
8287 }]
8288 });
8289 } else if (string(selector)) {
8290 if (!this.parse(selector)) {
8291 this.invalid = true;
8292 }
8293 } else {
8294 error('A selector must be created from a string; found ');
8295 }
8296};
8297
8298var selfn = Selector.prototype;
8299[parse$1, matching].forEach(function (p) {
8300 return extend(selfn, p);
8301});
8302
8303selfn.text = function () {
8304 return this.inputText;
8305};
8306
8307selfn.size = function () {
8308 return this.length;
8309};
8310
8311selfn.eq = function (i) {
8312 return this[i];
8313};
8314
8315selfn.sameText = function (otherSel) {
8316 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8317};
8318
8319selfn.addQuery = function (q) {
8320 this[this.length++] = q;
8321};
8322
8323selfn.selector = selfn.toString;
8324
8325var elesfn$f = {
8326 allAre: function allAre(selector) {
8327 var selObj = new Selector(selector);
8328 return this.every(function (ele) {
8329 return selObj.matches(ele);
8330 });
8331 },
8332 is: function is(selector) {
8333 var selObj = new Selector(selector);
8334 return this.some(function (ele) {
8335 return selObj.matches(ele);
8336 });
8337 },
8338 some: function some(fn, thisArg) {
8339 for (var i = 0; i < this.length; i++) {
8340 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8341
8342 if (ret) {
8343 return true;
8344 }
8345 }
8346
8347 return false;
8348 },
8349 every: function every(fn, thisArg) {
8350 for (var i = 0; i < this.length; i++) {
8351 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8352
8353 if (!ret) {
8354 return false;
8355 }
8356 }
8357
8358 return true;
8359 },
8360 same: function same(collection) {
8361 // cheap collection ref check
8362 if (this === collection) {
8363 return true;
8364 }
8365
8366 collection = this.cy().collection(collection);
8367 var thisLength = this.length;
8368 var collectionLength = collection.length; // cheap length check
8369
8370 if (thisLength !== collectionLength) {
8371 return false;
8372 } // cheap element ref check
8373
8374
8375 if (thisLength === 1) {
8376 return this[0] === collection[0];
8377 }
8378
8379 return this.every(function (ele) {
8380 return collection.hasElementWithId(ele.id());
8381 });
8382 },
8383 anySame: function anySame(collection) {
8384 collection = this.cy().collection(collection);
8385 return this.some(function (ele) {
8386 return collection.hasElementWithId(ele.id());
8387 });
8388 },
8389 allAreNeighbors: function allAreNeighbors(collection) {
8390 collection = this.cy().collection(collection);
8391 var nhood = this.neighborhood();
8392 return collection.every(function (ele) {
8393 return nhood.hasElementWithId(ele.id());
8394 });
8395 },
8396 contains: function contains(collection) {
8397 collection = this.cy().collection(collection);
8398 var self = this;
8399 return collection.every(function (ele) {
8400 return self.hasElementWithId(ele.id());
8401 });
8402 }
8403};
8404elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8405elesfn$f.has = elesfn$f.contains;
8406elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8407
8408var cache = function cache(fn, name) {
8409 return function traversalCache(arg1, arg2, arg3, arg4) {
8410 var selectorOrEles = arg1;
8411 var eles = this;
8412 var key;
8413
8414 if (selectorOrEles == null) {
8415 key = '';
8416 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8417 key = selectorOrEles.id();
8418 }
8419
8420 if (eles.length === 1 && key) {
8421 var _p = eles[0]._private;
8422 var tch = _p.traversalCache = _p.traversalCache || {};
8423 var ch = tch[name] = tch[name] || [];
8424 var hash = hashString(key);
8425 var cacheHit = ch[hash];
8426
8427 if (cacheHit) {
8428 return cacheHit;
8429 } else {
8430 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8431 }
8432 } else {
8433 return fn.call(eles, arg1, arg2, arg3, arg4);
8434 }
8435 };
8436};
8437
8438var elesfn$g = {
8439 parent: function parent(selector) {
8440 var parents = []; // optimisation for single ele call
8441
8442 if (this.length === 1) {
8443 var parent = this[0]._private.parent;
8444
8445 if (parent) {
8446 return parent;
8447 }
8448 }
8449
8450 for (var i = 0; i < this.length; i++) {
8451 var ele = this[i];
8452 var _parent = ele._private.parent;
8453
8454 if (_parent) {
8455 parents.push(_parent);
8456 }
8457 }
8458
8459 return this.spawn(parents, true).filter(selector);
8460 },
8461 parents: function parents(selector) {
8462 var parents = [];
8463 var eles = this.parent();
8464
8465 while (eles.nonempty()) {
8466 for (var i = 0; i < eles.length; i++) {
8467 var ele = eles[i];
8468 parents.push(ele);
8469 }
8470
8471 eles = eles.parent();
8472 }
8473
8474 return this.spawn(parents, true).filter(selector);
8475 },
8476 commonAncestors: function commonAncestors(selector) {
8477 var ancestors;
8478
8479 for (var i = 0; i < this.length; i++) {
8480 var ele = this[i];
8481 var parents = ele.parents();
8482 ancestors = ancestors || parents;
8483 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8484 }
8485
8486 return ancestors.filter(selector);
8487 },
8488 orphans: function orphans(selector) {
8489 return this.stdFilter(function (ele) {
8490 return ele.isOrphan();
8491 }).filter(selector);
8492 },
8493 nonorphans: function nonorphans(selector) {
8494 return this.stdFilter(function (ele) {
8495 return ele.isChild();
8496 }).filter(selector);
8497 },
8498 children: cache(function (selector) {
8499 var children = [];
8500
8501 for (var i = 0; i < this.length; i++) {
8502 var ele = this[i];
8503 var eleChildren = ele._private.children;
8504
8505 for (var j = 0; j < eleChildren.length; j++) {
8506 children.push(eleChildren[j]);
8507 }
8508 }
8509
8510 return this.spawn(children, true).filter(selector);
8511 }, 'children'),
8512 siblings: function siblings(selector) {
8513 return this.parent().children().not(this).filter(selector);
8514 },
8515 isParent: function isParent() {
8516 var ele = this[0];
8517
8518 if (ele) {
8519 return ele.isNode() && ele._private.children.length !== 0;
8520 }
8521 },
8522 isChildless: function isChildless() {
8523 var ele = this[0];
8524
8525 if (ele) {
8526 return ele.isNode() && ele._private.children.length === 0;
8527 }
8528 },
8529 isChild: function isChild() {
8530 var ele = this[0];
8531
8532 if (ele) {
8533 return ele.isNode() && ele._private.parent != null;
8534 }
8535 },
8536 isOrphan: function isOrphan() {
8537 var ele = this[0];
8538
8539 if (ele) {
8540 return ele.isNode() && ele._private.parent == null;
8541 }
8542 },
8543 descendants: function descendants(selector) {
8544 var elements = [];
8545
8546 function add(eles) {
8547 for (var i = 0; i < eles.length; i++) {
8548 var ele = eles[i];
8549 elements.push(ele);
8550
8551 if (ele.children().nonempty()) {
8552 add(ele.children());
8553 }
8554 }
8555 }
8556
8557 add(this.children());
8558 return this.spawn(elements, true).filter(selector);
8559 }
8560};
8561
8562function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8563 var q = [];
8564 var did = new Set$1();
8565 var cy = eles.cy();
8566 var hasCompounds = cy.hasCompoundNodes();
8567
8568 for (var i = 0; i < eles.length; i++) {
8569 var ele = eles[i];
8570
8571 if (includeSelf) {
8572 q.push(ele);
8573 } else if (hasCompounds) {
8574 recursiveStep(q, did, ele);
8575 }
8576 }
8577
8578 while (q.length > 0) {
8579 var _ele = q.shift();
8580
8581 fn(_ele);
8582 did.add(_ele.id());
8583
8584 if (hasCompounds) {
8585 recursiveStep(q, did, _ele);
8586 }
8587 }
8588
8589 return eles;
8590}
8591
8592function addChildren(q, did, ele) {
8593 if (ele.isParent()) {
8594 var children = ele._private.children;
8595
8596 for (var i = 0; i < children.length; i++) {
8597 var child = children[i];
8598
8599 if (!did.has(child.id())) {
8600 q.push(child);
8601 }
8602 }
8603 }
8604} // very efficient version of eles.add( eles.descendants() ).forEach()
8605// for internal use
8606
8607
8608elesfn$g.forEachDown = function (fn) {
8609 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8610 return forEachCompound(this, fn, includeSelf, addChildren);
8611};
8612
8613function addParent(q, did, ele) {
8614 if (ele.isChild()) {
8615 var parent = ele._private.parent;
8616
8617 if (!did.has(parent.id())) {
8618 q.push(parent);
8619 }
8620 }
8621}
8622
8623elesfn$g.forEachUp = function (fn) {
8624 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8625 return forEachCompound(this, fn, includeSelf, addParent);
8626};
8627
8628function addParentAndChildren(q, did, ele) {
8629 addParent(q, did, ele);
8630 addChildren(q, did, ele);
8631}
8632
8633elesfn$g.forEachUpAndDown = function (fn) {
8634 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8635 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8636}; // aliases
8637
8638
8639elesfn$g.ancestors = elesfn$g.parents;
8640
8641var fn$1, elesfn$h;
8642fn$1 = elesfn$h = {
8643 data: define$3.data({
8644 field: 'data',
8645 bindingEvent: 'data',
8646 allowBinding: true,
8647 allowSetting: true,
8648 settingEvent: 'data',
8649 settingTriggersEvent: true,
8650 triggerFnName: 'trigger',
8651 allowGetting: true,
8652 immutableKeys: {
8653 'id': true,
8654 'source': true,
8655 'target': true,
8656 'parent': true
8657 },
8658 updateStyle: true
8659 }),
8660 removeData: define$3.removeData({
8661 field: 'data',
8662 event: 'data',
8663 triggerFnName: 'trigger',
8664 triggerEvent: true,
8665 immutableKeys: {
8666 'id': true,
8667 'source': true,
8668 'target': true,
8669 'parent': true
8670 },
8671 updateStyle: true
8672 }),
8673 scratch: define$3.data({
8674 field: 'scratch',
8675 bindingEvent: 'scratch',
8676 allowBinding: true,
8677 allowSetting: true,
8678 settingEvent: 'scratch',
8679 settingTriggersEvent: true,
8680 triggerFnName: 'trigger',
8681 allowGetting: true,
8682 updateStyle: true
8683 }),
8684 removeScratch: define$3.removeData({
8685 field: 'scratch',
8686 event: 'scratch',
8687 triggerFnName: 'trigger',
8688 triggerEvent: true,
8689 updateStyle: true
8690 }),
8691 rscratch: define$3.data({
8692 field: 'rscratch',
8693 allowBinding: false,
8694 allowSetting: true,
8695 settingTriggersEvent: false,
8696 allowGetting: true
8697 }),
8698 removeRscratch: define$3.removeData({
8699 field: 'rscratch',
8700 triggerEvent: false
8701 }),
8702 id: function id() {
8703 var ele = this[0];
8704
8705 if (ele) {
8706 return ele._private.data.id;
8707 }
8708 }
8709}; // aliases
8710
8711fn$1.attr = fn$1.data;
8712fn$1.removeAttr = fn$1.removeData;
8713var data$1 = elesfn$h;
8714
8715var elesfn$i = {};
8716
8717function defineDegreeFunction(callback) {
8718 return function (includeLoops) {
8719 var self = this;
8720
8721 if (includeLoops === undefined) {
8722 includeLoops = true;
8723 }
8724
8725 if (self.length === 0) {
8726 return;
8727 }
8728
8729 if (self.isNode() && !self.removed()) {
8730 var degree = 0;
8731 var node = self[0];
8732 var connectedEdges = node._private.edges;
8733
8734 for (var i = 0; i < connectedEdges.length; i++) {
8735 var edge = connectedEdges[i];
8736
8737 if (!includeLoops && edge.isLoop()) {
8738 continue;
8739 }
8740
8741 degree += callback(node, edge);
8742 }
8743
8744 return degree;
8745 } else {
8746 return;
8747 }
8748 };
8749}
8750
8751extend(elesfn$i, {
8752 degree: defineDegreeFunction(function (node, edge) {
8753 if (edge.source().same(edge.target())) {
8754 return 2;
8755 } else {
8756 return 1;
8757 }
8758 }),
8759 indegree: defineDegreeFunction(function (node, edge) {
8760 if (edge.target().same(node)) {
8761 return 1;
8762 } else {
8763 return 0;
8764 }
8765 }),
8766 outdegree: defineDegreeFunction(function (node, edge) {
8767 if (edge.source().same(node)) {
8768 return 1;
8769 } else {
8770 return 0;
8771 }
8772 })
8773});
8774
8775function defineDegreeBoundsFunction(degreeFn, callback) {
8776 return function (includeLoops) {
8777 var ret;
8778 var nodes = this.nodes();
8779
8780 for (var i = 0; i < nodes.length; i++) {
8781 var ele = nodes[i];
8782 var degree = ele[degreeFn](includeLoops);
8783
8784 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8785 ret = degree;
8786 }
8787 }
8788
8789 return ret;
8790 };
8791}
8792
8793extend(elesfn$i, {
8794 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8795 return degree < min;
8796 }),
8797 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8798 return degree > max;
8799 }),
8800 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8801 return degree < min;
8802 }),
8803 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8804 return degree > max;
8805 }),
8806 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8807 return degree < min;
8808 }),
8809 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8810 return degree > max;
8811 })
8812});
8813extend(elesfn$i, {
8814 totalDegree: function totalDegree(includeLoops) {
8815 var total = 0;
8816 var nodes = this.nodes();
8817
8818 for (var i = 0; i < nodes.length; i++) {
8819 total += nodes[i].degree(includeLoops);
8820 }
8821
8822 return total;
8823 }
8824});
8825
8826var fn$2, elesfn$j;
8827
8828var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8829 for (var i = 0; i < eles.length; i++) {
8830 var ele = eles[i];
8831
8832 if (!ele.locked()) {
8833 var oldPos = ele._private.position;
8834 var delta = {
8835 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8836 y: newPos.y != null ? newPos.y - oldPos.y : 0
8837 };
8838
8839 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8840 ele.children().shift(delta, silent);
8841 }
8842
8843 ele.dirtyBoundingBoxCache();
8844 }
8845 }
8846};
8847
8848var positionDef = {
8849 field: 'position',
8850 bindingEvent: 'position',
8851 allowBinding: true,
8852 allowSetting: true,
8853 settingEvent: 'position',
8854 settingTriggersEvent: true,
8855 triggerFnName: 'emitAndNotify',
8856 allowGetting: true,
8857 validKeys: ['x', 'y'],
8858 beforeGet: function beforeGet(ele) {
8859 ele.updateCompoundBounds();
8860 },
8861 beforeSet: function beforeSet(eles, newPos) {
8862 beforePositionSet(eles, newPos, false);
8863 },
8864 onSet: function onSet(eles) {
8865 eles.dirtyCompoundBoundsCache();
8866 },
8867 canSet: function canSet(ele) {
8868 return !ele.locked();
8869 }
8870};
8871fn$2 = elesfn$j = {
8872 position: define$3.data(positionDef),
8873 // position but no notification to renderer
8874 silentPosition: define$3.data(extend({}, positionDef, {
8875 allowBinding: false,
8876 allowSetting: true,
8877 settingTriggersEvent: false,
8878 allowGetting: false,
8879 beforeSet: function beforeSet(eles, newPos) {
8880 beforePositionSet(eles, newPos, true);
8881 },
8882 onSet: function onSet(eles) {
8883 eles.dirtyCompoundBoundsCache();
8884 }
8885 })),
8886 positions: function positions(pos, silent) {
8887 if (plainObject(pos)) {
8888 if (silent) {
8889 this.silentPosition(pos);
8890 } else {
8891 this.position(pos);
8892 }
8893 } else if (fn(pos)) {
8894 var _fn = pos;
8895 var cy = this.cy();
8896 cy.startBatch();
8897
8898 for (var i = 0; i < this.length; i++) {
8899 var ele = this[i];
8900
8901 var _pos = void 0;
8902
8903 if (_pos = _fn(ele, i)) {
8904 if (silent) {
8905 ele.silentPosition(_pos);
8906 } else {
8907 ele.position(_pos);
8908 }
8909 }
8910 }
8911
8912 cy.endBatch();
8913 }
8914
8915 return this; // chaining
8916 },
8917 silentPositions: function silentPositions(pos) {
8918 return this.positions(pos, true);
8919 },
8920 shift: function shift(dim, val, silent) {
8921 var delta;
8922
8923 if (plainObject(dim)) {
8924 delta = {
8925 x: number(dim.x) ? dim.x : 0,
8926 y: number(dim.y) ? dim.y : 0
8927 };
8928 silent = val;
8929 } else if (string(dim) && number(val)) {
8930 delta = {
8931 x: 0,
8932 y: 0
8933 };
8934 delta[dim] = val;
8935 }
8936
8937 if (delta != null) {
8938 var cy = this.cy();
8939 cy.startBatch();
8940
8941 for (var i = 0; i < this.length; i++) {
8942 var ele = this[i]; // exclude any node that is a descendant of the calling collection
8943
8944 if (cy.hasCompoundNodes() && ele.isChild() && ele.ancestors().anySame(this)) {
8945 continue;
8946 }
8947
8948 var pos = ele.position();
8949 var newPos = {
8950 x: pos.x + delta.x,
8951 y: pos.y + delta.y
8952 };
8953
8954 if (silent) {
8955 ele.silentPosition(newPos);
8956 } else {
8957 ele.position(newPos);
8958 }
8959 }
8960
8961 cy.endBatch();
8962 }
8963
8964 return this;
8965 },
8966 silentShift: function silentShift(dim, val) {
8967 if (plainObject(dim)) {
8968 this.shift(dim, true);
8969 } else if (string(dim) && number(val)) {
8970 this.shift(dim, val, true);
8971 }
8972
8973 return this;
8974 },
8975 // get/set the rendered (i.e. on screen) positon of the element
8976 renderedPosition: function renderedPosition(dim, val) {
8977 var ele = this[0];
8978 var cy = this.cy();
8979 var zoom = cy.zoom();
8980 var pan = cy.pan();
8981 var rpos = plainObject(dim) ? dim : undefined;
8982 var setting = rpos !== undefined || val !== undefined && string(dim);
8983
8984 if (ele && ele.isNode()) {
8985 // must have an element and must be a node to return position
8986 if (setting) {
8987 for (var i = 0; i < this.length; i++) {
8988 var _ele = this[i];
8989
8990 if (val !== undefined) {
8991 // set one dimension
8992 _ele.position(dim, (val - pan[dim]) / zoom);
8993 } else if (rpos !== undefined) {
8994 // set whole position
8995 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8996 }
8997 }
8998 } else {
8999 // getting
9000 var pos = ele.position();
9001 rpos = modelToRenderedPosition(pos, zoom, pan);
9002
9003 if (dim === undefined) {
9004 // then return the whole rendered position
9005 return rpos;
9006 } else {
9007 // then return the specified dimension
9008 return rpos[dim];
9009 }
9010 }
9011 } else if (!setting) {
9012 return undefined; // for empty collection case
9013 }
9014
9015 return this; // chaining
9016 },
9017 // get/set the position relative to the parent
9018 relativePosition: function relativePosition(dim, val) {
9019 var ele = this[0];
9020 var cy = this.cy();
9021 var ppos = plainObject(dim) ? dim : undefined;
9022 var setting = ppos !== undefined || val !== undefined && string(dim);
9023 var hasCompoundNodes = cy.hasCompoundNodes();
9024
9025 if (ele && ele.isNode()) {
9026 // must have an element and must be a node to return position
9027 if (setting) {
9028 for (var i = 0; i < this.length; i++) {
9029 var _ele2 = this[i];
9030 var parent = hasCompoundNodes ? _ele2.parent() : null;
9031 var hasParent = parent && parent.length > 0;
9032 var relativeToParent = hasParent;
9033
9034 if (hasParent) {
9035 parent = parent[0];
9036 }
9037
9038 var origin = relativeToParent ? parent.position() : {
9039 x: 0,
9040 y: 0
9041 };
9042
9043 if (val !== undefined) {
9044 // set one dimension
9045 _ele2.position(dim, val + origin[dim]);
9046 } else if (ppos !== undefined) {
9047 // set whole position
9048 _ele2.position({
9049 x: ppos.x + origin.x,
9050 y: ppos.y + origin.y
9051 });
9052 }
9053 }
9054 } else {
9055 // getting
9056 var pos = ele.position();
9057
9058 var _parent = hasCompoundNodes ? ele.parent() : null;
9059
9060 var _hasParent = _parent && _parent.length > 0;
9061
9062 var _relativeToParent = _hasParent;
9063
9064 if (_hasParent) {
9065 _parent = _parent[0];
9066 }
9067
9068 var _origin = _relativeToParent ? _parent.position() : {
9069 x: 0,
9070 y: 0
9071 };
9072
9073 ppos = {
9074 x: pos.x - _origin.x,
9075 y: pos.y - _origin.y
9076 };
9077
9078 if (dim === undefined) {
9079 // then return the whole rendered position
9080 return ppos;
9081 } else {
9082 // then return the specified dimension
9083 return ppos[dim];
9084 }
9085 }
9086 } else if (!setting) {
9087 return undefined; // for empty collection case
9088 }
9089
9090 return this; // chaining
9091 }
9092}; // aliases
9093
9094fn$2.modelPosition = fn$2.point = fn$2.position;
9095fn$2.modelPositions = fn$2.points = fn$2.positions;
9096fn$2.renderedPoint = fn$2.renderedPosition;
9097fn$2.relativePoint = fn$2.relativePosition;
9098var position = elesfn$j;
9099
9100var fn$3, elesfn$k;
9101fn$3 = elesfn$k = {};
9102
9103elesfn$k.renderedBoundingBox = function (options) {
9104 var bb = this.boundingBox(options);
9105 var cy = this.cy();
9106 var zoom = cy.zoom();
9107 var pan = cy.pan();
9108 var x1 = bb.x1 * zoom + pan.x;
9109 var x2 = bb.x2 * zoom + pan.x;
9110 var y1 = bb.y1 * zoom + pan.y;
9111 var y2 = bb.y2 * zoom + pan.y;
9112 return {
9113 x1: x1,
9114 x2: x2,
9115 y1: y1,
9116 y2: y2,
9117 w: x2 - x1,
9118 h: y2 - y1
9119 };
9120};
9121
9122elesfn$k.dirtyCompoundBoundsCache = function () {
9123 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9124 var cy = this.cy();
9125
9126 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9127 return this;
9128 }
9129
9130 this.forEachUp(function (ele) {
9131 if (ele.isParent()) {
9132 var _p = ele._private;
9133 _p.compoundBoundsClean = false;
9134 _p.bbCache = null;
9135
9136 if (!silent) {
9137 ele.emitAndNotify('bounds');
9138 }
9139 }
9140 });
9141 return this;
9142};
9143
9144elesfn$k.updateCompoundBounds = function () {
9145 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9146 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9147
9148 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9149 return this;
9150 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9151
9152
9153 if (!force && cy.batching()) {
9154 return this;
9155 }
9156
9157 function update(parent) {
9158 if (!parent.isParent()) {
9159 return;
9160 }
9161
9162 var _p = parent._private;
9163 var children = parent.children();
9164 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9165 var min = {
9166 width: {
9167 val: parent.pstyle('min-width').pfValue,
9168 left: parent.pstyle('min-width-bias-left'),
9169 right: parent.pstyle('min-width-bias-right')
9170 },
9171 height: {
9172 val: parent.pstyle('min-height').pfValue,
9173 top: parent.pstyle('min-height-bias-top'),
9174 bottom: parent.pstyle('min-height-bias-bottom')
9175 }
9176 };
9177 var bb = children.boundingBox({
9178 includeLabels: includeLabels,
9179 includeOverlays: false,
9180 // updating the compound bounds happens outside of the regular
9181 // cache cycle (i.e. before fired events)
9182 useCache: false
9183 });
9184 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9185
9186 if (bb.w === 0 || bb.h === 0) {
9187 bb = {
9188 w: parent.pstyle('width').pfValue,
9189 h: parent.pstyle('height').pfValue
9190 };
9191 bb.x1 = pos.x - bb.w / 2;
9192 bb.x2 = pos.x + bb.w / 2;
9193 bb.y1 = pos.y - bb.h / 2;
9194 bb.y2 = pos.y + bb.h / 2;
9195 }
9196
9197 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9198 var biasDiff = 0;
9199 var biasComplementDiff = 0;
9200 var biasTotal = propBias + propBiasComplement;
9201
9202 if (propDiff > 0 && biasTotal > 0) {
9203 biasDiff = propBias / biasTotal * propDiff;
9204 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9205 }
9206
9207 return {
9208 biasDiff: biasDiff,
9209 biasComplementDiff: biasComplementDiff
9210 };
9211 }
9212
9213 function computePaddingValues(width, height, paddingObject, relativeTo) {
9214 // Assuming percentage is number from 0 to 1
9215 if (paddingObject.units === '%') {
9216 switch (relativeTo) {
9217 case 'width':
9218 return width > 0 ? paddingObject.pfValue * width : 0;
9219
9220 case 'height':
9221 return height > 0 ? paddingObject.pfValue * height : 0;
9222
9223 case 'average':
9224 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9225
9226 case 'min':
9227 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9228
9229 case 'max':
9230 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9231
9232 default:
9233 return 0;
9234 }
9235 } else if (paddingObject.units === 'px') {
9236 return paddingObject.pfValue;
9237 } else {
9238 return 0;
9239 }
9240 }
9241
9242 var leftVal = min.width.left.value;
9243
9244 if (min.width.left.units === 'px' && min.width.val > 0) {
9245 leftVal = leftVal * 100 / min.width.val;
9246 }
9247
9248 var rightVal = min.width.right.value;
9249
9250 if (min.width.right.units === 'px' && min.width.val > 0) {
9251 rightVal = rightVal * 100 / min.width.val;
9252 }
9253
9254 var topVal = min.height.top.value;
9255
9256 if (min.height.top.units === 'px' && min.height.val > 0) {
9257 topVal = topVal * 100 / min.height.val;
9258 }
9259
9260 var bottomVal = min.height.bottom.value;
9261
9262 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9263 bottomVal = bottomVal * 100 / min.height.val;
9264 }
9265
9266 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9267 var diffLeft = widthBiasDiffs.biasDiff;
9268 var diffRight = widthBiasDiffs.biasComplementDiff;
9269 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9270 var diffTop = heightBiasDiffs.biasDiff;
9271 var diffBottom = heightBiasDiffs.biasComplementDiff;
9272 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9273 _p.autoWidth = Math.max(bb.w, min.width.val);
9274 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9275 _p.autoHeight = Math.max(bb.h, min.height.val);
9276 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9277 }
9278
9279 for (var i = 0; i < this.length; i++) {
9280 var ele = this[i];
9281 var _p = ele._private;
9282
9283 if (!_p.compoundBoundsClean || force) {
9284 update(ele);
9285
9286 if (!cy.batching()) {
9287 _p.compoundBoundsClean = true;
9288 }
9289 }
9290 }
9291
9292 return this;
9293};
9294
9295var noninf = function noninf(x) {
9296 if (x === Infinity || x === -Infinity) {
9297 return 0;
9298 }
9299
9300 return x;
9301};
9302
9303var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9304 // don't update with zero area boxes
9305 if (x2 - x1 === 0 || y2 - y1 === 0) {
9306 return;
9307 } // don't update with null dim
9308
9309
9310 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9311 return;
9312 }
9313
9314 b.x1 = x1 < b.x1 ? x1 : b.x1;
9315 b.x2 = x2 > b.x2 ? x2 : b.x2;
9316 b.y1 = y1 < b.y1 ? y1 : b.y1;
9317 b.y2 = y2 > b.y2 ? y2 : b.y2;
9318 b.w = b.x2 - b.x1;
9319 b.h = b.y2 - b.y1;
9320};
9321
9322var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9323 if (b2 == null) {
9324 return b;
9325 }
9326
9327 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9328};
9329
9330var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9331 return getPrefixedProperty(obj, field, prefix);
9332};
9333
9334var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9335 if (ele.cy().headless()) {
9336 return;
9337 }
9338
9339 var _p = ele._private;
9340 var rstyle = _p.rstyle;
9341 var halfArW = rstyle.arrowWidth / 2;
9342 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9343 var x;
9344 var y;
9345
9346 if (arrowType !== 'none') {
9347 if (prefix === 'source') {
9348 x = rstyle.srcX;
9349 y = rstyle.srcY;
9350 } else if (prefix === 'target') {
9351 x = rstyle.tgtX;
9352 y = rstyle.tgtY;
9353 } else {
9354 x = rstyle.midX;
9355 y = rstyle.midY;
9356 } // always store the individual arrow bounds
9357
9358
9359 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9360 var bb = bbs[prefix] = bbs[prefix] || {};
9361 bb.x1 = x - halfArW;
9362 bb.y1 = y - halfArW;
9363 bb.x2 = x + halfArW;
9364 bb.y2 = y + halfArW;
9365 bb.w = bb.x2 - bb.x1;
9366 bb.h = bb.y2 - bb.y1;
9367 expandBoundingBox(bb, 1);
9368 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9369 }
9370};
9371
9372var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9373 if (ele.cy().headless()) {
9374 return;
9375 }
9376
9377 var prefixDash;
9378
9379 if (prefix) {
9380 prefixDash = prefix + '-';
9381 } else {
9382 prefixDash = '';
9383 }
9384
9385 var _p = ele._private;
9386 var rstyle = _p.rstyle;
9387 var label = ele.pstyle(prefixDash + 'label').strValue;
9388
9389 if (label) {
9390 var halign = ele.pstyle('text-halign');
9391 var valign = ele.pstyle('text-valign');
9392 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9393 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9394 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9395 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9396 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9397 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9398 var isEdge = ele.isEdge();
9399 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9400 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9401 var borderWidth = ele.pstyle('text-border-width').pfValue;
9402 var halfBorderWidth = borderWidth / 2;
9403 var padding = ele.pstyle('text-background-padding').pfValue;
9404 var marginOfError = 2; // expand to work around browser dimension inaccuracies
9405
9406 var lh = labelHeight;
9407 var lw = labelWidth;
9408 var lw_2 = lw / 2;
9409 var lh_2 = lh / 2;
9410 var lx1, lx2, ly1, ly2;
9411
9412 if (isEdge) {
9413 lx1 = labelX - lw_2;
9414 lx2 = labelX + lw_2;
9415 ly1 = labelY - lh_2;
9416 ly2 = labelY + lh_2;
9417 } else {
9418 switch (halign.value) {
9419 case 'left':
9420 lx1 = labelX - lw;
9421 lx2 = labelX;
9422 break;
9423
9424 case 'center':
9425 lx1 = labelX - lw_2;
9426 lx2 = labelX + lw_2;
9427 break;
9428
9429 case 'right':
9430 lx1 = labelX;
9431 lx2 = labelX + lw;
9432 break;
9433 }
9434
9435 switch (valign.value) {
9436 case 'top':
9437 ly1 = labelY - lh;
9438 ly2 = labelY;
9439 break;
9440
9441 case 'center':
9442 ly1 = labelY - lh_2;
9443 ly2 = labelY + lh_2;
9444 break;
9445
9446 case 'bottom':
9447 ly1 = labelY;
9448 ly2 = labelY + lh;
9449 break;
9450 }
9451 } // shift by margin and expand by outline and border
9452
9453
9454 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9455 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
9456 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9457 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
9458
9459 var bbPrefix = prefix || 'main';
9460 var bbs = _p.labelBounds;
9461 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9462 bb.x1 = lx1;
9463 bb.y1 = ly1;
9464 bb.x2 = lx2;
9465 bb.y2 = ly2;
9466 bb.w = lx2 - lx1;
9467 bb.h = ly2 - ly1;
9468 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9469 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9470
9471 if (isAutorotate || isPfValue) {
9472 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9473 var cos = Math.cos(theta);
9474 var sin = Math.sin(theta); // rotation point (default value for center-center)
9475
9476 var xo = (lx1 + lx2) / 2;
9477 var yo = (ly1 + ly2) / 2;
9478
9479 if (!isEdge) {
9480 switch (halign.value) {
9481 case 'left':
9482 xo = lx2;
9483 break;
9484
9485 case 'right':
9486 xo = lx1;
9487 break;
9488 }
9489
9490 switch (valign.value) {
9491 case 'top':
9492 yo = ly2;
9493 break;
9494
9495 case 'bottom':
9496 yo = ly1;
9497 break;
9498 }
9499 }
9500
9501 var rotate = function rotate(x, y) {
9502 x = x - xo;
9503 y = y - yo;
9504 return {
9505 x: x * cos - y * sin + xo,
9506 y: x * sin + y * cos + yo
9507 };
9508 };
9509
9510 var px1y1 = rotate(lx1, ly1);
9511 var px1y2 = rotate(lx1, ly2);
9512 var px2y1 = rotate(lx2, ly1);
9513 var px2y2 = rotate(lx2, ly2);
9514 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9515 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9516 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9517 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9518 }
9519
9520 var bbPrefixRot = bbPrefix + 'Rot';
9521 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9522 bbRot.x1 = lx1;
9523 bbRot.y1 = ly1;
9524 bbRot.x2 = lx2;
9525 bbRot.y2 = ly2;
9526 bbRot.w = lx2 - lx1;
9527 bbRot.h = ly2 - ly1;
9528 updateBounds(bounds, lx1, ly1, lx2, ly2);
9529 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9530 }
9531
9532 return bounds;
9533}; // get the bounding box of the elements (in raw model position)
9534
9535
9536var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9537 var cy = ele._private.cy;
9538 var styleEnabled = cy.styleEnabled();
9539 var headless = cy.headless();
9540 var bounds = makeBoundingBox();
9541 var _p = ele._private;
9542 var isNode = ele.isNode();
9543 var isEdge = ele.isEdge();
9544 var ex1, ex2, ey1, ey2; // extrema of body / lines
9545
9546 var x, y; // node pos
9547
9548 var rstyle = _p.rstyle;
9549 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9550 // (other factors like width values will be considered later in this function anyway)
9551
9552 var isDisplayed = function isDisplayed(ele) {
9553 return ele.pstyle('display').value !== 'none';
9554 };
9555
9556 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9557 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9558
9559 if (displayed) {
9560 // displayed suffices, since we will find zero area eles anyway
9561 var overlayOpacity = 0;
9562 var overlayPadding = 0;
9563
9564 if (styleEnabled && options.includeOverlays) {
9565 overlayOpacity = ele.pstyle('overlay-opacity').value;
9566
9567 if (overlayOpacity !== 0) {
9568 overlayPadding = ele.pstyle('overlay-padding').value;
9569 }
9570 }
9571
9572 var underlayOpacity = 0;
9573 var underlayPadding = 0;
9574
9575 if (styleEnabled && options.includeUnderlays) {
9576 underlayOpacity = ele.pstyle('underlay-opacity').value;
9577
9578 if (underlayOpacity !== 0) {
9579 underlayPadding = ele.pstyle('underlay-padding').value;
9580 }
9581 }
9582
9583 var padding = Math.max(overlayPadding, underlayPadding);
9584 var w = 0;
9585 var wHalf = 0;
9586
9587 if (styleEnabled) {
9588 w = ele.pstyle('width').pfValue;
9589 wHalf = w / 2;
9590 }
9591
9592 if (isNode && options.includeNodes) {
9593 var pos = ele.position();
9594 x = pos.x;
9595 y = pos.y;
9596
9597 var _w = ele.outerWidth();
9598
9599 var halfW = _w / 2;
9600 var h = ele.outerHeight();
9601 var halfH = h / 2; // handle node dimensions
9602 /////////////////////////
9603
9604 ex1 = x - halfW;
9605 ex2 = x + halfW;
9606 ey1 = y - halfH;
9607 ey2 = y + halfH;
9608 updateBounds(bounds, ex1, ey1, ex2, ey2);
9609 } else if (isEdge && options.includeEdges) {
9610 if (styleEnabled && !headless) {
9611 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9612 //////////////////////////////////////////////
9613
9614 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9615 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9616 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9617 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9618
9619 ex1 -= wHalf;
9620 ex2 += wHalf;
9621 ey1 -= wHalf;
9622 ey2 += wHalf;
9623 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9624 ////////////////
9625
9626 if (curveStyle === 'haystack') {
9627 var hpts = rstyle.haystackPts;
9628
9629 if (hpts && hpts.length === 2) {
9630 ex1 = hpts[0].x;
9631 ey1 = hpts[0].y;
9632 ex2 = hpts[1].x;
9633 ey2 = hpts[1].y;
9634
9635 if (ex1 > ex2) {
9636 var temp = ex1;
9637 ex1 = ex2;
9638 ex2 = temp;
9639 }
9640
9641 if (ey1 > ey2) {
9642 var _temp = ey1;
9643 ey1 = ey2;
9644 ey2 = _temp;
9645 }
9646
9647 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9648 }
9649 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9650 var pts;
9651
9652 switch (curveStyle) {
9653 case 'bezier':
9654 case 'unbundled-bezier':
9655 pts = rstyle.bezierPts;
9656 break;
9657
9658 case 'segments':
9659 case 'taxi':
9660 pts = rstyle.linePts;
9661 break;
9662 }
9663
9664 if (pts != null) {
9665 for (var j = 0; j < pts.length; j++) {
9666 var pt = pts[j];
9667 ex1 = pt.x - wHalf;
9668 ex2 = pt.x + wHalf;
9669 ey1 = pt.y - wHalf;
9670 ey2 = pt.y + wHalf;
9671 updateBounds(bounds, ex1, ey1, ex2, ey2);
9672 }
9673 }
9674 } // bezier-like or segment-like edge
9675
9676 } else {
9677 // headless or style disabled
9678 // fallback on source and target positions
9679 //////////////////////////////////////////
9680 var n1 = ele.source();
9681 var n1pos = n1.position();
9682 var n2 = ele.target();
9683 var n2pos = n2.position();
9684 ex1 = n1pos.x;
9685 ex2 = n2pos.x;
9686 ey1 = n1pos.y;
9687 ey2 = n2pos.y;
9688
9689 if (ex1 > ex2) {
9690 var _temp2 = ex1;
9691 ex1 = ex2;
9692 ex2 = _temp2;
9693 }
9694
9695 if (ey1 > ey2) {
9696 var _temp3 = ey1;
9697 ey1 = ey2;
9698 ey2 = _temp3;
9699 } // take into account edge width
9700
9701
9702 ex1 -= wHalf;
9703 ex2 += wHalf;
9704 ey1 -= wHalf;
9705 ey2 += wHalf;
9706 updateBounds(bounds, ex1, ey1, ex2, ey2);
9707 } // headless or style disabled
9708
9709 } // edges
9710 // handle edge arrow size
9711 /////////////////////////
9712
9713
9714 if (styleEnabled && options.includeEdges && isEdge) {
9715 updateBoundsFromArrow(bounds, ele, 'mid-source');
9716 updateBoundsFromArrow(bounds, ele, 'mid-target');
9717 updateBoundsFromArrow(bounds, ele, 'source');
9718 updateBoundsFromArrow(bounds, ele, 'target');
9719 } // ghost
9720 ////////
9721
9722
9723 if (styleEnabled) {
9724 var ghost = ele.pstyle('ghost').value === 'yes';
9725
9726 if (ghost) {
9727 var gx = ele.pstyle('ghost-offset-x').pfValue;
9728 var gy = ele.pstyle('ghost-offset-y').pfValue;
9729 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9730 }
9731 } // always store the body bounds separately from the labels
9732
9733
9734 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9735 assignBoundingBox(bbBody, bounds);
9736 expandBoundingBoxSides(bbBody, manualExpansion);
9737 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9738 // overlay
9739 //////////
9740
9741 if (styleEnabled) {
9742 ex1 = bounds.x1;
9743 ex2 = bounds.x2;
9744 ey1 = bounds.y1;
9745 ey2 = bounds.y2;
9746 updateBounds(bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding);
9747 } // always store the body bounds separately from the labels
9748
9749
9750 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9751 assignBoundingBox(bbOverlay, bounds);
9752 expandBoundingBoxSides(bbOverlay, manualExpansion);
9753 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9754 // handle label dimensions
9755 //////////////////////////
9756
9757 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9758
9759 if (bbLabels.all != null) {
9760 clearBoundingBox(bbLabels.all);
9761 } else {
9762 bbLabels.all = makeBoundingBox();
9763 }
9764
9765 if (styleEnabled && options.includeLabels) {
9766 if (options.includeMainLabels) {
9767 updateBoundsFromLabel(bounds, ele, null);
9768 }
9769
9770 if (isEdge) {
9771 if (options.includeSourceLabels) {
9772 updateBoundsFromLabel(bounds, ele, 'source');
9773 }
9774
9775 if (options.includeTargetLabels) {
9776 updateBoundsFromLabel(bounds, ele, 'target');
9777 }
9778 }
9779 } // style enabled for labels
9780
9781 } // if displayed
9782
9783
9784 bounds.x1 = noninf(bounds.x1);
9785 bounds.y1 = noninf(bounds.y1);
9786 bounds.x2 = noninf(bounds.x2);
9787 bounds.y2 = noninf(bounds.y2);
9788 bounds.w = noninf(bounds.x2 - bounds.x1);
9789 bounds.h = noninf(bounds.y2 - bounds.y1);
9790
9791 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9792 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9793
9794 expandBoundingBox(bounds, 1);
9795 }
9796
9797 return bounds;
9798};
9799
9800var getKey = function getKey(opts) {
9801 var i = 0;
9802
9803 var tf = function tf(val) {
9804 return (val ? 1 : 0) << i++;
9805 };
9806
9807 var key = 0;
9808 key += tf(opts.incudeNodes);
9809 key += tf(opts.includeEdges);
9810 key += tf(opts.includeLabels);
9811 key += tf(opts.includeMainLabels);
9812 key += tf(opts.includeSourceLabels);
9813 key += tf(opts.includeTargetLabels);
9814 key += tf(opts.includeOverlays);
9815 return key;
9816};
9817
9818var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9819 if (ele.isEdge()) {
9820 var p1 = ele.source().position();
9821 var p2 = ele.target().position();
9822
9823 var r = function r(x) {
9824 return Math.round(x);
9825 };
9826
9827 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9828 } else {
9829 return 0;
9830 }
9831};
9832
9833var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9834 var _p = ele._private;
9835 var bb;
9836 var isEdge = ele.isEdge();
9837 var key = opts == null ? defBbOptsKey : getKey(opts);
9838 var usingDefOpts = key === defBbOptsKey;
9839 var currPosKey = getBoundingBoxPosKey(ele);
9840 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9841 var useCache = opts.useCache && isPosKeySame;
9842
9843 var isDirty = function isDirty(ele) {
9844 return ele._private.bbCache == null || ele._private.styleDirty;
9845 };
9846
9847 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9848
9849 if (needRecalc) {
9850 if (!isPosKeySame) {
9851 ele.recalculateRenderedStyle(useCache);
9852 }
9853
9854 bb = boundingBoxImpl(ele, defBbOpts);
9855 _p.bbCache = bb;
9856 _p.bbCachePosKey = currPosKey;
9857 } else {
9858 bb = _p.bbCache;
9859 } // not using def opts => need to build up bb from combination of sub bbs
9860
9861
9862 if (!usingDefOpts) {
9863 var isNode = ele.isNode();
9864 bb = makeBoundingBox();
9865
9866 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9867 if (opts.includeOverlays) {
9868 updateBoundsFromBox(bb, _p.overlayBounds);
9869 } else {
9870 updateBoundsFromBox(bb, _p.bodyBounds);
9871 }
9872 }
9873
9874 if (opts.includeLabels) {
9875 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9876 updateBoundsFromBox(bb, _p.labelBounds.all);
9877 } else {
9878 if (opts.includeMainLabels) {
9879 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9880 }
9881
9882 if (opts.includeSourceLabels) {
9883 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9884 }
9885
9886 if (opts.includeTargetLabels) {
9887 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9888 }
9889 }
9890 }
9891
9892 bb.w = bb.x2 - bb.x1;
9893 bb.h = bb.y2 - bb.y1;
9894 }
9895
9896 return bb;
9897};
9898
9899var defBbOpts = {
9900 includeNodes: true,
9901 includeEdges: true,
9902 includeLabels: true,
9903 includeMainLabels: true,
9904 includeSourceLabels: true,
9905 includeTargetLabels: true,
9906 includeOverlays: true,
9907 includeUnderlays: true,
9908 useCache: true
9909};
9910var defBbOptsKey = getKey(defBbOpts);
9911var filledBbOpts = defaults(defBbOpts);
9912
9913elesfn$k.boundingBox = function (options) {
9914 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9915 // specified s.t. the cache is used, so check for this case to make it faster by
9916 // avoiding the overhead of the rest of the function
9917
9918 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9919 if (options === undefined) {
9920 options = defBbOpts;
9921 } else {
9922 options = filledBbOpts(options);
9923 }
9924
9925 bounds = cachedBoundingBoxImpl(this[0], options);
9926 } else {
9927 bounds = makeBoundingBox();
9928 options = options || defBbOpts;
9929 var opts = filledBbOpts(options);
9930 var eles = this;
9931 var cy = eles.cy();
9932 var styleEnabled = cy.styleEnabled();
9933
9934 if (styleEnabled) {
9935 for (var i = 0; i < eles.length; i++) {
9936 var ele = eles[i];
9937 var _p = ele._private;
9938 var currPosKey = getBoundingBoxPosKey(ele);
9939 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9940 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
9941 ele.recalculateRenderedStyle(useCache);
9942 }
9943 }
9944
9945 this.updateCompoundBounds(!options.useCache);
9946
9947 for (var _i = 0; _i < eles.length; _i++) {
9948 var _ele = eles[_i];
9949 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9950 }
9951 }
9952
9953 bounds.x1 = noninf(bounds.x1);
9954 bounds.y1 = noninf(bounds.y1);
9955 bounds.x2 = noninf(bounds.x2);
9956 bounds.y2 = noninf(bounds.y2);
9957 bounds.w = noninf(bounds.x2 - bounds.x1);
9958 bounds.h = noninf(bounds.y2 - bounds.y1);
9959 return bounds;
9960};
9961
9962elesfn$k.dirtyBoundingBoxCache = function () {
9963 for (var i = 0; i < this.length; i++) {
9964 var _p = this[i]._private;
9965 _p.bbCache = null;
9966 _p.bbCachePosKey = null;
9967 _p.bodyBounds = null;
9968 _p.overlayBounds = null;
9969 _p.labelBounds.all = null;
9970 _p.labelBounds.source = null;
9971 _p.labelBounds.target = null;
9972 _p.labelBounds.main = null;
9973 _p.labelBounds.sourceRot = null;
9974 _p.labelBounds.targetRot = null;
9975 _p.labelBounds.mainRot = null;
9976 _p.arrowBounds.source = null;
9977 _p.arrowBounds.target = null;
9978 _p.arrowBounds['mid-source'] = null;
9979 _p.arrowBounds['mid-target'] = null;
9980 }
9981
9982 this.emitAndNotify('bounds');
9983 return this;
9984}; // private helper to get bounding box for custom node positions
9985// - good for perf in certain cases but currently requires dirtying the rendered style
9986// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9987// - try to use for only things like discrete layouts where the node position would change anyway
9988
9989
9990elesfn$k.boundingBoxAt = function (fn) {
9991 var nodes = this.nodes();
9992 var cy = this.cy();
9993 var hasCompoundNodes = cy.hasCompoundNodes();
9994 var parents = cy.collection();
9995
9996 if (hasCompoundNodes) {
9997 parents = nodes.filter(function (node) {
9998 return node.isParent();
9999 });
10000 nodes = nodes.not(parents);
10001 }
10002
10003 if (plainObject(fn)) {
10004 var obj = fn;
10005
10006 fn = function fn() {
10007 return obj;
10008 };
10009 }
10010
10011 var storeOldPos = function storeOldPos(node, i) {
10012 return node._private.bbAtOldPos = fn(node, i);
10013 };
10014
10015 var getOldPos = function getOldPos(node) {
10016 return node._private.bbAtOldPos;
10017 };
10018
10019 cy.startBatch();
10020 nodes.forEach(storeOldPos).silentPositions(fn);
10021
10022 if (hasCompoundNodes) {
10023 parents.dirtyCompoundBoundsCache();
10024 parents.dirtyBoundingBoxCache();
10025 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10026 }
10027
10028 var bb = copyBoundingBox(this.boundingBox({
10029 useCache: false
10030 }));
10031 nodes.silentPositions(getOldPos);
10032
10033 if (hasCompoundNodes) {
10034 parents.dirtyCompoundBoundsCache();
10035 parents.dirtyBoundingBoxCache();
10036 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10037 }
10038
10039 cy.endBatch();
10040 return bb;
10041};
10042
10043fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
10044fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
10045var bounds = elesfn$k;
10046
10047var fn$4, elesfn$l;
10048fn$4 = elesfn$l = {};
10049
10050var defineDimFns = function defineDimFns(opts) {
10051 opts.uppercaseName = capitalize(opts.name);
10052 opts.autoName = 'auto' + opts.uppercaseName;
10053 opts.labelName = 'label' + opts.uppercaseName;
10054 opts.outerName = 'outer' + opts.uppercaseName;
10055 opts.uppercaseOuterName = capitalize(opts.outerName);
10056
10057 fn$4[opts.name] = function dimImpl() {
10058 var ele = this[0];
10059 var _p = ele._private;
10060 var cy = _p.cy;
10061 var styleEnabled = cy._private.styleEnabled;
10062
10063 if (ele) {
10064 if (styleEnabled) {
10065 if (ele.isParent()) {
10066 ele.updateCompoundBounds();
10067 return _p[opts.autoName] || 0;
10068 }
10069
10070 var d = ele.pstyle(opts.name);
10071
10072 switch (d.strValue) {
10073 case 'label':
10074 ele.recalculateRenderedStyle();
10075 return _p.rstyle[opts.labelName] || 0;
10076
10077 default:
10078 return d.pfValue;
10079 }
10080 } else {
10081 return 1;
10082 }
10083 }
10084 };
10085
10086 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10087 var ele = this[0];
10088 var _p = ele._private;
10089 var cy = _p.cy;
10090 var styleEnabled = cy._private.styleEnabled;
10091
10092 if (ele) {
10093 if (styleEnabled) {
10094 var dim = ele[opts.name]();
10095 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10096
10097 var padding = 2 * ele.padding();
10098 return dim + border + padding;
10099 } else {
10100 return 1;
10101 }
10102 }
10103 };
10104
10105 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10106 var ele = this[0];
10107
10108 if (ele) {
10109 var d = ele[opts.name]();
10110 return d * this.cy().zoom();
10111 }
10112 };
10113
10114 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10115 var ele = this[0];
10116
10117 if (ele) {
10118 var od = ele[opts.outerName]();
10119 return od * this.cy().zoom();
10120 }
10121 };
10122};
10123
10124defineDimFns({
10125 name: 'width'
10126});
10127defineDimFns({
10128 name: 'height'
10129});
10130
10131elesfn$l.padding = function () {
10132 var ele = this[0];
10133 var _p = ele._private;
10134
10135 if (ele.isParent()) {
10136 ele.updateCompoundBounds();
10137
10138 if (_p.autoPadding !== undefined) {
10139 return _p.autoPadding;
10140 } else {
10141 return ele.pstyle('padding').pfValue;
10142 }
10143 } else {
10144 return ele.pstyle('padding').pfValue;
10145 }
10146};
10147
10148elesfn$l.paddedHeight = function () {
10149 var ele = this[0];
10150 return ele.height() + 2 * ele.padding();
10151};
10152
10153elesfn$l.paddedWidth = function () {
10154 var ele = this[0];
10155 return ele.width() + 2 * ele.padding();
10156};
10157
10158var widthHeight = elesfn$l;
10159
10160var ifEdge = function ifEdge(ele, getValue) {
10161 if (ele.isEdge()) {
10162 return getValue(ele);
10163 }
10164};
10165
10166var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10167 if (ele.isEdge()) {
10168 var cy = ele.cy();
10169 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10170 }
10171};
10172
10173var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10174 if (ele.isEdge()) {
10175 var cy = ele.cy();
10176 var pan = cy.pan();
10177 var zoom = cy.zoom();
10178 return getPoints(ele).map(function (p) {
10179 return modelToRenderedPosition(p, zoom, pan);
10180 });
10181 }
10182};
10183
10184var controlPoints = function controlPoints(ele) {
10185 return ele.renderer().getControlPoints(ele);
10186};
10187
10188var segmentPoints = function segmentPoints(ele) {
10189 return ele.renderer().getSegmentPoints(ele);
10190};
10191
10192var sourceEndpoint = function sourceEndpoint(ele) {
10193 return ele.renderer().getSourceEndpoint(ele);
10194};
10195
10196var targetEndpoint = function targetEndpoint(ele) {
10197 return ele.renderer().getTargetEndpoint(ele);
10198};
10199
10200var midpoint = function midpoint(ele) {
10201 return ele.renderer().getEdgeMidpoint(ele);
10202};
10203
10204var pts = {
10205 controlPoints: {
10206 get: controlPoints,
10207 mult: true
10208 },
10209 segmentPoints: {
10210 get: segmentPoints,
10211 mult: true
10212 },
10213 sourceEndpoint: {
10214 get: sourceEndpoint
10215 },
10216 targetEndpoint: {
10217 get: targetEndpoint
10218 },
10219 midpoint: {
10220 get: midpoint
10221 }
10222};
10223
10224var renderedName = function renderedName(name) {
10225 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10226};
10227
10228var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10229 var spec = pts[name];
10230 var rName = renderedName(name);
10231
10232 obj[name] = function () {
10233 return ifEdge(this, spec.get);
10234 };
10235
10236 if (spec.mult) {
10237 obj[rName] = function () {
10238 return ifEdgeRenderedPositions(this, spec.get);
10239 };
10240 } else {
10241 obj[rName] = function () {
10242 return ifEdgeRenderedPosition(this, spec.get);
10243 };
10244 }
10245
10246 return obj;
10247}, {});
10248
10249var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10250
10251/*!
10252Event object based on jQuery events, MIT license
10253
10254https://jquery.org/license/
10255https://tldrlegal.com/license/mit-license
10256https://github.com/jquery/jquery/blob/master/src/event.js
10257*/
10258var Event = function Event(src, props) {
10259 this.recycle(src, props);
10260};
10261
10262function returnFalse() {
10263 return false;
10264}
10265
10266function returnTrue() {
10267 return true;
10268} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10269
10270
10271Event.prototype = {
10272 instanceString: function instanceString() {
10273 return 'event';
10274 },
10275 recycle: function recycle(src, props) {
10276 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10277
10278 if (src != null && src.preventDefault) {
10279 // Browser Event object
10280 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10281 // by a handler lower down the tree; reflect the correct value.
10282
10283 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10284 } else if (src != null && src.type) {
10285 // Plain object containing all event details
10286 props = src;
10287 } else {
10288 // Event string
10289 this.type = src;
10290 } // Put explicitly provided properties onto the event object
10291
10292
10293 if (props != null) {
10294 // more efficient to manually copy fields we use
10295 this.originalEvent = props.originalEvent;
10296 this.type = props.type != null ? props.type : this.type;
10297 this.cy = props.cy;
10298 this.target = props.target;
10299 this.position = props.position;
10300 this.renderedPosition = props.renderedPosition;
10301 this.namespace = props.namespace;
10302 this.layout = props.layout;
10303 }
10304
10305 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10306 // create a rendered position based on the passed position
10307 var pos = this.position;
10308 var zoom = this.cy.zoom();
10309 var pan = this.cy.pan();
10310 this.renderedPosition = {
10311 x: pos.x * zoom + pan.x,
10312 y: pos.y * zoom + pan.y
10313 };
10314 } // Create a timestamp if incoming event doesn't have one
10315
10316
10317 this.timeStamp = src && src.timeStamp || Date.now();
10318 },
10319 preventDefault: function preventDefault() {
10320 this.isDefaultPrevented = returnTrue;
10321 var e = this.originalEvent;
10322
10323 if (!e) {
10324 return;
10325 } // if preventDefault exists run it on the original event
10326
10327
10328 if (e.preventDefault) {
10329 e.preventDefault();
10330 }
10331 },
10332 stopPropagation: function stopPropagation() {
10333 this.isPropagationStopped = returnTrue;
10334 var e = this.originalEvent;
10335
10336 if (!e) {
10337 return;
10338 } // if stopPropagation exists run it on the original event
10339
10340
10341 if (e.stopPropagation) {
10342 e.stopPropagation();
10343 }
10344 },
10345 stopImmediatePropagation: function stopImmediatePropagation() {
10346 this.isImmediatePropagationStopped = returnTrue;
10347 this.stopPropagation();
10348 },
10349 isDefaultPrevented: returnFalse,
10350 isPropagationStopped: returnFalse,
10351 isImmediatePropagationStopped: returnFalse
10352};
10353
10354var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10355
10356var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10357
10358var defaults$8 = {
10359 qualifierCompare: function qualifierCompare(q1, q2) {
10360 return q1 === q2;
10361 },
10362 eventMatches: function eventMatches()
10363 /*context, listener, eventObj*/
10364 {
10365 return true;
10366 },
10367 addEventFields: function addEventFields()
10368 /*context, evt*/
10369 {},
10370 callbackContext: function callbackContext(context
10371 /*, listener, eventObj*/
10372 ) {
10373 return context;
10374 },
10375 beforeEmit: function beforeEmit()
10376 /* context, listener, eventObj */
10377 {},
10378 afterEmit: function afterEmit()
10379 /* context, listener, eventObj */
10380 {},
10381 bubble: function bubble()
10382 /*context*/
10383 {
10384 return false;
10385 },
10386 parent: function parent()
10387 /*context*/
10388 {
10389 return null;
10390 },
10391 context: null
10392};
10393var defaultsKeys = Object.keys(defaults$8);
10394var emptyOpts = {};
10395
10396function Emitter() {
10397 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10398 var context = arguments.length > 1 ? arguments[1] : undefined;
10399
10400 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10401 for (var i = 0; i < defaultsKeys.length; i++) {
10402 var key = defaultsKeys[i];
10403 this[key] = opts[key] || defaults$8[key];
10404 }
10405
10406 this.context = context || this.context;
10407 this.listeners = [];
10408 this.emitting = 0;
10409}
10410
10411var p = Emitter.prototype;
10412
10413var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10414 if (fn(qualifier)) {
10415 callback = qualifier;
10416 qualifier = null;
10417 }
10418
10419 if (confOverrides) {
10420 if (conf == null) {
10421 conf = confOverrides;
10422 } else {
10423 conf = extend({}, conf, confOverrides);
10424 }
10425 }
10426
10427 var eventList = array(events) ? events : events.split(/\s+/);
10428
10429 for (var i = 0; i < eventList.length; i++) {
10430 var evt = eventList[i];
10431
10432 if (emptyString(evt)) {
10433 continue;
10434 }
10435
10436 var match = evt.match(eventRegex); // type[.namespace]
10437
10438 if (match) {
10439 var type = match[1];
10440 var namespace = match[2] ? match[2] : null;
10441 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10442
10443 if (ret === false) {
10444 break;
10445 } // allow exiting early
10446
10447 }
10448 }
10449};
10450
10451var makeEventObj = function makeEventObj(self, obj) {
10452 self.addEventFields(self.context, obj);
10453 return new Event(obj.type, obj);
10454};
10455
10456var forEachEventObj = function forEachEventObj(self, handler, events) {
10457 if (event(events)) {
10458 handler(self, events);
10459 return;
10460 } else if (plainObject(events)) {
10461 handler(self, makeEventObj(self, events));
10462 return;
10463 }
10464
10465 var eventList = array(events) ? events : events.split(/\s+/);
10466
10467 for (var i = 0; i < eventList.length; i++) {
10468 var evt = eventList[i];
10469
10470 if (emptyString(evt)) {
10471 continue;
10472 }
10473
10474 var match = evt.match(eventRegex); // type[.namespace]
10475
10476 if (match) {
10477 var type = match[1];
10478 var namespace = match[2] ? match[2] : null;
10479 var eventObj = makeEventObj(self, {
10480 type: type,
10481 namespace: namespace,
10482 target: self.context
10483 });
10484 handler(self, eventObj);
10485 }
10486 }
10487};
10488
10489p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10490 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10491 if (fn(callback)) {
10492 self.listeners.push({
10493 event: event,
10494 // full event string
10495 callback: callback,
10496 // callback to run
10497 type: type,
10498 // the event type (e.g. 'click')
10499 namespace: namespace,
10500 // the event namespace (e.g. ".foo")
10501 qualifier: qualifier,
10502 // a restriction on whether to match this emitter
10503 conf: conf // additional configuration
10504
10505 });
10506 }
10507 }, events, qualifier, callback, conf, confOverrides);
10508 return this;
10509};
10510
10511p.one = function (events, qualifier, callback, conf) {
10512 return this.on(events, qualifier, callback, conf, {
10513 one: true
10514 });
10515};
10516
10517p.removeListener = p.off = function (events, qualifier, callback, conf) {
10518 var _this = this;
10519
10520 if (this.emitting !== 0) {
10521 this.listeners = copyArray(this.listeners);
10522 }
10523
10524 var listeners = this.listeners;
10525
10526 var _loop = function _loop(i) {
10527 var listener = listeners[i];
10528 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10529 /*, conf*/
10530 ) {
10531 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10532 listeners.splice(i, 1);
10533 return false;
10534 }
10535 }, events, qualifier, callback, conf);
10536 };
10537
10538 for (var i = listeners.length - 1; i >= 0; i--) {
10539 _loop(i);
10540 }
10541
10542 return this;
10543};
10544
10545p.removeAllListeners = function () {
10546 return this.removeListener('*');
10547};
10548
10549p.emit = p.trigger = function (events, extraParams, manualCallback) {
10550 var listeners = this.listeners;
10551 var numListenersBeforeEmit = listeners.length;
10552 this.emitting++;
10553
10554 if (!array(extraParams)) {
10555 extraParams = [extraParams];
10556 }
10557
10558 forEachEventObj(this, function (self, eventObj) {
10559 if (manualCallback != null) {
10560 listeners = [{
10561 event: eventObj.event,
10562 type: eventObj.type,
10563 namespace: eventObj.namespace,
10564 callback: manualCallback
10565 }];
10566 numListenersBeforeEmit = listeners.length;
10567 }
10568
10569 var _loop2 = function _loop2(i) {
10570 var listener = listeners[i];
10571
10572 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10573 var args = [eventObj];
10574
10575 if (extraParams != null) {
10576 push(args, extraParams);
10577 }
10578
10579 self.beforeEmit(self.context, listener, eventObj);
10580
10581 if (listener.conf && listener.conf.one) {
10582 self.listeners = self.listeners.filter(function (l) {
10583 return l !== listener;
10584 });
10585 }
10586
10587 var context = self.callbackContext(self.context, listener, eventObj);
10588 var ret = listener.callback.apply(context, args);
10589 self.afterEmit(self.context, listener, eventObj);
10590
10591 if (ret === false) {
10592 eventObj.stopPropagation();
10593 eventObj.preventDefault();
10594 }
10595 } // if listener matches
10596
10597 };
10598
10599 for (var i = 0; i < numListenersBeforeEmit; i++) {
10600 _loop2(i);
10601 } // for listener
10602
10603
10604 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10605 self.parent(self.context).emit(eventObj, extraParams);
10606 }
10607 }, events);
10608 this.emitting--;
10609 return this;
10610};
10611
10612var emitterOptions = {
10613 qualifierCompare: function qualifierCompare(selector1, selector2) {
10614 if (selector1 == null || selector2 == null) {
10615 return selector1 == null && selector2 == null;
10616 } else {
10617 return selector1.sameText(selector2);
10618 }
10619 },
10620 eventMatches: function eventMatches(ele, listener, eventObj) {
10621 var selector = listener.qualifier;
10622
10623 if (selector != null) {
10624 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10625 }
10626
10627 return true;
10628 },
10629 addEventFields: function addEventFields(ele, evt) {
10630 evt.cy = ele.cy();
10631 evt.target = ele;
10632 },
10633 callbackContext: function callbackContext(ele, listener, eventObj) {
10634 return listener.qualifier != null ? eventObj.target : ele;
10635 },
10636 beforeEmit: function beforeEmit(context, listener
10637 /*, eventObj*/
10638 ) {
10639 if (listener.conf && listener.conf.once) {
10640 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10641 }
10642 },
10643 bubble: function bubble() {
10644 return true;
10645 },
10646 parent: function parent(ele) {
10647 return ele.isChild() ? ele.parent() : ele.cy();
10648 }
10649};
10650
10651var argSelector = function argSelector(arg) {
10652 if (string(arg)) {
10653 return new Selector(arg);
10654 } else {
10655 return arg;
10656 }
10657};
10658
10659var elesfn$m = {
10660 createEmitter: function createEmitter() {
10661 for (var i = 0; i < this.length; i++) {
10662 var ele = this[i];
10663 var _p = ele._private;
10664
10665 if (!_p.emitter) {
10666 _p.emitter = new Emitter(emitterOptions, ele);
10667 }
10668 }
10669
10670 return this;
10671 },
10672 emitter: function emitter() {
10673 return this._private.emitter;
10674 },
10675 on: function on(events, selector, callback) {
10676 var argSel = argSelector(selector);
10677
10678 for (var i = 0; i < this.length; i++) {
10679 var ele = this[i];
10680 ele.emitter().on(events, argSel, callback);
10681 }
10682
10683 return this;
10684 },
10685 removeListener: function removeListener(events, selector, callback) {
10686 var argSel = argSelector(selector);
10687
10688 for (var i = 0; i < this.length; i++) {
10689 var ele = this[i];
10690 ele.emitter().removeListener(events, argSel, callback);
10691 }
10692
10693 return this;
10694 },
10695 removeAllListeners: function removeAllListeners() {
10696 for (var i = 0; i < this.length; i++) {
10697 var ele = this[i];
10698 ele.emitter().removeAllListeners();
10699 }
10700
10701 return this;
10702 },
10703 one: function one(events, selector, callback) {
10704 var argSel = argSelector(selector);
10705
10706 for (var i = 0; i < this.length; i++) {
10707 var ele = this[i];
10708 ele.emitter().one(events, argSel, callback);
10709 }
10710
10711 return this;
10712 },
10713 once: function once(events, selector, callback) {
10714 var argSel = argSelector(selector);
10715
10716 for (var i = 0; i < this.length; i++) {
10717 var ele = this[i];
10718 ele.emitter().on(events, argSel, callback, {
10719 once: true,
10720 onceCollection: this
10721 });
10722 }
10723 },
10724 emit: function emit(events, extraParams) {
10725 for (var i = 0; i < this.length; i++) {
10726 var ele = this[i];
10727 ele.emitter().emit(events, extraParams);
10728 }
10729
10730 return this;
10731 },
10732 emitAndNotify: function emitAndNotify(event, extraParams) {
10733 // for internal use only
10734 if (this.length === 0) {
10735 return;
10736 } // empty collections don't need to notify anything
10737 // notify renderer
10738
10739
10740 this.cy().notify(event, this);
10741 this.emit(event, extraParams);
10742 return this;
10743 }
10744};
10745define$3.eventAliasesOn(elesfn$m);
10746
10747var elesfn$n = {
10748 nodes: function nodes(selector) {
10749 return this.filter(function (ele) {
10750 return ele.isNode();
10751 }).filter(selector);
10752 },
10753 edges: function edges(selector) {
10754 return this.filter(function (ele) {
10755 return ele.isEdge();
10756 }).filter(selector);
10757 },
10758 // internal helper to get nodes and edges as separate collections with single iteration over elements
10759 byGroup: function byGroup() {
10760 var nodes = this.spawn();
10761 var edges = this.spawn();
10762
10763 for (var i = 0; i < this.length; i++) {
10764 var ele = this[i];
10765
10766 if (ele.isNode()) {
10767 nodes.push(ele);
10768 } else {
10769 edges.push(ele);
10770 }
10771 }
10772
10773 return {
10774 nodes: nodes,
10775 edges: edges
10776 };
10777 },
10778 filter: function filter(_filter, thisArg) {
10779 if (_filter === undefined) {
10780 // check this first b/c it's the most common/performant case
10781 return this;
10782 } else if (string(_filter) || elementOrCollection(_filter)) {
10783 return new Selector(_filter).filter(this);
10784 } else if (fn(_filter)) {
10785 var filterEles = this.spawn();
10786 var eles = this;
10787
10788 for (var i = 0; i < eles.length; i++) {
10789 var ele = eles[i];
10790 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10791
10792 if (include) {
10793 filterEles.push(ele);
10794 }
10795 }
10796
10797 return filterEles;
10798 }
10799
10800 return this.spawn(); // if not handled by above, give 'em an empty collection
10801 },
10802 not: function not(toRemove) {
10803 if (!toRemove) {
10804 return this;
10805 } else {
10806 if (string(toRemove)) {
10807 toRemove = this.filter(toRemove);
10808 }
10809
10810 var elements = this.spawn();
10811
10812 for (var i = 0; i < this.length; i++) {
10813 var element = this[i];
10814 var remove = toRemove.has(element);
10815
10816 if (!remove) {
10817 elements.push(element);
10818 }
10819 }
10820
10821 return elements;
10822 }
10823 },
10824 absoluteComplement: function absoluteComplement() {
10825 var cy = this.cy();
10826 return cy.mutableElements().not(this);
10827 },
10828 intersect: function intersect(other) {
10829 // if a selector is specified, then filter by it instead
10830 if (string(other)) {
10831 var selector = other;
10832 return this.filter(selector);
10833 }
10834
10835 var elements = this.spawn();
10836 var col1 = this;
10837 var col2 = other;
10838 var col1Smaller = this.length < other.length;
10839 var colS = col1Smaller ? col1 : col2;
10840 var colL = col1Smaller ? col2 : col1;
10841
10842 for (var i = 0; i < colS.length; i++) {
10843 var ele = colS[i];
10844
10845 if (colL.has(ele)) {
10846 elements.push(ele);
10847 }
10848 }
10849
10850 return elements;
10851 },
10852 xor: function xor(other) {
10853 var cy = this._private.cy;
10854
10855 if (string(other)) {
10856 other = cy.$(other);
10857 }
10858
10859 var elements = this.spawn();
10860 var col1 = this;
10861 var col2 = other;
10862
10863 var add = function add(col, other) {
10864 for (var i = 0; i < col.length; i++) {
10865 var ele = col[i];
10866 var id = ele._private.data.id;
10867 var inOther = other.hasElementWithId(id);
10868
10869 if (!inOther) {
10870 elements.push(ele);
10871 }
10872 }
10873 };
10874
10875 add(col1, col2);
10876 add(col2, col1);
10877 return elements;
10878 },
10879 diff: function diff(other) {
10880 var cy = this._private.cy;
10881
10882 if (string(other)) {
10883 other = cy.$(other);
10884 }
10885
10886 var left = this.spawn();
10887 var right = this.spawn();
10888 var both = this.spawn();
10889 var col1 = this;
10890 var col2 = other;
10891
10892 var add = function add(col, other, retEles) {
10893 for (var i = 0; i < col.length; i++) {
10894 var ele = col[i];
10895 var id = ele._private.data.id;
10896 var inOther = other.hasElementWithId(id);
10897
10898 if (inOther) {
10899 both.merge(ele);
10900 } else {
10901 retEles.push(ele);
10902 }
10903 }
10904 };
10905
10906 add(col1, col2, left);
10907 add(col2, col1, right);
10908 return {
10909 left: left,
10910 right: right,
10911 both: both
10912 };
10913 },
10914 add: function add(toAdd) {
10915 var cy = this._private.cy;
10916
10917 if (!toAdd) {
10918 return this;
10919 }
10920
10921 if (string(toAdd)) {
10922 var selector = toAdd;
10923 toAdd = cy.mutableElements().filter(selector);
10924 }
10925
10926 var elements = this.spawnSelf();
10927
10928 for (var i = 0; i < toAdd.length; i++) {
10929 var ele = toAdd[i];
10930 var add = !this.has(ele);
10931
10932 if (add) {
10933 elements.push(ele);
10934 }
10935 }
10936
10937 return elements;
10938 },
10939 // in place merge on calling collection
10940 merge: function merge(toAdd) {
10941 var _p = this._private;
10942 var cy = _p.cy;
10943
10944 if (!toAdd) {
10945 return this;
10946 }
10947
10948 if (toAdd && string(toAdd)) {
10949 var selector = toAdd;
10950 toAdd = cy.mutableElements().filter(selector);
10951 }
10952
10953 var map = _p.map;
10954
10955 for (var i = 0; i < toAdd.length; i++) {
10956 var toAddEle = toAdd[i];
10957 var id = toAddEle._private.data.id;
10958 var add = !map.has(id);
10959
10960 if (add) {
10961 var index = this.length++;
10962 this[index] = toAddEle;
10963 map.set(id, {
10964 ele: toAddEle,
10965 index: index
10966 });
10967 }
10968 }
10969
10970 return this; // chaining
10971 },
10972 unmergeAt: function unmergeAt(i) {
10973 var ele = this[i];
10974 var id = ele.id();
10975 var _p = this._private;
10976 var map = _p.map; // remove ele
10977
10978 this[i] = undefined;
10979 map["delete"](id);
10980 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10981
10982 if (this.length > 1 && !unmergedLastEle) {
10983 var lastEleI = this.length - 1;
10984 var lastEle = this[lastEleI];
10985 var lastEleId = lastEle._private.data.id;
10986 this[lastEleI] = undefined;
10987 this[i] = lastEle;
10988 map.set(lastEleId, {
10989 ele: lastEle,
10990 index: i
10991 });
10992 } // the collection is now 1 ele smaller
10993
10994
10995 this.length--;
10996 return this;
10997 },
10998 // remove single ele in place in calling collection
10999 unmergeOne: function unmergeOne(ele) {
11000 ele = ele[0];
11001 var _p = this._private;
11002 var id = ele._private.data.id;
11003 var map = _p.map;
11004 var entry = map.get(id);
11005
11006 if (!entry) {
11007 return this; // no need to remove
11008 }
11009
11010 var i = entry.index;
11011 this.unmergeAt(i);
11012 return this;
11013 },
11014 // remove eles in place on calling collection
11015 unmerge: function unmerge(toRemove) {
11016 var cy = this._private.cy;
11017
11018 if (!toRemove) {
11019 return this;
11020 }
11021
11022 if (toRemove && string(toRemove)) {
11023 var selector = toRemove;
11024 toRemove = cy.mutableElements().filter(selector);
11025 }
11026
11027 for (var i = 0; i < toRemove.length; i++) {
11028 this.unmergeOne(toRemove[i]);
11029 }
11030
11031 return this; // chaining
11032 },
11033 unmergeBy: function unmergeBy(toRmFn) {
11034 for (var i = this.length - 1; i >= 0; i--) {
11035 var ele = this[i];
11036
11037 if (toRmFn(ele)) {
11038 this.unmergeAt(i);
11039 }
11040 }
11041
11042 return this;
11043 },
11044 map: function map(mapFn, thisArg) {
11045 var arr = [];
11046 var eles = this;
11047
11048 for (var i = 0; i < eles.length; i++) {
11049 var ele = eles[i];
11050 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11051 arr.push(ret);
11052 }
11053
11054 return arr;
11055 },
11056 reduce: function reduce(fn, initialValue) {
11057 var val = initialValue;
11058 var eles = this;
11059
11060 for (var i = 0; i < eles.length; i++) {
11061 val = fn(val, eles[i], i, eles);
11062 }
11063
11064 return val;
11065 },
11066 max: function max(valFn, thisArg) {
11067 var max = -Infinity;
11068 var maxEle;
11069 var eles = this;
11070
11071 for (var i = 0; i < eles.length; i++) {
11072 var ele = eles[i];
11073 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11074
11075 if (val > max) {
11076 max = val;
11077 maxEle = ele;
11078 }
11079 }
11080
11081 return {
11082 value: max,
11083 ele: maxEle
11084 };
11085 },
11086 min: function min(valFn, thisArg) {
11087 var min = Infinity;
11088 var minEle;
11089 var eles = this;
11090
11091 for (var i = 0; i < eles.length; i++) {
11092 var ele = eles[i];
11093 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11094
11095 if (val < min) {
11096 min = val;
11097 minEle = ele;
11098 }
11099 }
11100
11101 return {
11102 value: min,
11103 ele: minEle
11104 };
11105 }
11106}; // aliases
11107
11108var fn$5 = elesfn$n;
11109fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11110fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11111fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11112fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11113fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11114fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11115
11116var elesfn$o = {
11117 isNode: function isNode() {
11118 return this.group() === 'nodes';
11119 },
11120 isEdge: function isEdge() {
11121 return this.group() === 'edges';
11122 },
11123 isLoop: function isLoop() {
11124 return this.isEdge() && this.source()[0] === this.target()[0];
11125 },
11126 isSimple: function isSimple() {
11127 return this.isEdge() && this.source()[0] !== this.target()[0];
11128 },
11129 group: function group() {
11130 var ele = this[0];
11131
11132 if (ele) {
11133 return ele._private.group;
11134 }
11135 }
11136};
11137
11138/**
11139 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11140 * and z-index (low to high). These styles affect how this applies:
11141 *
11142 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11143 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11144 * root to leaves of the compound graph. The last drawn is `top`.
11145 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11146 * `manual` ignores this convention and draws based on the `z-index` value setting.
11147 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11148 * `z-index` will be drawn on top of an element with a lower `z-index`.
11149 */
11150
11151var zIndexSort = function zIndexSort(a, b) {
11152 var cy = a.cy();
11153 var hasCompoundNodes = cy.hasCompoundNodes();
11154
11155 function getDepth(ele) {
11156 var style = ele.pstyle('z-compound-depth');
11157
11158 if (style.value === 'auto') {
11159 return hasCompoundNodes ? ele.zDepth() : 0;
11160 } else if (style.value === 'bottom') {
11161 return -1;
11162 } else if (style.value === 'top') {
11163 return MAX_INT;
11164 } // 'orphan'
11165
11166
11167 return 0;
11168 }
11169
11170 var depthDiff = getDepth(a) - getDepth(b);
11171
11172 if (depthDiff !== 0) {
11173 return depthDiff;
11174 }
11175
11176 function getEleDepth(ele) {
11177 var style = ele.pstyle('z-index-compare');
11178
11179 if (style.value === 'auto') {
11180 return ele.isNode() ? 1 : 0;
11181 } // 'manual'
11182
11183
11184 return 0;
11185 }
11186
11187 var eleDiff = getEleDepth(a) - getEleDepth(b);
11188
11189 if (eleDiff !== 0) {
11190 return eleDiff;
11191 }
11192
11193 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11194
11195 if (zDiff !== 0) {
11196 return zDiff;
11197 } // compare indices in the core (order added to graph w/ last on top)
11198
11199
11200 return a.poolIndex() - b.poolIndex();
11201};
11202
11203var elesfn$p = {
11204 forEach: function forEach(fn$1, thisArg) {
11205 if (fn(fn$1)) {
11206 var N = this.length;
11207
11208 for (var i = 0; i < N; i++) {
11209 var ele = this[i];
11210 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11211
11212 if (ret === false) {
11213 break;
11214 } // exit each early on return false
11215
11216 }
11217 }
11218
11219 return this;
11220 },
11221 toArray: function toArray() {
11222 var array = [];
11223
11224 for (var i = 0; i < this.length; i++) {
11225 array.push(this[i]);
11226 }
11227
11228 return array;
11229 },
11230 slice: function slice(start, end) {
11231 var array = [];
11232 var thisSize = this.length;
11233
11234 if (end == null) {
11235 end = thisSize;
11236 }
11237
11238 if (start == null) {
11239 start = 0;
11240 }
11241
11242 if (start < 0) {
11243 start = thisSize + start;
11244 }
11245
11246 if (end < 0) {
11247 end = thisSize + end;
11248 }
11249
11250 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11251 array.push(this[i]);
11252 }
11253
11254 return this.spawn(array);
11255 },
11256 size: function size() {
11257 return this.length;
11258 },
11259 eq: function eq(i) {
11260 return this[i] || this.spawn();
11261 },
11262 first: function first() {
11263 return this[0] || this.spawn();
11264 },
11265 last: function last() {
11266 return this[this.length - 1] || this.spawn();
11267 },
11268 empty: function empty() {
11269 return this.length === 0;
11270 },
11271 nonempty: function nonempty() {
11272 return !this.empty();
11273 },
11274 sort: function sort(sortFn) {
11275 if (!fn(sortFn)) {
11276 return this;
11277 }
11278
11279 var sorted = this.toArray().sort(sortFn);
11280 return this.spawn(sorted);
11281 },
11282 sortByZIndex: function sortByZIndex() {
11283 return this.sort(zIndexSort);
11284 },
11285 zDepth: function zDepth() {
11286 var ele = this[0];
11287
11288 if (!ele) {
11289 return undefined;
11290 } // let cy = ele.cy();
11291
11292
11293 var _p = ele._private;
11294 var group = _p.group;
11295
11296 if (group === 'nodes') {
11297 var depth = _p.data.parent ? ele.parents().size() : 0;
11298
11299 if (!ele.isParent()) {
11300 return MAX_INT - 1; // childless nodes always on top
11301 }
11302
11303 return depth;
11304 } else {
11305 var src = _p.source;
11306 var tgt = _p.target;
11307 var srcDepth = src.zDepth();
11308 var tgtDepth = tgt.zDepth();
11309 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11310 }
11311 }
11312};
11313elesfn$p.each = elesfn$p.forEach;
11314
11315var defineSymbolIterator = function defineSymbolIterator() {
11316 var typeofUndef = "undefined" ;
11317 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11318
11319 if (isIteratorSupported) {
11320 elesfn$p[Symbol.iterator] = function () {
11321 var _this = this;
11322
11323 // eslint-disable-line no-undef
11324 var entry = {
11325 value: undefined,
11326 done: false
11327 };
11328 var i = 0;
11329 var length = this.length;
11330 return _defineProperty({
11331 next: function next() {
11332 if (i < length) {
11333 entry.value = _this[i++];
11334 } else {
11335 entry.value = undefined;
11336 entry.done = true;
11337 }
11338
11339 return entry;
11340 }
11341 }, Symbol.iterator, function () {
11342 // eslint-disable-line no-undef
11343 return this;
11344 });
11345 };
11346 }
11347};
11348
11349defineSymbolIterator();
11350
11351var getLayoutDimensionOptions = defaults({
11352 nodeDimensionsIncludeLabels: false
11353});
11354var elesfn$q = {
11355 // Calculates and returns node dimensions { x, y } based on options given
11356 layoutDimensions: function layoutDimensions(options) {
11357 options = getLayoutDimensionOptions(options);
11358 var dims;
11359
11360 if (!this.takesUpSpace()) {
11361 dims = {
11362 w: 0,
11363 h: 0
11364 };
11365 } else if (options.nodeDimensionsIncludeLabels) {
11366 var bbDim = this.boundingBox();
11367 dims = {
11368 w: bbDim.w,
11369 h: bbDim.h
11370 };
11371 } else {
11372 dims = {
11373 w: this.outerWidth(),
11374 h: this.outerHeight()
11375 };
11376 } // sanitise the dimensions for external layouts (avoid division by zero)
11377
11378
11379 if (dims.w === 0 || dims.h === 0) {
11380 dims.w = dims.h = 1;
11381 }
11382
11383 return dims;
11384 },
11385 // using standard layout options, apply position function (w/ or w/o animation)
11386 layoutPositions: function layoutPositions(layout, options, fn) {
11387 var nodes = this.nodes().filter(function (n) {
11388 return !n.isParent();
11389 });
11390 var cy = this.cy();
11391 var layoutEles = options.eles; // nodes & edges
11392
11393 var getMemoizeKey = function getMemoizeKey(node) {
11394 return node.id();
11395 };
11396
11397 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11398
11399 layout.emit({
11400 type: 'layoutstart',
11401 layout: layout
11402 });
11403 layout.animations = [];
11404
11405 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11406 var center = {
11407 x: nodesBb.x1 + nodesBb.w / 2,
11408 y: nodesBb.y1 + nodesBb.h / 2
11409 };
11410 var spacingVector = {
11411 // scale from center of bounding box (not necessarily 0,0)
11412 x: (pos.x - center.x) * spacing,
11413 y: (pos.y - center.y) * spacing
11414 };
11415 return {
11416 x: center.x + spacingVector.x,
11417 y: center.y + spacingVector.y
11418 };
11419 };
11420
11421 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11422
11423 var spacingBb = function spacingBb() {
11424 if (!useSpacingFactor) {
11425 return null;
11426 }
11427
11428 var bb = makeBoundingBox();
11429
11430 for (var i = 0; i < nodes.length; i++) {
11431 var node = nodes[i];
11432 var pos = fnMem(node, i);
11433 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11434 }
11435
11436 return bb;
11437 };
11438
11439 var bb = spacingBb();
11440 var getFinalPos = memoize(function (node, i) {
11441 var newPos = fnMem(node, i);
11442
11443 if (useSpacingFactor) {
11444 var spacing = Math.abs(options.spacingFactor);
11445 newPos = calculateSpacing(spacing, bb, newPos);
11446 }
11447
11448 if (options.transform != null) {
11449 newPos = options.transform(node, newPos);
11450 }
11451
11452 return newPos;
11453 }, getMemoizeKey);
11454
11455 if (options.animate) {
11456 for (var i = 0; i < nodes.length; i++) {
11457 var node = nodes[i];
11458 var newPos = getFinalPos(node, i);
11459 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11460
11461 if (animateNode) {
11462 var ani = node.animation({
11463 position: newPos,
11464 duration: options.animationDuration,
11465 easing: options.animationEasing
11466 });
11467 layout.animations.push(ani);
11468 } else {
11469 node.position(newPos);
11470 }
11471 }
11472
11473 if (options.fit) {
11474 var fitAni = cy.animation({
11475 fit: {
11476 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11477 padding: options.padding
11478 },
11479 duration: options.animationDuration,
11480 easing: options.animationEasing
11481 });
11482 layout.animations.push(fitAni);
11483 } else if (options.zoom !== undefined && options.pan !== undefined) {
11484 var zoomPanAni = cy.animation({
11485 zoom: options.zoom,
11486 pan: options.pan,
11487 duration: options.animationDuration,
11488 easing: options.animationEasing
11489 });
11490 layout.animations.push(zoomPanAni);
11491 }
11492
11493 layout.animations.forEach(function (ani) {
11494 return ani.play();
11495 });
11496 layout.one('layoutready', options.ready);
11497 layout.emit({
11498 type: 'layoutready',
11499 layout: layout
11500 });
11501 Promise$1.all(layout.animations.map(function (ani) {
11502 return ani.promise();
11503 })).then(function () {
11504 layout.one('layoutstop', options.stop);
11505 layout.emit({
11506 type: 'layoutstop',
11507 layout: layout
11508 });
11509 });
11510 } else {
11511 nodes.positions(getFinalPos);
11512
11513 if (options.fit) {
11514 cy.fit(options.eles, options.padding);
11515 }
11516
11517 if (options.zoom != null) {
11518 cy.zoom(options.zoom);
11519 }
11520
11521 if (options.pan) {
11522 cy.pan(options.pan);
11523 }
11524
11525 layout.one('layoutready', options.ready);
11526 layout.emit({
11527 type: 'layoutready',
11528 layout: layout
11529 });
11530 layout.one('layoutstop', options.stop);
11531 layout.emit({
11532 type: 'layoutstop',
11533 layout: layout
11534 });
11535 }
11536
11537 return this; // chaining
11538 },
11539 layout: function layout(options) {
11540 var cy = this.cy();
11541 return cy.makeLayout(extend({}, options, {
11542 eles: this
11543 }));
11544 }
11545}; // aliases:
11546
11547elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11548
11549function styleCache(key, fn, ele) {
11550 var _p = ele._private;
11551 var cache = _p.styleCache = _p.styleCache || [];
11552 var val;
11553
11554 if ((val = cache[key]) != null) {
11555 return val;
11556 } else {
11557 val = cache[key] = fn(ele);
11558 return val;
11559 }
11560}
11561
11562function cacheStyleFunction(key, fn) {
11563 key = hashString(key);
11564 return function cachedStyleFunction(ele) {
11565 return styleCache(key, fn, ele);
11566 };
11567}
11568
11569function cachePrototypeStyleFunction(key, fn) {
11570 key = hashString(key);
11571
11572 var selfFn = function selfFn(ele) {
11573 return fn.call(ele);
11574 };
11575
11576 return function cachedPrototypeStyleFunction() {
11577 var ele = this[0];
11578
11579 if (ele) {
11580 return styleCache(key, selfFn, ele);
11581 }
11582 };
11583}
11584
11585var elesfn$r = {
11586 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11587 var cy = this.cy();
11588 var renderer = cy.renderer();
11589 var styleEnabled = cy.styleEnabled();
11590
11591 if (renderer && styleEnabled) {
11592 renderer.recalculateRenderedStyle(this, useCache);
11593 }
11594
11595 return this;
11596 },
11597 dirtyStyleCache: function dirtyStyleCache() {
11598 var cy = this.cy();
11599
11600 var dirty = function dirty(ele) {
11601 return ele._private.styleCache = null;
11602 };
11603
11604 if (cy.hasCompoundNodes()) {
11605 var eles;
11606 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11607 eles.merge(eles.connectedEdges());
11608 eles.forEach(dirty);
11609 } else {
11610 this.forEach(function (ele) {
11611 dirty(ele);
11612 ele.connectedEdges().forEach(dirty);
11613 });
11614 }
11615
11616 return this;
11617 },
11618 // fully updates (recalculates) the style for the elements
11619 updateStyle: function updateStyle(notifyRenderer) {
11620 var cy = this._private.cy;
11621
11622 if (!cy.styleEnabled()) {
11623 return this;
11624 }
11625
11626 if (cy.batching()) {
11627 var bEles = cy._private.batchStyleEles;
11628 bEles.merge(this);
11629 return this; // chaining and exit early when batching
11630 }
11631
11632 var hasCompounds = cy.hasCompoundNodes();
11633 var updatedEles = this;
11634 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11635
11636 if (hasCompounds) {
11637 // then add everything up and down for compound selector checks
11638 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11639 } // let changedEles = style.apply( updatedEles );
11640
11641
11642 var changedEles = updatedEles;
11643
11644 if (notifyRenderer) {
11645 changedEles.emitAndNotify('style'); // let renderer know we changed style
11646 } else {
11647 changedEles.emit('style'); // just fire the event
11648 }
11649
11650 updatedEles.forEach(function (ele) {
11651 return ele._private.styleDirty = true;
11652 });
11653 return this; // chaining
11654 },
11655 // private: clears dirty flag and recalculates style
11656 cleanStyle: function cleanStyle() {
11657 var cy = this.cy();
11658
11659 if (!cy.styleEnabled()) {
11660 return;
11661 }
11662
11663 for (var i = 0; i < this.length; i++) {
11664 var ele = this[i];
11665
11666 if (ele._private.styleDirty) {
11667 // n.b. this flag should be set before apply() to avoid potential infinite recursion
11668 ele._private.styleDirty = false;
11669 cy.style().apply(ele);
11670 }
11671 }
11672 },
11673 // get the internal parsed style object for the specified property
11674 parsedStyle: function parsedStyle(property) {
11675 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11676 var ele = this[0];
11677 var cy = ele.cy();
11678
11679 if (!cy.styleEnabled()) {
11680 return;
11681 }
11682
11683 if (ele) {
11684 this.cleanStyle();
11685 var overriddenStyle = ele._private.style[property];
11686
11687 if (overriddenStyle != null) {
11688 return overriddenStyle;
11689 } else if (includeNonDefault) {
11690 return cy.style().getDefaultProperty(property);
11691 } else {
11692 return null;
11693 }
11694 }
11695 },
11696 numericStyle: function numericStyle(property) {
11697 var ele = this[0];
11698
11699 if (!ele.cy().styleEnabled()) {
11700 return;
11701 }
11702
11703 if (ele) {
11704 var pstyle = ele.pstyle(property);
11705 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11706 }
11707 },
11708 numericStyleUnits: function numericStyleUnits(property) {
11709 var ele = this[0];
11710
11711 if (!ele.cy().styleEnabled()) {
11712 return;
11713 }
11714
11715 if (ele) {
11716 return ele.pstyle(property).units;
11717 }
11718 },
11719 // get the specified css property as a rendered value (i.e. on-screen value)
11720 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11721 renderedStyle: function renderedStyle(property) {
11722 var cy = this.cy();
11723
11724 if (!cy.styleEnabled()) {
11725 return this;
11726 }
11727
11728 var ele = this[0];
11729
11730 if (ele) {
11731 return cy.style().getRenderedStyle(ele, property);
11732 }
11733 },
11734 // read the calculated css style of the element or override the style (via a bypass)
11735 style: function style(name, value) {
11736 var cy = this.cy();
11737
11738 if (!cy.styleEnabled()) {
11739 return this;
11740 }
11741
11742 var updateTransitions = false;
11743 var style = cy.style();
11744
11745 if (plainObject(name)) {
11746 // then extend the bypass
11747 var props = name;
11748 style.applyBypass(this, props, updateTransitions);
11749 this.emitAndNotify('style'); // let the renderer know we've updated style
11750 } else if (string(name)) {
11751 if (value === undefined) {
11752 // then get the property from the style
11753 var ele = this[0];
11754
11755 if (ele) {
11756 return style.getStylePropertyValue(ele, name);
11757 } else {
11758 // empty collection => can't get any value
11759 return;
11760 }
11761 } else {
11762 // then set the bypass with the property value
11763 style.applyBypass(this, name, value, updateTransitions);
11764 this.emitAndNotify('style'); // let the renderer know we've updated style
11765 }
11766 } else if (name === undefined) {
11767 var _ele = this[0];
11768
11769 if (_ele) {
11770 return style.getRawStyle(_ele);
11771 } else {
11772 // empty collection => can't get any value
11773 return;
11774 }
11775 }
11776
11777 return this; // chaining
11778 },
11779 removeStyle: function removeStyle(names) {
11780 var cy = this.cy();
11781
11782 if (!cy.styleEnabled()) {
11783 return this;
11784 }
11785
11786 var updateTransitions = false;
11787 var style = cy.style();
11788 var eles = this;
11789
11790 if (names === undefined) {
11791 for (var i = 0; i < eles.length; i++) {
11792 var ele = eles[i];
11793 style.removeAllBypasses(ele, updateTransitions);
11794 }
11795 } else {
11796 names = names.split(/\s+/);
11797
11798 for (var _i = 0; _i < eles.length; _i++) {
11799 var _ele2 = eles[_i];
11800 style.removeBypasses(_ele2, names, updateTransitions);
11801 }
11802 }
11803
11804 this.emitAndNotify('style'); // let the renderer know we've updated style
11805
11806 return this; // chaining
11807 },
11808 show: function show() {
11809 this.css('display', 'element');
11810 return this; // chaining
11811 },
11812 hide: function hide() {
11813 this.css('display', 'none');
11814 return this; // chaining
11815 },
11816 effectiveOpacity: function effectiveOpacity() {
11817 var cy = this.cy();
11818
11819 if (!cy.styleEnabled()) {
11820 return 1;
11821 }
11822
11823 var hasCompoundNodes = cy.hasCompoundNodes();
11824 var ele = this[0];
11825
11826 if (ele) {
11827 var _p = ele._private;
11828 var parentOpacity = ele.pstyle('opacity').value;
11829
11830 if (!hasCompoundNodes) {
11831 return parentOpacity;
11832 }
11833
11834 var parents = !_p.data.parent ? null : ele.parents();
11835
11836 if (parents) {
11837 for (var i = 0; i < parents.length; i++) {
11838 var parent = parents[i];
11839 var opacity = parent.pstyle('opacity').value;
11840 parentOpacity = opacity * parentOpacity;
11841 }
11842 }
11843
11844 return parentOpacity;
11845 }
11846 },
11847 transparent: function transparent() {
11848 var cy = this.cy();
11849
11850 if (!cy.styleEnabled()) {
11851 return false;
11852 }
11853
11854 var ele = this[0];
11855 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11856
11857 if (ele) {
11858 if (!hasCompoundNodes) {
11859 return ele.pstyle('opacity').value === 0;
11860 } else {
11861 return ele.effectiveOpacity() === 0;
11862 }
11863 }
11864 },
11865 backgrounding: function backgrounding() {
11866 var cy = this.cy();
11867
11868 if (!cy.styleEnabled()) {
11869 return false;
11870 }
11871
11872 var ele = this[0];
11873 return ele._private.backgrounding ? true : false;
11874 }
11875};
11876
11877function checkCompound(ele, parentOk) {
11878 var _p = ele._private;
11879 var parents = _p.data.parent ? ele.parents() : null;
11880
11881 if (parents) {
11882 for (var i = 0; i < parents.length; i++) {
11883 var parent = parents[i];
11884
11885 if (!parentOk(parent)) {
11886 return false;
11887 }
11888 }
11889 }
11890
11891 return true;
11892}
11893
11894function defineDerivedStateFunction(specs) {
11895 var ok = specs.ok;
11896 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11897 var parentOk = specs.parentOk || specs.ok;
11898 return function () {
11899 var cy = this.cy();
11900
11901 if (!cy.styleEnabled()) {
11902 return true;
11903 }
11904
11905 var ele = this[0];
11906 var hasCompoundNodes = cy.hasCompoundNodes();
11907
11908 if (ele) {
11909 var _p = ele._private;
11910
11911 if (!ok(ele)) {
11912 return false;
11913 }
11914
11915 if (ele.isNode()) {
11916 return !hasCompoundNodes || checkCompound(ele, parentOk);
11917 } else {
11918 var src = _p.source;
11919 var tgt = _p.target;
11920 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11921 }
11922 }
11923 };
11924}
11925
11926var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11927 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11928});
11929elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11930 ok: eleTakesUpSpace
11931}));
11932var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11933 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11934});
11935var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11936 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11937});
11938elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11939 ok: eleInteractive,
11940 parentOk: parentInteractive,
11941 edgeOkViaNode: eleTakesUpSpace
11942}));
11943
11944elesfn$r.noninteractive = function () {
11945 var ele = this[0];
11946
11947 if (ele) {
11948 return !ele.interactive();
11949 }
11950};
11951
11952var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11953 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11954});
11955var edgeVisibleViaNode = eleTakesUpSpace;
11956elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11957 ok: eleVisible,
11958 edgeOkViaNode: edgeVisibleViaNode
11959}));
11960
11961elesfn$r.hidden = function () {
11962 var ele = this[0];
11963
11964 if (ele) {
11965 return !ele.visible();
11966 }
11967};
11968
11969elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11970 if (!this.cy().styleEnabled()) {
11971 return false;
11972 }
11973
11974 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11975});
11976elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11977elesfn$r.renderedCss = elesfn$r.renderedStyle;
11978elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11979elesfn$r.pstyle = elesfn$r.parsedStyle;
11980
11981var elesfn$s = {};
11982
11983function defineSwitchFunction(params) {
11984 return function () {
11985 var args = arguments;
11986 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11987
11988 if (args.length === 2) {
11989 var data = args[0];
11990 var handler = args[1];
11991 this.on(params.event, data, handler);
11992 } // e.g. cy.nodes().select( handler )
11993 else if (args.length === 1 && fn(args[0])) {
11994 var _handler = args[0];
11995 this.on(params.event, _handler);
11996 } // e.g. cy.nodes().select()
11997 // e.g. (private) cy.nodes().select(['tapselect'])
11998 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11999 var addlEvents = args.length === 1 ? args[0] : null;
12000
12001 for (var i = 0; i < this.length; i++) {
12002 var ele = this[i];
12003 var able = !params.ableField || ele._private[params.ableField];
12004 var changed = ele._private[params.field] != params.value;
12005
12006 if (params.overrideAble) {
12007 var overrideAble = params.overrideAble(ele);
12008
12009 if (overrideAble !== undefined) {
12010 able = overrideAble;
12011
12012 if (!overrideAble) {
12013 return this;
12014 } // to save cycles assume not able for all on override
12015
12016 }
12017 }
12018
12019 if (able) {
12020 ele._private[params.field] = params.value;
12021
12022 if (changed) {
12023 changedEles.push(ele);
12024 }
12025 }
12026 }
12027
12028 var changedColl = this.spawn(changedEles);
12029 changedColl.updateStyle(); // change of state => possible change of style
12030
12031 changedColl.emit(params.event);
12032
12033 if (addlEvents) {
12034 changedColl.emit(addlEvents);
12035 }
12036 }
12037
12038 return this;
12039 };
12040}
12041
12042function defineSwitchSet(params) {
12043 elesfn$s[params.field] = function () {
12044 var ele = this[0];
12045
12046 if (ele) {
12047 if (params.overrideField) {
12048 var val = params.overrideField(ele);
12049
12050 if (val !== undefined) {
12051 return val;
12052 }
12053 }
12054
12055 return ele._private[params.field];
12056 }
12057 };
12058
12059 elesfn$s[params.on] = defineSwitchFunction({
12060 event: params.on,
12061 field: params.field,
12062 ableField: params.ableField,
12063 overrideAble: params.overrideAble,
12064 value: true
12065 });
12066 elesfn$s[params.off] = defineSwitchFunction({
12067 event: params.off,
12068 field: params.field,
12069 ableField: params.ableField,
12070 overrideAble: params.overrideAble,
12071 value: false
12072 });
12073}
12074
12075defineSwitchSet({
12076 field: 'locked',
12077 overrideField: function overrideField(ele) {
12078 return ele.cy().autolock() ? true : undefined;
12079 },
12080 on: 'lock',
12081 off: 'unlock'
12082});
12083defineSwitchSet({
12084 field: 'grabbable',
12085 overrideField: function overrideField(ele) {
12086 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12087 },
12088 on: 'grabify',
12089 off: 'ungrabify'
12090});
12091defineSwitchSet({
12092 field: 'selected',
12093 ableField: 'selectable',
12094 overrideAble: function overrideAble(ele) {
12095 return ele.cy().autounselectify() ? false : undefined;
12096 },
12097 on: 'select',
12098 off: 'unselect'
12099});
12100defineSwitchSet({
12101 field: 'selectable',
12102 overrideField: function overrideField(ele) {
12103 return ele.cy().autounselectify() ? false : undefined;
12104 },
12105 on: 'selectify',
12106 off: 'unselectify'
12107});
12108elesfn$s.deselect = elesfn$s.unselect;
12109
12110elesfn$s.grabbed = function () {
12111 var ele = this[0];
12112
12113 if (ele) {
12114 return ele._private.grabbed;
12115 }
12116};
12117
12118defineSwitchSet({
12119 field: 'active',
12120 on: 'activate',
12121 off: 'unactivate'
12122});
12123defineSwitchSet({
12124 field: 'pannable',
12125 on: 'panify',
12126 off: 'unpanify'
12127});
12128
12129elesfn$s.inactive = function () {
12130 var ele = this[0];
12131
12132 if (ele) {
12133 return !ele._private.active;
12134 }
12135};
12136
12137var elesfn$t = {}; // DAG functions
12138////////////////
12139
12140var defineDagExtremity = function defineDagExtremity(params) {
12141 return function dagExtremityImpl(selector) {
12142 var eles = this;
12143 var ret = [];
12144
12145 for (var i = 0; i < eles.length; i++) {
12146 var ele = eles[i];
12147
12148 if (!ele.isNode()) {
12149 continue;
12150 }
12151
12152 var disqualified = false;
12153 var edges = ele.connectedEdges();
12154
12155 for (var j = 0; j < edges.length; j++) {
12156 var edge = edges[j];
12157 var src = edge.source();
12158 var tgt = edge.target();
12159
12160 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12161 disqualified = true;
12162 break;
12163 }
12164 }
12165
12166 if (!disqualified) {
12167 ret.push(ele);
12168 }
12169 }
12170
12171 return this.spawn(ret, true).filter(selector);
12172 };
12173};
12174
12175var defineDagOneHop = function defineDagOneHop(params) {
12176 return function (selector) {
12177 var eles = this;
12178 var oEles = [];
12179
12180 for (var i = 0; i < eles.length; i++) {
12181 var ele = eles[i];
12182
12183 if (!ele.isNode()) {
12184 continue;
12185 }
12186
12187 var edges = ele.connectedEdges();
12188
12189 for (var j = 0; j < edges.length; j++) {
12190 var edge = edges[j];
12191 var src = edge.source();
12192 var tgt = edge.target();
12193
12194 if (params.outgoing && src === ele) {
12195 oEles.push(edge);
12196 oEles.push(tgt);
12197 } else if (params.incoming && tgt === ele) {
12198 oEles.push(edge);
12199 oEles.push(src);
12200 }
12201 }
12202 }
12203
12204 return this.spawn(oEles, true).filter(selector);
12205 };
12206};
12207
12208var defineDagAllHops = function defineDagAllHops(params) {
12209 return function (selector) {
12210 var eles = this;
12211 var sEles = [];
12212 var sElesIds = {};
12213
12214 for (;;) {
12215 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12216
12217 if (next.length === 0) {
12218 break;
12219 } // done if none left
12220
12221
12222 var newNext = false;
12223
12224 for (var i = 0; i < next.length; i++) {
12225 var n = next[i];
12226 var nid = n.id();
12227
12228 if (!sElesIds[nid]) {
12229 sElesIds[nid] = true;
12230 sEles.push(n);
12231 newNext = true;
12232 }
12233 }
12234
12235 if (!newNext) {
12236 break;
12237 } // done if touched all outgoers already
12238
12239
12240 eles = next;
12241 }
12242
12243 return this.spawn(sEles, true).filter(selector);
12244 };
12245};
12246
12247elesfn$t.clearTraversalCache = function () {
12248 for (var i = 0; i < this.length; i++) {
12249 this[i]._private.traversalCache = null;
12250 }
12251};
12252
12253extend(elesfn$t, {
12254 // get the root nodes in the DAG
12255 roots: defineDagExtremity({
12256 noIncomingEdges: true
12257 }),
12258 // get the leaf nodes in the DAG
12259 leaves: defineDagExtremity({
12260 noOutgoingEdges: true
12261 }),
12262 // normally called children in graph theory
12263 // these nodes =edges=> outgoing nodes
12264 outgoers: cache(defineDagOneHop({
12265 outgoing: true
12266 }), 'outgoers'),
12267 // aka DAG descendants
12268 successors: defineDagAllHops({
12269 outgoing: true
12270 }),
12271 // normally called parents in graph theory
12272 // these nodes <=edges= incoming nodes
12273 incomers: cache(defineDagOneHop({
12274 incoming: true
12275 }), 'incomers'),
12276 // aka DAG ancestors
12277 predecessors: defineDagAllHops({
12278 incoming: true
12279 })
12280}); // Neighbourhood functions
12281//////////////////////////
12282
12283extend(elesfn$t, {
12284 neighborhood: cache(function (selector) {
12285 var elements = [];
12286 var nodes = this.nodes();
12287
12288 for (var i = 0; i < nodes.length; i++) {
12289 // for all nodes
12290 var node = nodes[i];
12291 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12292
12293 for (var j = 0; j < connectedEdges.length; j++) {
12294 var edge = connectedEdges[j];
12295 var src = edge.source();
12296 var tgt = edge.target();
12297 var otherNode = node === src ? tgt : src; // need check in case of loop
12298
12299 if (otherNode.length > 0) {
12300 elements.push(otherNode[0]); // add node 1 hop away
12301 } // add connected edge
12302
12303
12304 elements.push(edge[0]);
12305 }
12306 }
12307
12308 return this.spawn(elements, true).filter(selector);
12309 }, 'neighborhood'),
12310 closedNeighborhood: function closedNeighborhood(selector) {
12311 return this.neighborhood().add(this).filter(selector);
12312 },
12313 openNeighborhood: function openNeighborhood(selector) {
12314 return this.neighborhood(selector);
12315 }
12316}); // aliases
12317
12318elesfn$t.neighbourhood = elesfn$t.neighborhood;
12319elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12320elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12321/////////////////
12322
12323extend(elesfn$t, {
12324 source: cache(function sourceImpl(selector) {
12325 var ele = this[0];
12326 var src;
12327
12328 if (ele) {
12329 src = ele._private.source || ele.cy().collection();
12330 }
12331
12332 return src && selector ? src.filter(selector) : src;
12333 }, 'source'),
12334 target: cache(function targetImpl(selector) {
12335 var ele = this[0];
12336 var tgt;
12337
12338 if (ele) {
12339 tgt = ele._private.target || ele.cy().collection();
12340 }
12341
12342 return tgt && selector ? tgt.filter(selector) : tgt;
12343 }, 'target'),
12344 sources: defineSourceFunction({
12345 attr: 'source'
12346 }),
12347 targets: defineSourceFunction({
12348 attr: 'target'
12349 })
12350});
12351
12352function defineSourceFunction(params) {
12353 return function sourceImpl(selector) {
12354 var sources = [];
12355
12356 for (var i = 0; i < this.length; i++) {
12357 var ele = this[i];
12358 var src = ele._private[params.attr];
12359
12360 if (src) {
12361 sources.push(src);
12362 }
12363 }
12364
12365 return this.spawn(sources, true).filter(selector);
12366 };
12367}
12368
12369extend(elesfn$t, {
12370 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12371 edgesTo: cache(defineEdgesWithFunction({
12372 thisIsSrc: true
12373 }), 'edgesTo')
12374});
12375
12376function defineEdgesWithFunction(params) {
12377 return function edgesWithImpl(otherNodes) {
12378 var elements = [];
12379 var cy = this._private.cy;
12380 var p = params || {}; // get elements if a selector is specified
12381
12382 if (string(otherNodes)) {
12383 otherNodes = cy.$(otherNodes);
12384 }
12385
12386 for (var h = 0; h < otherNodes.length; h++) {
12387 var edges = otherNodes[h]._private.edges;
12388
12389 for (var i = 0; i < edges.length; i++) {
12390 var edge = edges[i];
12391 var edgeData = edge._private.data;
12392 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12393 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12394 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12395
12396 if (!edgeConnectsThisAndOther) {
12397 continue;
12398 }
12399
12400 if (p.thisIsSrc || p.thisIsTgt) {
12401 if (p.thisIsSrc && !thisToOther) {
12402 continue;
12403 }
12404
12405 if (p.thisIsTgt && !otherToThis) {
12406 continue;
12407 }
12408 }
12409
12410 elements.push(edge);
12411 }
12412 }
12413
12414 return this.spawn(elements, true);
12415 };
12416}
12417
12418extend(elesfn$t, {
12419 connectedEdges: cache(function (selector) {
12420 var retEles = [];
12421 var eles = this;
12422
12423 for (var i = 0; i < eles.length; i++) {
12424 var node = eles[i];
12425
12426 if (!node.isNode()) {
12427 continue;
12428 }
12429
12430 var edges = node._private.edges;
12431
12432 for (var j = 0; j < edges.length; j++) {
12433 var edge = edges[j];
12434 retEles.push(edge);
12435 }
12436 }
12437
12438 return this.spawn(retEles, true).filter(selector);
12439 }, 'connectedEdges'),
12440 connectedNodes: cache(function (selector) {
12441 var retEles = [];
12442 var eles = this;
12443
12444 for (var i = 0; i < eles.length; i++) {
12445 var edge = eles[i];
12446
12447 if (!edge.isEdge()) {
12448 continue;
12449 }
12450
12451 retEles.push(edge.source()[0]);
12452 retEles.push(edge.target()[0]);
12453 }
12454
12455 return this.spawn(retEles, true).filter(selector);
12456 }, 'connectedNodes'),
12457 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12458 codirectedEdges: cache(defineParallelEdgesFunction({
12459 codirected: true
12460 }), 'codirectedEdges')
12461});
12462
12463function defineParallelEdgesFunction(params) {
12464 var defaults = {
12465 codirected: false
12466 };
12467 params = extend({}, defaults, params);
12468 return function parallelEdgesImpl(selector) {
12469 // micro-optimised for renderer
12470 var elements = [];
12471 var edges = this.edges();
12472 var p = params; // look at all the edges in the collection
12473
12474 for (var i = 0; i < edges.length; i++) {
12475 var edge1 = edges[i];
12476 var edge1_p = edge1._private;
12477 var src1 = edge1_p.source;
12478 var srcid1 = src1._private.data.id;
12479 var tgtid1 = edge1_p.data.target;
12480 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12481
12482 for (var j = 0; j < srcEdges1.length; j++) {
12483 var edge2 = srcEdges1[j];
12484 var edge2data = edge2._private.data;
12485 var tgtid2 = edge2data.target;
12486 var srcid2 = edge2data.source;
12487 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12488 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12489
12490 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12491 elements.push(edge2);
12492 }
12493 }
12494 }
12495
12496 return this.spawn(elements, true).filter(selector);
12497 };
12498} // Misc functions
12499/////////////////
12500
12501
12502extend(elesfn$t, {
12503 components: function components(root) {
12504 var self = this;
12505 var cy = self.cy();
12506 var visited = cy.collection();
12507 var unvisited = root == null ? self.nodes() : root.nodes();
12508 var components = [];
12509
12510 if (root != null && unvisited.empty()) {
12511 // root may contain only edges
12512 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12513 }
12514
12515 var visitInComponent = function visitInComponent(node, component) {
12516 visited.merge(node);
12517 unvisited.unmerge(node);
12518 component.merge(node);
12519 };
12520
12521 if (unvisited.empty()) {
12522 return self.spawn();
12523 }
12524
12525 var _loop = function _loop() {
12526 // each iteration yields a component
12527 var cmpt = cy.collection();
12528 components.push(cmpt);
12529 var root = unvisited[0];
12530 visitInComponent(root, cmpt);
12531 self.bfs({
12532 directed: false,
12533 roots: root,
12534 visit: function visit(v) {
12535 return visitInComponent(v, cmpt);
12536 }
12537 });
12538 cmpt.forEach(function (node) {
12539 node.connectedEdges().forEach(function (e) {
12540 // connectedEdges() usually cached
12541 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12542 // has() is cheap
12543 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12544 }
12545 });
12546 });
12547 };
12548
12549 do {
12550 _loop();
12551 } while (unvisited.length > 0);
12552
12553 return components;
12554 },
12555 component: function component() {
12556 var ele = this[0];
12557 return ele.cy().mutableElements().components(ele)[0];
12558 }
12559});
12560elesfn$t.componentsOf = elesfn$t.components;
12561
12562var Collection = function Collection(cy, elements) {
12563 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12564
12565 if (cy === undefined) {
12566 error('A collection must have a reference to the core');
12567 return;
12568 }
12569
12570 var map = new Map$1();
12571 var createdElements = false;
12572
12573 if (!elements) {
12574 elements = [];
12575 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12576 createdElements = true; // make elements from json and restore all at once later
12577
12578 var eles = [];
12579 var elesIds = new Set$1();
12580
12581 for (var i = 0, l = elements.length; i < l; i++) {
12582 var json = elements[i];
12583
12584 if (json.data == null) {
12585 json.data = {};
12586 }
12587
12588 var _data = json.data; // make sure newly created elements have valid ids
12589
12590 if (_data.id == null) {
12591 _data.id = uuid();
12592 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12593 continue; // can't create element if prior id already exists
12594 }
12595
12596 var ele = new Element(cy, json, false);
12597 eles.push(ele);
12598 elesIds.add(_data.id);
12599 }
12600
12601 elements = eles;
12602 }
12603
12604 this.length = 0;
12605
12606 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12607 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12608
12609 if (element$1 == null) {
12610 continue;
12611 }
12612
12613 var id = element$1._private.data.id;
12614
12615 if (!unique || !map.has(id)) {
12616 if (unique) {
12617 map.set(id, {
12618 index: this.length,
12619 ele: element$1
12620 });
12621 }
12622
12623 this[this.length] = element$1;
12624 this.length++;
12625 }
12626 }
12627
12628 this._private = {
12629 eles: this,
12630 cy: cy,
12631
12632 get map() {
12633 if (this.lazyMap == null) {
12634 this.rebuildMap();
12635 }
12636
12637 return this.lazyMap;
12638 },
12639
12640 set map(m) {
12641 this.lazyMap = m;
12642 },
12643
12644 rebuildMap: function rebuildMap() {
12645 var m = this.lazyMap = new Map$1();
12646 var eles = this.eles;
12647
12648 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12649 var _ele = eles[_i2];
12650 m.set(_ele.id(), {
12651 index: _i2,
12652 ele: _ele
12653 });
12654 }
12655 }
12656 };
12657
12658 if (unique) {
12659 this._private.map = map;
12660 } // restore the elements if we created them from json
12661
12662
12663 if (createdElements) {
12664 this.restore();
12665 }
12666}; // Functions
12667////////////////////////////////////////////////////////////////////////////////////////////////////
12668// keep the prototypes in sync (an element has the same functions as a collection)
12669// and use elefn and elesfn as shorthands to the prototypes
12670
12671
12672var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12673
12674elesfn$u.instanceString = function () {
12675 return 'collection';
12676};
12677
12678elesfn$u.spawn = function (eles, unique) {
12679 return new Collection(this.cy(), eles, unique);
12680};
12681
12682elesfn$u.spawnSelf = function () {
12683 return this.spawn(this);
12684};
12685
12686elesfn$u.cy = function () {
12687 return this._private.cy;
12688};
12689
12690elesfn$u.renderer = function () {
12691 return this._private.cy.renderer();
12692};
12693
12694elesfn$u.element = function () {
12695 return this[0];
12696};
12697
12698elesfn$u.collection = function () {
12699 if (collection(this)) {
12700 return this;
12701 } else {
12702 // an element
12703 return new Collection(this._private.cy, [this]);
12704 }
12705};
12706
12707elesfn$u.unique = function () {
12708 return new Collection(this._private.cy, this, true);
12709};
12710
12711elesfn$u.hasElementWithId = function (id) {
12712 id = '' + id; // id must be string
12713
12714 return this._private.map.has(id);
12715};
12716
12717elesfn$u.getElementById = function (id) {
12718 id = '' + id; // id must be string
12719
12720 var cy = this._private.cy;
12721
12722 var entry = this._private.map.get(id);
12723
12724 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12725};
12726
12727elesfn$u.$id = elesfn$u.getElementById;
12728
12729elesfn$u.poolIndex = function () {
12730 var cy = this._private.cy;
12731 var eles = cy._private.elements;
12732 var id = this[0]._private.data.id;
12733 return eles._private.map.get(id).index;
12734};
12735
12736elesfn$u.indexOf = function (ele) {
12737 var id = ele[0]._private.data.id;
12738 return this._private.map.get(id).index;
12739};
12740
12741elesfn$u.indexOfId = function (id) {
12742 id = '' + id; // id must be string
12743
12744 return this._private.map.get(id).index;
12745};
12746
12747elesfn$u.json = function (obj) {
12748 var ele = this.element();
12749 var cy = this.cy();
12750
12751 if (ele == null && obj) {
12752 return this;
12753 } // can't set to no eles
12754
12755
12756 if (ele == null) {
12757 return undefined;
12758 } // can't get from no eles
12759
12760
12761 var p = ele._private;
12762
12763 if (plainObject(obj)) {
12764 // set
12765 cy.startBatch();
12766
12767 if (obj.data) {
12768 ele.data(obj.data);
12769 var _data2 = p.data;
12770
12771 if (ele.isEdge()) {
12772 // source and target are immutable via data()
12773 var move = false;
12774 var spec = {};
12775 var src = obj.data.source;
12776 var tgt = obj.data.target;
12777
12778 if (src != null && src != _data2.source) {
12779 spec.source = '' + src; // id must be string
12780
12781 move = true;
12782 }
12783
12784 if (tgt != null && tgt != _data2.target) {
12785 spec.target = '' + tgt; // id must be string
12786
12787 move = true;
12788 }
12789
12790 if (move) {
12791 ele = ele.move(spec);
12792 }
12793 } else {
12794 // parent is immutable via data()
12795 var newParentValSpecd = 'parent' in obj.data;
12796 var parent = obj.data.parent;
12797
12798 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12799 if (parent === undefined) {
12800 // can't set undefined imperatively, so use null
12801 parent = null;
12802 }
12803
12804 if (parent != null) {
12805 parent = '' + parent; // id must be string
12806 }
12807
12808 ele = ele.move({
12809 parent: parent
12810 });
12811 }
12812 }
12813 }
12814
12815 if (obj.position) {
12816 ele.position(obj.position);
12817 } // ignore group -- immutable
12818
12819
12820 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12821 var obj_k = obj[k];
12822
12823 if (obj_k != null && obj_k !== p[k]) {
12824 if (obj_k) {
12825 ele[trueFnName]();
12826 } else {
12827 ele[falseFnName]();
12828 }
12829 }
12830 };
12831
12832 checkSwitch('removed', 'remove', 'restore');
12833 checkSwitch('selected', 'select', 'unselect');
12834 checkSwitch('selectable', 'selectify', 'unselectify');
12835 checkSwitch('locked', 'lock', 'unlock');
12836 checkSwitch('grabbable', 'grabify', 'ungrabify');
12837 checkSwitch('pannable', 'panify', 'unpanify');
12838
12839 if (obj.classes != null) {
12840 ele.classes(obj.classes);
12841 }
12842
12843 cy.endBatch();
12844 return this;
12845 } else if (obj === undefined) {
12846 // get
12847 var json = {
12848 data: copy(p.data),
12849 position: copy(p.position),
12850 group: p.group,
12851 removed: p.removed,
12852 selected: p.selected,
12853 selectable: p.selectable,
12854 locked: p.locked,
12855 grabbable: p.grabbable,
12856 pannable: p.pannable,
12857 classes: null
12858 };
12859 json.classes = '';
12860 var i = 0;
12861 p.classes.forEach(function (cls) {
12862 return json.classes += i++ === 0 ? cls : ' ' + cls;
12863 });
12864 return json;
12865 }
12866};
12867
12868elesfn$u.jsons = function () {
12869 var jsons = [];
12870
12871 for (var i = 0; i < this.length; i++) {
12872 var ele = this[i];
12873 var json = ele.json();
12874 jsons.push(json);
12875 }
12876
12877 return jsons;
12878};
12879
12880elesfn$u.clone = function () {
12881 var cy = this.cy();
12882 var elesArr = [];
12883
12884 for (var i = 0; i < this.length; i++) {
12885 var ele = this[i];
12886 var json = ele.json();
12887 var clone = new Element(cy, json, false); // NB no restore
12888
12889 elesArr.push(clone);
12890 }
12891
12892 return new Collection(cy, elesArr);
12893};
12894
12895elesfn$u.copy = elesfn$u.clone;
12896
12897elesfn$u.restore = function () {
12898 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12899 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12900 var self = this;
12901 var cy = self.cy();
12902 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12903 // restore the nodes first
12904
12905 var nodes = [];
12906 var edges = [];
12907 var elements;
12908
12909 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12910 var ele = self[_i3];
12911
12912 if (addToPool && !ele.removed()) {
12913 // don't need to handle this ele
12914 continue;
12915 } // keep nodes first in the array and edges after
12916
12917
12918 if (ele.isNode()) {
12919 // put to front of array if node
12920 nodes.push(ele);
12921 } else {
12922 // put to end of array if edge
12923 edges.push(ele);
12924 }
12925 }
12926
12927 elements = nodes.concat(edges);
12928 var i;
12929
12930 var removeFromElements = function removeFromElements() {
12931 elements.splice(i, 1);
12932 i--;
12933 }; // now, restore each element
12934
12935
12936 for (i = 0; i < elements.length; i++) {
12937 var _ele2 = elements[i];
12938 var _private = _ele2._private;
12939 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12940
12941 _ele2.clearTraversalCache(); // set id and validate
12942
12943
12944 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12945 _data3.id = uuid();
12946 } else if (number(_data3.id)) {
12947 _data3.id = '' + _data3.id; // now it's a string
12948 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12949 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
12950
12951 removeFromElements();
12952 continue;
12953 } else if (cy.hasElementWithId(_data3.id)) {
12954 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12955
12956 removeFromElements();
12957 continue;
12958 }
12959
12960 var id = _data3.id; // id is finalised, now let's keep a ref
12961
12962 if (_ele2.isNode()) {
12963 // extra checks for nodes
12964 var pos = _private.position; // make sure the nodes have a defined position
12965
12966 if (pos.x == null) {
12967 pos.x = 0;
12968 }
12969
12970 if (pos.y == null) {
12971 pos.y = 0;
12972 }
12973 }
12974
12975 if (_ele2.isEdge()) {
12976 // extra checks for edges
12977 var edge = _ele2;
12978 var fields = ['source', 'target'];
12979 var fieldsLength = fields.length;
12980 var badSourceOrTarget = false;
12981
12982 for (var j = 0; j < fieldsLength; j++) {
12983 var field = fields[j];
12984 var val = _data3[field];
12985
12986 if (number(val)) {
12987 val = _data3[field] = '' + _data3[field]; // now string
12988 }
12989
12990 if (val == null || val === '') {
12991 // can't create if source or target is not defined properly
12992 error('Can not create edge `' + id + '` with unspecified ' + field);
12993 badSourceOrTarget = true;
12994 } else if (!cy.hasElementWithId(val)) {
12995 // can't create edge if one of its nodes doesn't exist
12996 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12997 badSourceOrTarget = true;
12998 }
12999 }
13000
13001 if (badSourceOrTarget) {
13002 removeFromElements();
13003 continue;
13004 } // can't create this
13005
13006
13007 var src = cy.getElementById(_data3.source);
13008 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
13009
13010 if (src.same(tgt)) {
13011 src._private.edges.push(edge);
13012 } else {
13013 src._private.edges.push(edge);
13014
13015 tgt._private.edges.push(edge);
13016 }
13017
13018 edge._private.source = src;
13019 edge._private.target = tgt;
13020 } // if is edge
13021 // create mock ids / indexes maps for element so it can be used like collections
13022
13023
13024 _private.map = new Map$1();
13025
13026 _private.map.set(id, {
13027 ele: _ele2,
13028 index: 0
13029 });
13030
13031 _private.removed = false;
13032
13033 if (addToPool) {
13034 cy.addToPool(_ele2);
13035 }
13036 } // for each element
13037 // do compound node sanity checks
13038
13039
13040 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13041 // each node
13042 var node = nodes[_i4];
13043 var _data4 = node._private.data;
13044
13045 if (number(_data4.parent)) {
13046 // then automake string
13047 _data4.parent = '' + _data4.parent;
13048 }
13049
13050 var parentId = _data4.parent;
13051 var specifiedParent = parentId != null;
13052
13053 if (specifiedParent) {
13054 var parent = cy.getElementById(parentId);
13055
13056 if (parent.empty()) {
13057 // non-existant parent; just remove it
13058 _data4.parent = undefined;
13059 } else {
13060 var selfAsParent = false;
13061 var ancestor = parent;
13062
13063 while (!ancestor.empty()) {
13064 if (node.same(ancestor)) {
13065 // mark self as parent and remove from data
13066 selfAsParent = true;
13067 _data4.parent = undefined; // remove parent reference
13068 // exit or we loop forever
13069
13070 break;
13071 }
13072
13073 ancestor = ancestor.parent();
13074 }
13075
13076 if (!selfAsParent) {
13077 // connect with children
13078 parent[0]._private.children.push(node);
13079
13080 node._private.parent = parent[0]; // let the core know we have a compound graph
13081
13082 cy_p.hasCompoundNodes = true;
13083 }
13084 } // else
13085
13086 } // if specified parent
13087
13088 } // for each node
13089
13090
13091 if (elements.length > 0) {
13092 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13093
13094 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13095 var _ele3 = restored[_i5];
13096
13097 if (_ele3.isNode()) {
13098 continue;
13099 } // adding an edge invalidates the traversal caches for the parallel edges
13100
13101
13102 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13103
13104
13105 _ele3.source().clearTraversalCache();
13106
13107 _ele3.target().clearTraversalCache();
13108 }
13109
13110 var toUpdateStyle;
13111
13112 if (cy_p.hasCompoundNodes) {
13113 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13114 } else {
13115 toUpdateStyle = restored;
13116 }
13117
13118 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13119
13120 if (notifyRenderer) {
13121 restored.emitAndNotify('add');
13122 } else if (addToPool) {
13123 restored.emit('add');
13124 }
13125 }
13126
13127 return self; // chainability
13128};
13129
13130elesfn$u.removed = function () {
13131 var ele = this[0];
13132 return ele && ele._private.removed;
13133};
13134
13135elesfn$u.inside = function () {
13136 var ele = this[0];
13137 return ele && !ele._private.removed;
13138};
13139
13140elesfn$u.remove = function () {
13141 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13142 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13143 var self = this;
13144 var elesToRemove = [];
13145 var elesToRemoveIds = {};
13146 var cy = self._private.cy; // add connected edges
13147
13148 function addConnectedEdges(node) {
13149 var edges = node._private.edges;
13150
13151 for (var i = 0; i < edges.length; i++) {
13152 add(edges[i]);
13153 }
13154 } // add descendant nodes
13155
13156
13157 function addChildren(node) {
13158 var children = node._private.children;
13159
13160 for (var i = 0; i < children.length; i++) {
13161 add(children[i]);
13162 }
13163 }
13164
13165 function add(ele) {
13166 var alreadyAdded = elesToRemoveIds[ele.id()];
13167
13168 if (removeFromPool && ele.removed() || alreadyAdded) {
13169 return;
13170 } else {
13171 elesToRemoveIds[ele.id()] = true;
13172 }
13173
13174 if (ele.isNode()) {
13175 elesToRemove.push(ele); // nodes are removed last
13176
13177 addConnectedEdges(ele);
13178 addChildren(ele);
13179 } else {
13180 elesToRemove.unshift(ele); // edges are removed first
13181 }
13182 } // make the list of elements to remove
13183 // (may be removing more than specified due to connected edges etc)
13184
13185
13186 for (var i = 0, l = self.length; i < l; i++) {
13187 var ele = self[i];
13188 add(ele);
13189 }
13190
13191 function removeEdgeRef(node, edge) {
13192 var connectedEdges = node._private.edges;
13193 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13194
13195 node.clearTraversalCache();
13196 }
13197
13198 function removeParallelRef(pllEdge) {
13199 // removing an edge invalidates the traversal caches for the parallel edges
13200 pllEdge.clearTraversalCache();
13201 }
13202
13203 var alteredParents = [];
13204 alteredParents.ids = {};
13205
13206 function removeChildRef(parent, ele) {
13207 ele = ele[0];
13208 parent = parent[0];
13209 var children = parent._private.children;
13210 var pid = parent.id();
13211 removeFromArray(children, ele); // remove parent => child ref
13212
13213 ele._private.parent = null; // remove child => parent ref
13214
13215 if (!alteredParents.ids[pid]) {
13216 alteredParents.ids[pid] = true;
13217 alteredParents.push(parent);
13218 }
13219 }
13220
13221 self.dirtyCompoundBoundsCache();
13222
13223 if (removeFromPool) {
13224 cy.removeFromPool(elesToRemove); // remove from core pool
13225 }
13226
13227 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13228 var _ele4 = elesToRemove[_i6];
13229
13230 if (_ele4.isEdge()) {
13231 // remove references to this edge in its connected nodes
13232 var src = _ele4.source()[0];
13233
13234 var tgt = _ele4.target()[0];
13235
13236 removeEdgeRef(src, _ele4);
13237 removeEdgeRef(tgt, _ele4);
13238
13239 var pllEdges = _ele4.parallelEdges();
13240
13241 for (var j = 0; j < pllEdges.length; j++) {
13242 var pllEdge = pllEdges[j];
13243 removeParallelRef(pllEdge);
13244
13245 if (pllEdge.isBundledBezier()) {
13246 pllEdge.dirtyBoundingBoxCache();
13247 }
13248 }
13249 } else {
13250 // remove reference to parent
13251 var parent = _ele4.parent();
13252
13253 if (parent.length !== 0) {
13254 removeChildRef(parent, _ele4);
13255 }
13256 }
13257
13258 if (removeFromPool) {
13259 // mark as removed
13260 _ele4._private.removed = true;
13261 }
13262 } // check to see if we have a compound graph or not
13263
13264
13265 var elesStillInside = cy._private.elements;
13266 cy._private.hasCompoundNodes = false;
13267
13268 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13269 var _ele5 = elesStillInside[_i7];
13270
13271 if (_ele5.isParent()) {
13272 cy._private.hasCompoundNodes = true;
13273 break;
13274 }
13275 }
13276
13277 var removedElements = new Collection(this.cy(), elesToRemove);
13278
13279 if (removedElements.size() > 0) {
13280 // must manually notify since trigger won't do this automatically once removed
13281 if (notifyRenderer) {
13282 removedElements.emitAndNotify('remove');
13283 } else if (removeFromPool) {
13284 removedElements.emit('remove');
13285 }
13286 } // the parents who were modified by the removal need their style updated
13287
13288
13289 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13290 var _ele6 = alteredParents[_i8];
13291
13292 if (!removeFromPool || !_ele6.removed()) {
13293 _ele6.updateStyle();
13294 }
13295 }
13296
13297 return removedElements;
13298};
13299
13300elesfn$u.move = function (struct) {
13301 var cy = this._private.cy;
13302 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13303 // (our calls to remove/restore do not remove from the graph or make events)
13304
13305 var notifyRenderer = false;
13306 var modifyPool = false;
13307
13308 var toString = function toString(id) {
13309 return id == null ? id : '' + id;
13310 }; // id must be string
13311
13312
13313 if (struct.source !== undefined || struct.target !== undefined) {
13314 var srcId = toString(struct.source);
13315 var tgtId = toString(struct.target);
13316 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13317 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13318
13319 if (srcExists || tgtExists) {
13320 cy.batch(function () {
13321 // avoid duplicate style updates
13322 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13323
13324 eles.emitAndNotify('moveout');
13325
13326 for (var i = 0; i < eles.length; i++) {
13327 var ele = eles[i];
13328 var _data5 = ele._private.data;
13329
13330 if (ele.isEdge()) {
13331 if (srcExists) {
13332 _data5.source = srcId;
13333 }
13334
13335 if (tgtExists) {
13336 _data5.target = tgtId;
13337 }
13338 }
13339 }
13340
13341 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13342 });
13343 eles.emitAndNotify('move');
13344 }
13345 } else if (struct.parent !== undefined) {
13346 // move node to new parent
13347 var parentId = toString(struct.parent);
13348 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13349
13350 if (parentExists) {
13351 var pidToAssign = parentId === null ? undefined : parentId;
13352 cy.batch(function () {
13353 // avoid duplicate style updates
13354 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13355
13356 updated.emitAndNotify('moveout');
13357
13358 for (var i = 0; i < eles.length; i++) {
13359 var ele = eles[i];
13360 var _data6 = ele._private.data;
13361
13362 if (ele.isNode()) {
13363 _data6.parent = pidToAssign;
13364 }
13365 }
13366
13367 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13368 });
13369 eles.emitAndNotify('move');
13370 }
13371 }
13372
13373 return this;
13374};
13375
13376[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) {
13377 extend(elesfn$u, props);
13378});
13379
13380var corefn = {
13381 add: function add(opts) {
13382 var elements;
13383 var cy = this; // add the elements
13384
13385 if (elementOrCollection(opts)) {
13386 var eles = opts;
13387
13388 if (eles._private.cy === cy) {
13389 // same instance => just restore
13390 elements = eles.restore();
13391 } else {
13392 // otherwise, copy from json
13393 var jsons = [];
13394
13395 for (var i = 0; i < eles.length; i++) {
13396 var ele = eles[i];
13397 jsons.push(ele.json());
13398 }
13399
13400 elements = new Collection(cy, jsons);
13401 }
13402 } // specify an array of options
13403 else if (array(opts)) {
13404 var _jsons = opts;
13405 elements = new Collection(cy, _jsons);
13406 } // specify via opts.nodes and opts.edges
13407 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13408 var elesByGroup = opts;
13409 var _jsons2 = [];
13410 var grs = ['nodes', 'edges'];
13411
13412 for (var _i = 0, il = grs.length; _i < il; _i++) {
13413 var group = grs[_i];
13414 var elesArray = elesByGroup[group];
13415
13416 if (array(elesArray)) {
13417 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13418 var json = extend({
13419 group: group
13420 }, elesArray[j]);
13421
13422 _jsons2.push(json);
13423 }
13424 }
13425 }
13426
13427 elements = new Collection(cy, _jsons2);
13428 } // specify options for one element
13429 else {
13430 var _json = opts;
13431 elements = new Element(cy, _json).collection();
13432 }
13433
13434 return elements;
13435 },
13436 remove: function remove(collection) {
13437 if (elementOrCollection(collection)) ; else if (string(collection)) {
13438 var selector = collection;
13439 collection = this.$(selector);
13440 }
13441
13442 return collection.remove();
13443 }
13444};
13445
13446/* global Float32Array */
13447
13448/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13449function generateCubicBezier(mX1, mY1, mX2, mY2) {
13450 var NEWTON_ITERATIONS = 4,
13451 NEWTON_MIN_SLOPE = 0.001,
13452 SUBDIVISION_PRECISION = 0.0000001,
13453 SUBDIVISION_MAX_ITERATIONS = 10,
13454 kSplineTableSize = 11,
13455 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13456 float32ArraySupported = typeof Float32Array !== 'undefined';
13457 /* Must contain four arguments. */
13458
13459 if (arguments.length !== 4) {
13460 return false;
13461 }
13462 /* Arguments must be numbers. */
13463
13464
13465 for (var i = 0; i < 4; ++i) {
13466 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13467 return false;
13468 }
13469 }
13470 /* X values must be in the [0, 1] range. */
13471
13472
13473 mX1 = Math.min(mX1, 1);
13474 mX2 = Math.min(mX2, 1);
13475 mX1 = Math.max(mX1, 0);
13476 mX2 = Math.max(mX2, 0);
13477 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13478
13479 function A(aA1, aA2) {
13480 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13481 }
13482
13483 function B(aA1, aA2) {
13484 return 3.0 * aA2 - 6.0 * aA1;
13485 }
13486
13487 function C(aA1) {
13488 return 3.0 * aA1;
13489 }
13490
13491 function calcBezier(aT, aA1, aA2) {
13492 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13493 }
13494
13495 function getSlope(aT, aA1, aA2) {
13496 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13497 }
13498
13499 function newtonRaphsonIterate(aX, aGuessT) {
13500 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13501 var currentSlope = getSlope(aGuessT, mX1, mX2);
13502
13503 if (currentSlope === 0.0) {
13504 return aGuessT;
13505 }
13506
13507 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13508 aGuessT -= currentX / currentSlope;
13509 }
13510
13511 return aGuessT;
13512 }
13513
13514 function calcSampleValues() {
13515 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13516 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13517 }
13518 }
13519
13520 function binarySubdivide(aX, aA, aB) {
13521 var currentX,
13522 currentT,
13523 i = 0;
13524
13525 do {
13526 currentT = aA + (aB - aA) / 2.0;
13527 currentX = calcBezier(currentT, mX1, mX2) - aX;
13528
13529 if (currentX > 0.0) {
13530 aB = currentT;
13531 } else {
13532 aA = currentT;
13533 }
13534 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13535
13536 return currentT;
13537 }
13538
13539 function getTForX(aX) {
13540 var intervalStart = 0.0,
13541 currentSample = 1,
13542 lastSample = kSplineTableSize - 1;
13543
13544 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13545 intervalStart += kSampleStepSize;
13546 }
13547
13548 --currentSample;
13549 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13550 guessForT = intervalStart + dist * kSampleStepSize,
13551 initialSlope = getSlope(guessForT, mX1, mX2);
13552
13553 if (initialSlope >= NEWTON_MIN_SLOPE) {
13554 return newtonRaphsonIterate(aX, guessForT);
13555 } else if (initialSlope === 0.0) {
13556 return guessForT;
13557 } else {
13558 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13559 }
13560 }
13561
13562 var _precomputed = false;
13563
13564 function precompute() {
13565 _precomputed = true;
13566
13567 if (mX1 !== mY1 || mX2 !== mY2) {
13568 calcSampleValues();
13569 }
13570 }
13571
13572 var f = function f(aX) {
13573 if (!_precomputed) {
13574 precompute();
13575 }
13576
13577 if (mX1 === mY1 && mX2 === mY2) {
13578 return aX;
13579 }
13580
13581 if (aX === 0) {
13582 return 0;
13583 }
13584
13585 if (aX === 1) {
13586 return 1;
13587 }
13588
13589 return calcBezier(getTForX(aX), mY1, mY2);
13590 };
13591
13592 f.getControlPoints = function () {
13593 return [{
13594 x: mX1,
13595 y: mY1
13596 }, {
13597 x: mX2,
13598 y: mY2
13599 }];
13600 };
13601
13602 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13603
13604 f.toString = function () {
13605 return str;
13606 };
13607
13608 return f;
13609}
13610
13611/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13612
13613/* 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
13614 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13615var generateSpringRK4 = function () {
13616 function springAccelerationForState(state) {
13617 return -state.tension * state.x - state.friction * state.v;
13618 }
13619
13620 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13621 var state = {
13622 x: initialState.x + derivative.dx * dt,
13623 v: initialState.v + derivative.dv * dt,
13624 tension: initialState.tension,
13625 friction: initialState.friction
13626 };
13627 return {
13628 dx: state.v,
13629 dv: springAccelerationForState(state)
13630 };
13631 }
13632
13633 function springIntegrateState(state, dt) {
13634 var a = {
13635 dx: state.v,
13636 dv: springAccelerationForState(state)
13637 },
13638 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13639 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13640 d = springEvaluateStateWithDerivative(state, dt, c),
13641 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13642 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13643 state.x = state.x + dxdt * dt;
13644 state.v = state.v + dvdt * dt;
13645 return state;
13646 }
13647
13648 return function springRK4Factory(tension, friction, duration) {
13649 var initState = {
13650 x: -1,
13651 v: 0,
13652 tension: null,
13653 friction: null
13654 },
13655 path = [0],
13656 time_lapsed = 0,
13657 tolerance = 1 / 10000,
13658 DT = 16 / 1000,
13659 have_duration,
13660 dt,
13661 last_state;
13662 tension = parseFloat(tension) || 500;
13663 friction = parseFloat(friction) || 20;
13664 duration = duration || null;
13665 initState.tension = tension;
13666 initState.friction = friction;
13667 have_duration = duration !== null;
13668 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13669
13670 if (have_duration) {
13671 /* Run the simulation without a duration. */
13672 time_lapsed = springRK4Factory(tension, friction);
13673 /* Compute the adjusted time delta. */
13674
13675 dt = time_lapsed / duration * DT;
13676 } else {
13677 dt = DT;
13678 }
13679
13680 for (;;) {
13681 /* Next/step function .*/
13682 last_state = springIntegrateState(last_state || initState, dt);
13683 /* Store the position. */
13684
13685 path.push(1 + last_state.x);
13686 time_lapsed += 16;
13687 /* If the change threshold is reached, break. */
13688
13689 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13690 break;
13691 }
13692 }
13693 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13694 computed path and returns a snapshot of the position according to a given percentComplete. */
13695
13696
13697 return !have_duration ? time_lapsed : function (percentComplete) {
13698 return path[percentComplete * (path.length - 1) | 0];
13699 };
13700 };
13701}();
13702
13703var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13704 var bezier = generateCubicBezier(t1, p1, t2, p2);
13705 return function (start, end, percent) {
13706 return start + (end - start) * bezier(percent);
13707 };
13708};
13709
13710var easings = {
13711 'linear': function linear(start, end, percent) {
13712 return start + (end - start) * percent;
13713 },
13714 // default easings
13715 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13716 'ease-in': cubicBezier(0.42, 0, 1, 1),
13717 'ease-out': cubicBezier(0, 0, 0.58, 1),
13718 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13719 // sine
13720 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13721 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13722 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13723 // quad
13724 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13725 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13726 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13727 // cubic
13728 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13729 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13730 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13731 // quart
13732 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13733 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13734 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13735 // quint
13736 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13737 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13738 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13739 // expo
13740 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13741 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13742 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13743 // circ
13744 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13745 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13746 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13747 // user param easings...
13748 'spring': function spring(tension, friction, duration) {
13749 if (duration === 0) {
13750 // can't get a spring w/ duration 0
13751 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13752 }
13753
13754 var spring = generateSpringRK4(tension, friction, duration);
13755 return function (start, end, percent) {
13756 return start + (end - start) * spring(percent);
13757 };
13758 },
13759 'cubic-bezier': cubicBezier
13760};
13761
13762function getEasedValue(type, start, end, percent, easingFn) {
13763 if (percent === 1) {
13764 return end;
13765 }
13766
13767 if (start === end) {
13768 return end;
13769 }
13770
13771 var val = easingFn(start, end, percent);
13772
13773 if (type == null) {
13774 return val;
13775 }
13776
13777 if (type.roundValue || type.color) {
13778 val = Math.round(val);
13779 }
13780
13781 if (type.min !== undefined) {
13782 val = Math.max(val, type.min);
13783 }
13784
13785 if (type.max !== undefined) {
13786 val = Math.min(val, type.max);
13787 }
13788
13789 return val;
13790}
13791
13792function getValue(prop, spec) {
13793 if (prop.pfValue != null || prop.value != null) {
13794 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13795 return prop.pfValue;
13796 } else {
13797 return prop.value;
13798 }
13799 } else {
13800 return prop;
13801 }
13802}
13803
13804function ease(startProp, endProp, percent, easingFn, propSpec) {
13805 var type = propSpec != null ? propSpec.type : null;
13806
13807 if (percent < 0) {
13808 percent = 0;
13809 } else if (percent > 1) {
13810 percent = 1;
13811 }
13812
13813 var start = getValue(startProp, propSpec);
13814 var end = getValue(endProp, propSpec);
13815
13816 if (number(start) && number(end)) {
13817 return getEasedValue(type, start, end, percent, easingFn);
13818 } else if (array(start) && array(end)) {
13819 var easedArr = [];
13820
13821 for (var i = 0; i < end.length; i++) {
13822 var si = start[i];
13823 var ei = end[i];
13824
13825 if (si != null && ei != null) {
13826 var val = getEasedValue(type, si, ei, percent, easingFn);
13827 easedArr.push(val);
13828 } else {
13829 easedArr.push(ei);
13830 }
13831 }
13832
13833 return easedArr;
13834 }
13835
13836 return undefined;
13837}
13838
13839function step(self, ani, now, isCore) {
13840 var isEles = !isCore;
13841 var _p = self._private;
13842 var ani_p = ani._private;
13843 var pEasing = ani_p.easing;
13844 var startTime = ani_p.startTime;
13845 var cy = isCore ? self : self.cy();
13846 var style = cy.style();
13847
13848 if (!ani_p.easingImpl) {
13849 if (pEasing == null) {
13850 // use default
13851 ani_p.easingImpl = easings['linear'];
13852 } else {
13853 // then define w/ name
13854 var easingVals;
13855
13856 if (string(pEasing)) {
13857 var easingProp = style.parse('transition-timing-function', pEasing);
13858 easingVals = easingProp.value;
13859 } else {
13860 // then assume preparsed array
13861 easingVals = pEasing;
13862 }
13863
13864 var name, args;
13865
13866 if (string(easingVals)) {
13867 name = easingVals;
13868 args = [];
13869 } else {
13870 name = easingVals[1];
13871 args = easingVals.slice(2).map(function (n) {
13872 return +n;
13873 });
13874 }
13875
13876 if (args.length > 0) {
13877 // create with args
13878 if (name === 'spring') {
13879 args.push(ani_p.duration); // need duration to generate spring
13880 }
13881
13882 ani_p.easingImpl = easings[name].apply(null, args);
13883 } else {
13884 // static impl by name
13885 ani_p.easingImpl = easings[name];
13886 }
13887 }
13888 }
13889
13890 var easing = ani_p.easingImpl;
13891 var percent;
13892
13893 if (ani_p.duration === 0) {
13894 percent = 1;
13895 } else {
13896 percent = (now - startTime) / ani_p.duration;
13897 }
13898
13899 if (ani_p.applying) {
13900 percent = ani_p.progress;
13901 }
13902
13903 if (percent < 0) {
13904 percent = 0;
13905 } else if (percent > 1) {
13906 percent = 1;
13907 }
13908
13909 if (ani_p.delay == null) {
13910 // then update
13911 var startPos = ani_p.startPosition;
13912 var endPos = ani_p.position;
13913
13914 if (endPos && isEles && !self.locked()) {
13915 var newPos = {};
13916
13917 if (valid(startPos.x, endPos.x)) {
13918 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13919 }
13920
13921 if (valid(startPos.y, endPos.y)) {
13922 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13923 }
13924
13925 self.position(newPos);
13926 }
13927
13928 var startPan = ani_p.startPan;
13929 var endPan = ani_p.pan;
13930 var pan = _p.pan;
13931 var animatingPan = endPan != null && isCore;
13932
13933 if (animatingPan) {
13934 if (valid(startPan.x, endPan.x)) {
13935 pan.x = ease(startPan.x, endPan.x, percent, easing);
13936 }
13937
13938 if (valid(startPan.y, endPan.y)) {
13939 pan.y = ease(startPan.y, endPan.y, percent, easing);
13940 }
13941
13942 self.emit('pan');
13943 }
13944
13945 var startZoom = ani_p.startZoom;
13946 var endZoom = ani_p.zoom;
13947 var animatingZoom = endZoom != null && isCore;
13948
13949 if (animatingZoom) {
13950 if (valid(startZoom, endZoom)) {
13951 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13952 }
13953
13954 self.emit('zoom');
13955 }
13956
13957 if (animatingPan || animatingZoom) {
13958 self.emit('viewport');
13959 }
13960
13961 var props = ani_p.style;
13962
13963 if (props && props.length > 0 && isEles) {
13964 for (var i = 0; i < props.length; i++) {
13965 var prop = props[i];
13966 var _name = prop.name;
13967 var end = prop;
13968 var start = ani_p.startStyle[_name];
13969 var propSpec = style.properties[start.name];
13970 var easedVal = ease(start, end, percent, easing, propSpec);
13971 style.overrideBypass(self, _name, easedVal);
13972 } // for props
13973
13974
13975 self.emit('style');
13976 } // if
13977
13978 }
13979
13980 ani_p.progress = percent;
13981 return percent;
13982}
13983
13984function valid(start, end) {
13985 if (start == null || end == null) {
13986 return false;
13987 }
13988
13989 if (number(start) && number(end)) {
13990 return true;
13991 } else if (start && end) {
13992 return true;
13993 }
13994
13995 return false;
13996}
13997
13998function startAnimation(self, ani, now, isCore) {
13999 var ani_p = ani._private;
14000 ani_p.started = true;
14001 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14002}
14003
14004function stepAll(now, cy) {
14005 var eles = cy._private.aniEles;
14006 var doneEles = [];
14007
14008 function stepOne(ele, isCore) {
14009 var _p = ele._private;
14010 var current = _p.animation.current;
14011 var queue = _p.animation.queue;
14012 var ranAnis = false; // if nothing currently animating, get something from the queue
14013
14014 if (current.length === 0) {
14015 var next = queue.shift();
14016
14017 if (next) {
14018 current.push(next);
14019 }
14020 }
14021
14022 var callbacks = function callbacks(_callbacks) {
14023 for (var j = _callbacks.length - 1; j >= 0; j--) {
14024 var cb = _callbacks[j];
14025 cb();
14026 }
14027
14028 _callbacks.splice(0, _callbacks.length);
14029 }; // step and remove if done
14030
14031
14032 for (var i = current.length - 1; i >= 0; i--) {
14033 var ani = current[i];
14034 var ani_p = ani._private;
14035
14036 if (ani_p.stopped) {
14037 current.splice(i, 1);
14038 ani_p.hooked = false;
14039 ani_p.playing = false;
14040 ani_p.started = false;
14041 callbacks(ani_p.frames);
14042 continue;
14043 }
14044
14045 if (!ani_p.playing && !ani_p.applying) {
14046 continue;
14047 } // an apply() while playing shouldn't do anything
14048
14049
14050 if (ani_p.playing && ani_p.applying) {
14051 ani_p.applying = false;
14052 }
14053
14054 if (!ani_p.started) {
14055 startAnimation(ele, ani, now);
14056 }
14057
14058 step(ele, ani, now, isCore);
14059
14060 if (ani_p.applying) {
14061 ani_p.applying = false;
14062 }
14063
14064 callbacks(ani_p.frames);
14065
14066 if (ani_p.step != null) {
14067 ani_p.step(now);
14068 }
14069
14070 if (ani.completed()) {
14071 current.splice(i, 1);
14072 ani_p.hooked = false;
14073 ani_p.playing = false;
14074 ani_p.started = false;
14075 callbacks(ani_p.completes);
14076 }
14077
14078 ranAnis = true;
14079 }
14080
14081 if (!isCore && current.length === 0 && queue.length === 0) {
14082 doneEles.push(ele);
14083 }
14084
14085 return ranAnis;
14086 } // stepElement
14087 // handle all eles
14088
14089
14090 var ranEleAni = false;
14091
14092 for (var e = 0; e < eles.length; e++) {
14093 var ele = eles[e];
14094 var handledThisEle = stepOne(ele);
14095 ranEleAni = ranEleAni || handledThisEle;
14096 } // each element
14097
14098
14099 var ranCoreAni = stepOne(cy, true); // notify renderer
14100
14101 if (ranEleAni || ranCoreAni) {
14102 if (eles.length > 0) {
14103 cy.notify('draw', eles);
14104 } else {
14105 cy.notify('draw');
14106 }
14107 } // remove elements from list of currently animating if its queues are empty
14108
14109
14110 eles.unmerge(doneEles);
14111 cy.emit('step');
14112} // stepAll
14113
14114var corefn$1 = {
14115 // pull in animation functions
14116 animate: define$3.animate(),
14117 animation: define$3.animation(),
14118 animated: define$3.animated(),
14119 clearQueue: define$3.clearQueue(),
14120 delay: define$3.delay(),
14121 delayAnimation: define$3.delayAnimation(),
14122 stop: define$3.stop(),
14123 addToAnimationPool: function addToAnimationPool(eles) {
14124 var cy = this;
14125
14126 if (!cy.styleEnabled()) {
14127 return;
14128 } // save cycles when no style used
14129
14130
14131 cy._private.aniEles.merge(eles);
14132 },
14133 stopAnimationLoop: function stopAnimationLoop() {
14134 this._private.animationsRunning = false;
14135 },
14136 startAnimationLoop: function startAnimationLoop() {
14137 var cy = this;
14138 cy._private.animationsRunning = true;
14139
14140 if (!cy.styleEnabled()) {
14141 return;
14142 } // save cycles when no style used
14143 // NB the animation loop will exec in headless environments if style enabled
14144 // and explicit cy.destroy() is necessary to stop the loop
14145
14146
14147 function headlessStep() {
14148 if (!cy._private.animationsRunning) {
14149 return;
14150 }
14151
14152 requestAnimationFrame(function animationStep(now) {
14153 stepAll(now, cy);
14154 headlessStep();
14155 });
14156 }
14157
14158 var renderer = cy.renderer();
14159
14160 if (renderer && renderer.beforeRender) {
14161 // let the renderer schedule animations
14162 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14163 stepAll(now, cy);
14164 }, renderer.beforeRenderPriorities.animations);
14165 } else {
14166 // manage the animation loop ourselves
14167 headlessStep(); // first call
14168 }
14169 }
14170};
14171
14172var emitterOptions$1 = {
14173 qualifierCompare: function qualifierCompare(selector1, selector2) {
14174 if (selector1 == null || selector2 == null) {
14175 return selector1 == null && selector2 == null;
14176 } else {
14177 return selector1.sameText(selector2);
14178 }
14179 },
14180 eventMatches: function eventMatches(cy, listener, eventObj) {
14181 var selector = listener.qualifier;
14182
14183 if (selector != null) {
14184 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14185 }
14186
14187 return true;
14188 },
14189 addEventFields: function addEventFields(cy, evt) {
14190 evt.cy = cy;
14191 evt.target = cy;
14192 },
14193 callbackContext: function callbackContext(cy, listener, eventObj) {
14194 return listener.qualifier != null ? eventObj.target : cy;
14195 }
14196};
14197
14198var argSelector$1 = function argSelector(arg) {
14199 if (string(arg)) {
14200 return new Selector(arg);
14201 } else {
14202 return arg;
14203 }
14204};
14205
14206var elesfn$v = {
14207 createEmitter: function createEmitter() {
14208 var _p = this._private;
14209
14210 if (!_p.emitter) {
14211 _p.emitter = new Emitter(emitterOptions$1, this);
14212 }
14213
14214 return this;
14215 },
14216 emitter: function emitter() {
14217 return this._private.emitter;
14218 },
14219 on: function on(events, selector, callback) {
14220 this.emitter().on(events, argSelector$1(selector), callback);
14221 return this;
14222 },
14223 removeListener: function removeListener(events, selector, callback) {
14224 this.emitter().removeListener(events, argSelector$1(selector), callback);
14225 return this;
14226 },
14227 removeAllListeners: function removeAllListeners() {
14228 this.emitter().removeAllListeners();
14229 return this;
14230 },
14231 one: function one(events, selector, callback) {
14232 this.emitter().one(events, argSelector$1(selector), callback);
14233 return this;
14234 },
14235 once: function once(events, selector, callback) {
14236 this.emitter().one(events, argSelector$1(selector), callback);
14237 return this;
14238 },
14239 emit: function emit(events, extraParams) {
14240 this.emitter().emit(events, extraParams);
14241 return this;
14242 },
14243 emitAndNotify: function emitAndNotify(event, eles) {
14244 this.emit(event);
14245 this.notify(event, eles);
14246 return this;
14247 }
14248};
14249define$3.eventAliasesOn(elesfn$v);
14250
14251var corefn$2 = {
14252 png: function png(options) {
14253 var renderer = this._private.renderer;
14254 options = options || {};
14255 return renderer.png(options);
14256 },
14257 jpg: function jpg(options) {
14258 var renderer = this._private.renderer;
14259 options = options || {};
14260 options.bg = options.bg || '#fff';
14261 return renderer.jpg(options);
14262 }
14263};
14264corefn$2.jpeg = corefn$2.jpg;
14265
14266var corefn$3 = {
14267 layout: function layout(options) {
14268 var cy = this;
14269
14270 if (options == null) {
14271 error('Layout options must be specified to make a layout');
14272 return;
14273 }
14274
14275 if (options.name == null) {
14276 error('A `name` must be specified to make a layout');
14277 return;
14278 }
14279
14280 var name = options.name;
14281 var Layout = cy.extension('layout', name);
14282
14283 if (Layout == null) {
14284 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14285 return;
14286 }
14287
14288 var eles;
14289
14290 if (string(options.eles)) {
14291 eles = cy.$(options.eles);
14292 } else {
14293 eles = options.eles != null ? options.eles : cy.$();
14294 }
14295
14296 var layout = new Layout(extend({}, options, {
14297 cy: cy,
14298 eles: eles
14299 }));
14300 return layout;
14301 }
14302};
14303corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14304
14305var corefn$4 = {
14306 notify: function notify(eventName, eventEles) {
14307 var _p = this._private;
14308
14309 if (this.batching()) {
14310 _p.batchNotifications = _p.batchNotifications || {};
14311 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14312
14313 if (eventEles != null) {
14314 eles.merge(eventEles);
14315 }
14316
14317 return; // notifications are disabled during batching
14318 }
14319
14320 if (!_p.notificationsEnabled) {
14321 return;
14322 } // exit on disabled
14323
14324
14325 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14326
14327 if (this.destroyed() || !renderer) {
14328 return;
14329 }
14330
14331 renderer.notify(eventName, eventEles);
14332 },
14333 notifications: function notifications(bool) {
14334 var p = this._private;
14335
14336 if (bool === undefined) {
14337 return p.notificationsEnabled;
14338 } else {
14339 p.notificationsEnabled = bool ? true : false;
14340 }
14341
14342 return this;
14343 },
14344 noNotifications: function noNotifications(callback) {
14345 this.notifications(false);
14346 callback();
14347 this.notifications(true);
14348 },
14349 batching: function batching() {
14350 return this._private.batchCount > 0;
14351 },
14352 startBatch: function startBatch() {
14353 var _p = this._private;
14354
14355 if (_p.batchCount == null) {
14356 _p.batchCount = 0;
14357 }
14358
14359 if (_p.batchCount === 0) {
14360 _p.batchStyleEles = this.collection();
14361 _p.batchNotifications = {};
14362 }
14363
14364 _p.batchCount++;
14365 return this;
14366 },
14367 endBatch: function endBatch() {
14368 var _p = this._private;
14369
14370 if (_p.batchCount === 0) {
14371 return this;
14372 }
14373
14374 _p.batchCount--;
14375
14376 if (_p.batchCount === 0) {
14377 // update style for dirty eles
14378 _p.batchStyleEles.updateStyle();
14379
14380 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14381
14382 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14383 var eles = _p.batchNotifications[eventName];
14384
14385 if (eles.empty()) {
14386 renderer.notify(eventName);
14387 } else {
14388 renderer.notify(eventName, eles);
14389 }
14390 });
14391 }
14392
14393 return this;
14394 },
14395 batch: function batch(callback) {
14396 this.startBatch();
14397 callback();
14398 this.endBatch();
14399 return this;
14400 },
14401 // for backwards compatibility
14402 batchData: function batchData(map) {
14403 var cy = this;
14404 return this.batch(function () {
14405 var ids = Object.keys(map);
14406
14407 for (var i = 0; i < ids.length; i++) {
14408 var id = ids[i];
14409 var data = map[id];
14410 var ele = cy.getElementById(id);
14411 ele.data(data);
14412 }
14413 });
14414 }
14415};
14416
14417var rendererDefaults = defaults({
14418 hideEdgesOnViewport: false,
14419 textureOnViewport: false,
14420 motionBlur: false,
14421 motionBlurOpacity: 0.05,
14422 pixelRatio: undefined,
14423 desktopTapThreshold: 4,
14424 touchTapThreshold: 8,
14425 wheelSensitivity: 1,
14426 debug: false,
14427 showFps: false
14428});
14429var corefn$5 = {
14430 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14431 var r = this._private.renderer;
14432 r.renderTo(context, zoom, pan, pxRatio);
14433 return this;
14434 },
14435 renderer: function renderer() {
14436 return this._private.renderer;
14437 },
14438 forceRender: function forceRender() {
14439 this.notify('draw');
14440 return this;
14441 },
14442 resize: function resize() {
14443 this.invalidateSize();
14444 this.emitAndNotify('resize');
14445 return this;
14446 },
14447 initRenderer: function initRenderer(options) {
14448 var cy = this;
14449 var RendererProto = cy.extension('renderer', options.name);
14450
14451 if (RendererProto == null) {
14452 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14453 return;
14454 }
14455
14456 if (options.wheelSensitivity !== undefined) {
14457 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.");
14458 }
14459
14460 var rOpts = rendererDefaults(options);
14461 rOpts.cy = cy;
14462 cy._private.renderer = new RendererProto(rOpts);
14463 this.notify('init');
14464 },
14465 destroyRenderer: function destroyRenderer() {
14466 var cy = this;
14467 cy.notify('destroy'); // destroy the renderer
14468
14469 var domEle = cy.container();
14470
14471 if (domEle) {
14472 domEle._cyreg = null;
14473
14474 while (domEle.childNodes.length > 0) {
14475 domEle.removeChild(domEle.childNodes[0]);
14476 }
14477 }
14478
14479 cy._private.renderer = null; // to be extra safe, remove the ref
14480
14481 cy.mutableElements().forEach(function (ele) {
14482 var _p = ele._private;
14483 _p.rscratch = {};
14484 _p.rstyle = {};
14485 _p.animation.current = [];
14486 _p.animation.queue = [];
14487 });
14488 },
14489 onRender: function onRender(fn) {
14490 return this.on('render', fn);
14491 },
14492 offRender: function offRender(fn) {
14493 return this.off('render', fn);
14494 }
14495};
14496corefn$5.invalidateDimensions = corefn$5.resize;
14497
14498var corefn$6 = {
14499 // get a collection
14500 // - empty collection on no args
14501 // - collection of elements in the graph on selector arg
14502 // - guarantee a returned collection when elements or collection specified
14503 collection: function collection(eles, opts) {
14504 if (string(eles)) {
14505 return this.$(eles);
14506 } else if (elementOrCollection(eles)) {
14507 return eles.collection();
14508 } else if (array(eles)) {
14509 return new Collection(this, eles, opts);
14510 }
14511
14512 return new Collection(this);
14513 },
14514 nodes: function nodes(selector) {
14515 var nodes = this.$(function (ele) {
14516 return ele.isNode();
14517 });
14518
14519 if (selector) {
14520 return nodes.filter(selector);
14521 }
14522
14523 return nodes;
14524 },
14525 edges: function edges(selector) {
14526 var edges = this.$(function (ele) {
14527 return ele.isEdge();
14528 });
14529
14530 if (selector) {
14531 return edges.filter(selector);
14532 }
14533
14534 return edges;
14535 },
14536 // search the graph like jQuery
14537 $: function $(selector) {
14538 var eles = this._private.elements;
14539
14540 if (selector) {
14541 return eles.filter(selector);
14542 } else {
14543 return eles.spawnSelf();
14544 }
14545 },
14546 mutableElements: function mutableElements() {
14547 return this._private.elements;
14548 }
14549}; // aliases
14550
14551corefn$6.elements = corefn$6.filter = corefn$6.$;
14552
14553var styfn = {}; // keys for style blocks, e.g. ttfftt
14554
14555var TRUE = 't';
14556var FALSE = 'f'; // (potentially expensive calculation)
14557// apply the style to the element based on
14558// - its bypass
14559// - what selectors match it
14560
14561styfn.apply = function (eles) {
14562 var self = this;
14563 var _p = self._private;
14564 var cy = _p.cy;
14565 var updatedEles = cy.collection();
14566
14567 for (var ie = 0; ie < eles.length; ie++) {
14568 var ele = eles[ie];
14569 var cxtMeta = self.getContextMeta(ele);
14570
14571 if (cxtMeta.empty) {
14572 continue;
14573 }
14574
14575 var cxtStyle = self.getContextStyle(cxtMeta);
14576 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14577
14578 if (ele._private.appliedInitStyle) {
14579 self.updateTransitions(ele, app.diffProps);
14580 } else {
14581 ele._private.appliedInitStyle = true;
14582 }
14583
14584 var hintsDiff = self.updateStyleHints(ele);
14585
14586 if (hintsDiff) {
14587 updatedEles.push(ele);
14588 }
14589 } // for elements
14590
14591
14592 return updatedEles;
14593};
14594
14595styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14596 var self = this;
14597 var cache = self._private.propDiffs = self._private.propDiffs || {};
14598 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14599 var cachedVal = cache[dualCxtKey];
14600
14601 if (cachedVal) {
14602 return cachedVal;
14603 }
14604
14605 var diffProps = [];
14606 var addedProp = {};
14607
14608 for (var i = 0; i < self.length; i++) {
14609 var cxt = self[i];
14610 var oldHasCxt = oldCxtKey[i] === TRUE;
14611 var newHasCxt = newCxtKey[i] === TRUE;
14612 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14613 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14614
14615 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14616 var props = void 0;
14617
14618 if (cxtHasDiffed && cxtHasMappedProps) {
14619 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14620 } else if (cxtHasDiffed) {
14621 props = cxt.properties; // need to check them all
14622 } else if (cxtHasMappedProps) {
14623 props = cxt.mappedProperties; // only need to check mapped
14624 }
14625
14626 for (var j = 0; j < props.length; j++) {
14627 var prop = props[j];
14628 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14629 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14630 // is cached)
14631
14632 var laterCxtOverrides = false;
14633
14634 for (var k = i + 1; k < self.length; k++) {
14635 var laterCxt = self[k];
14636 var hasLaterCxt = newCxtKey[k] === TRUE;
14637
14638 if (!hasLaterCxt) {
14639 continue;
14640 } // can't override unless the context is active
14641
14642
14643 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14644
14645 if (laterCxtOverrides) {
14646 break;
14647 } // exit early as long as one later context overrides
14648
14649 }
14650
14651 if (!addedProp[name] && !laterCxtOverrides) {
14652 addedProp[name] = true;
14653 diffProps.push(name);
14654 }
14655 } // for props
14656
14657 } // if
14658
14659 } // for contexts
14660
14661
14662 cache[dualCxtKey] = diffProps;
14663 return diffProps;
14664};
14665
14666styfn.getContextMeta = function (ele) {
14667 var self = this;
14668 var cxtKey = '';
14669 var diffProps;
14670 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
14671
14672 for (var i = 0; i < self.length; i++) {
14673 var context = self[i];
14674 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14675
14676 if (contextSelectorMatches) {
14677 cxtKey += TRUE;
14678 } else {
14679 cxtKey += FALSE;
14680 }
14681 } // for context
14682
14683
14684 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14685 ele._private.styleCxtKey = cxtKey;
14686 return {
14687 key: cxtKey,
14688 diffPropNames: diffProps,
14689 empty: diffProps.length === 0
14690 };
14691}; // gets a computed ele style object based on matched contexts
14692
14693
14694styfn.getContextStyle = function (cxtMeta) {
14695 var cxtKey = cxtMeta.key;
14696 var self = this;
14697 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14698
14699 if (cxtStyles[cxtKey]) {
14700 return cxtStyles[cxtKey];
14701 }
14702
14703 var style = {
14704 _private: {
14705 key: cxtKey
14706 }
14707 };
14708
14709 for (var i = 0; i < self.length; i++) {
14710 var cxt = self[i];
14711 var hasCxt = cxtKey[i] === TRUE;
14712
14713 if (!hasCxt) {
14714 continue;
14715 }
14716
14717 for (var j = 0; j < cxt.properties.length; j++) {
14718 var prop = cxt.properties[j];
14719 style[prop.name] = prop;
14720 }
14721 }
14722
14723 cxtStyles[cxtKey] = style;
14724 return style;
14725};
14726
14727styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14728 var self = this;
14729 var diffProps = cxtMeta.diffPropNames;
14730 var retDiffProps = {};
14731 var types = self.types;
14732
14733 for (var i = 0; i < diffProps.length; i++) {
14734 var diffPropName = diffProps[i];
14735 var cxtProp = cxtStyle[diffPropName];
14736 var eleProp = ele.pstyle(diffPropName);
14737
14738 if (!cxtProp) {
14739 // no context prop means delete
14740 if (!eleProp) {
14741 continue; // no existing prop means nothing needs to be removed
14742 // nb affects initial application on mapped values like control-point-distances
14743 } else if (eleProp.bypass) {
14744 cxtProp = {
14745 name: diffPropName,
14746 deleteBypassed: true
14747 };
14748 } else {
14749 cxtProp = {
14750 name: diffPropName,
14751 "delete": true
14752 };
14753 }
14754 } // save cycles when the context prop doesn't need to be applied
14755
14756
14757 if (eleProp === cxtProp) {
14758 continue;
14759 } // save cycles when a mapped context prop doesn't need to be applied
14760
14761
14762 if (cxtProp.mapped === types.fn // context prop is function mapper
14763 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14764 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14765 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14766 ) {
14767 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14768 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14769
14770 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14771
14772 if (fnValue === mapping.prevFnValue) {
14773 continue;
14774 }
14775 }
14776
14777 var retDiffProp = retDiffProps[diffPropName] = {
14778 prev: eleProp
14779 };
14780 self.applyParsedProperty(ele, cxtProp);
14781 retDiffProp.next = ele.pstyle(diffPropName);
14782
14783 if (retDiffProp.next && retDiffProp.next.bypass) {
14784 retDiffProp.next = retDiffProp.next.bypassed;
14785 }
14786 }
14787
14788 return {
14789 diffProps: retDiffProps
14790 };
14791};
14792
14793styfn.updateStyleHints = function (ele) {
14794 var _p = ele._private;
14795 var self = this;
14796 var propNames = self.propertyGroupNames;
14797 var propGrKeys = self.propertyGroupKeys;
14798
14799 var propHash = function propHash(ele, propNames, seedKey) {
14800 return self.getPropertiesHash(ele, propNames, seedKey);
14801 };
14802
14803 var oldStyleKey = _p.styleKey;
14804
14805 if (ele.removed()) {
14806 return false;
14807 }
14808
14809 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14810 // but lazily -- only use non-default prop values to reduce the number of hashes
14811 //
14812
14813 var overriddenStyles = ele._private.style;
14814 propNames = Object.keys(overriddenStyles);
14815
14816 for (var i = 0; i < propGrKeys.length; i++) {
14817 var grKey = propGrKeys[i];
14818 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14819 }
14820
14821 var updateGrKey1 = function updateGrKey1(val, grKey) {
14822 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14823 };
14824
14825 var updateGrKey2 = function updateGrKey2(val, grKey) {
14826 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14827 };
14828
14829 var updateGrKey = function updateGrKey(val, grKey) {
14830 updateGrKey1(val, grKey);
14831 updateGrKey2(val, grKey);
14832 };
14833
14834 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14835 for (var j = 0; j < strVal.length; j++) {
14836 var ch = strVal.charCodeAt(j);
14837 updateGrKey1(ch, grKey);
14838 updateGrKey2(ch, grKey);
14839 }
14840 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14841 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14842 // - raise up small numbers so more significant digits are seen by hashing
14843 // - make small numbers larger than a normal value to avoid collisions
14844 // - works in practice and it's relatively cheap
14845
14846
14847 var N = 2000000000;
14848
14849 var cleanNum = function cleanNum(val) {
14850 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14851 };
14852
14853 for (var _i = 0; _i < propNames.length; _i++) {
14854 var name = propNames[_i];
14855 var parsedProp = overriddenStyles[name];
14856
14857 if (parsedProp == null) {
14858 continue;
14859 }
14860
14861 var propInfo = this.properties[name];
14862 var type = propInfo.type;
14863 var _grKey = propInfo.groupKey;
14864 var normalizedNumberVal = void 0;
14865
14866 if (propInfo.hashOverride != null) {
14867 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14868 } else if (parsedProp.pfValue != null) {
14869 normalizedNumberVal = parsedProp.pfValue;
14870 } // might not be a number if it allows enums
14871
14872
14873 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14874 var haveNormNum = normalizedNumberVal != null;
14875 var haveUnitedNum = numberVal != null;
14876 var haveNum = haveNormNum || haveUnitedNum;
14877 var units = parsedProp.units; // numbers are cheaper to hash than strings
14878 // 1 hash op vs n hash ops (for length n string)
14879
14880 if (type.number && haveNum && !type.multiple) {
14881 var v = haveNormNum ? normalizedNumberVal : numberVal;
14882 updateGrKey(cleanNum(v), _grKey);
14883
14884 if (!haveNormNum && units != null) {
14885 updateGrKeyWStr(units, _grKey);
14886 }
14887 } else {
14888 updateGrKeyWStr(parsedProp.strValue, _grKey);
14889 }
14890 } // overall style key
14891 //
14892
14893
14894 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14895
14896 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14897 var _grKey2 = propGrKeys[_i2];
14898 var grHash = _p.styleKeys[_grKey2];
14899 hash[0] = hashInt(grHash[0], hash[0]);
14900 hash[1] = hashIntAlt(grHash[1], hash[1]);
14901 }
14902
14903 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14904 //
14905
14906 var sk = _p.styleKeys;
14907 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14908 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14909 _p.labelKey = combineHashesArray(labelKeys);
14910 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14911
14912 if (!isNode) {
14913 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14914 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14915 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14916 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14917 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14918 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14919 } // node
14920 //
14921
14922
14923 if (isNode) {
14924 var _p$styleKeys = _p.styleKeys,
14925 nodeBody = _p$styleKeys.nodeBody,
14926 nodeBorder = _p$styleKeys.nodeBorder,
14927 backgroundImage = _p$styleKeys.backgroundImage,
14928 compound = _p$styleKeys.compound,
14929 pie = _p$styleKeys.pie;
14930 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14931 return k != null;
14932 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14933 _p.nodeKey = combineHashesArray(nodeKeys);
14934 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14935 }
14936
14937 return oldStyleKey !== _p.styleKey;
14938};
14939
14940styfn.clearStyleHints = function (ele) {
14941 var _p = ele._private;
14942 _p.styleCxtKey = '';
14943 _p.styleKeys = {};
14944 _p.styleKey = null;
14945 _p.labelKey = null;
14946 _p.labelStyleKey = null;
14947 _p.sourceLabelKey = null;
14948 _p.sourceLabelStyleKey = null;
14949 _p.targetLabelKey = null;
14950 _p.targetLabelStyleKey = null;
14951 _p.nodeKey = null;
14952 _p.hasPie = null;
14953}; // apply a property to the style (for internal use)
14954// returns whether application was successful
14955//
14956// now, this function flattens the property, and here's how:
14957//
14958// for parsedProp:{ bypass: true, deleteBypass: true }
14959// no property is generated, instead the bypass property in the
14960// element's style is replaced by what's pointed to by the `bypassed`
14961// field in the bypass property (i.e. restoring the property the
14962// bypass was overriding)
14963//
14964// for parsedProp:{ mapped: truthy }
14965// the generated flattenedProp:{ mapping: prop }
14966//
14967// for parsedProp:{ bypass: true }
14968// the generated flattenedProp:{ bypassed: parsedProp }
14969
14970
14971styfn.applyParsedProperty = function (ele, parsedProp) {
14972 var self = this;
14973 var prop = parsedProp;
14974 var style = ele._private.style;
14975 var flatProp;
14976 var types = self.types;
14977 var type = self.properties[prop.name].type;
14978 var propIsBypass = prop.bypass;
14979 var origProp = style[prop.name];
14980 var origPropIsBypass = origProp && origProp.bypass;
14981 var _p = ele._private;
14982 var flatPropMapping = 'mapping';
14983
14984 var getVal = function getVal(p) {
14985 if (p == null) {
14986 return null;
14987 } else if (p.pfValue != null) {
14988 return p.pfValue;
14989 } else {
14990 return p.value;
14991 }
14992 };
14993
14994 var checkTriggers = function checkTriggers() {
14995 var fromVal = getVal(origProp);
14996 var toVal = getVal(prop);
14997 self.checkTriggers(ele, prop.name, fromVal, toVal);
14998 };
14999
15000 if (prop && prop.name.substr(0, 3) === 'pie') {
15001 warn('The pie style properties are deprecated. Create charts using background images instead.');
15002 } // edge sanity checks to prevent the client from making serious mistakes
15003
15004
15005 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
15006 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
15007 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15008 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15009 }
15010
15011 if (prop["delete"]) {
15012 // delete the property and use the default value on falsey value
15013 style[prop.name] = undefined;
15014 checkTriggers();
15015 return true;
15016 }
15017
15018 if (prop.deleteBypassed) {
15019 // delete the property that the
15020 if (!origProp) {
15021 checkTriggers();
15022 return true; // can't delete if no prop
15023 } else if (origProp.bypass) {
15024 // delete bypassed
15025 origProp.bypassed = undefined;
15026 checkTriggers();
15027 return true;
15028 } else {
15029 return false; // we're unsuccessful deleting the bypassed
15030 }
15031 } // check if we need to delete the current bypass
15032
15033
15034 if (prop.deleteBypass) {
15035 // then this property is just here to indicate we need to delete
15036 if (!origProp) {
15037 checkTriggers();
15038 return true; // property is already not defined
15039 } else if (origProp.bypass) {
15040 // then replace the bypass property with the original
15041 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15042 style[prop.name] = origProp.bypassed;
15043 checkTriggers();
15044 return true;
15045 } else {
15046 return false; // we're unsuccessful deleting the bypass
15047 }
15048 }
15049
15050 var printMappingErr = function printMappingErr() {
15051 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');
15052 }; // put the property in the style objects
15053
15054
15055 switch (prop.mapped) {
15056 // flatten the property if mapped
15057 case types.mapData:
15058 {
15059 // flatten the field (e.g. data.foo.bar)
15060 var fields = prop.field.split('.');
15061 var fieldVal = _p.data;
15062
15063 for (var i = 0; i < fields.length && fieldVal; i++) {
15064 var field = fields[i];
15065 fieldVal = fieldVal[field];
15066 }
15067
15068 if (fieldVal == null) {
15069 printMappingErr();
15070 return false;
15071 }
15072
15073 var percent;
15074
15075 if (!number(fieldVal)) {
15076 // then don't apply and fall back on the existing style
15077 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15078 return false;
15079 } else {
15080 var fieldWidth = prop.fieldMax - prop.fieldMin;
15081
15082 if (fieldWidth === 0) {
15083 // safety check -- not strictly necessary as no props of zero range should be passed here
15084 percent = 0;
15085 } else {
15086 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15087 }
15088 } // make sure to bound percent value
15089
15090
15091 if (percent < 0) {
15092 percent = 0;
15093 } else if (percent > 1) {
15094 percent = 1;
15095 }
15096
15097 if (type.color) {
15098 var r1 = prop.valueMin[0];
15099 var r2 = prop.valueMax[0];
15100 var g1 = prop.valueMin[1];
15101 var g2 = prop.valueMax[1];
15102 var b1 = prop.valueMin[2];
15103 var b2 = prop.valueMax[2];
15104 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15105 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15106 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)];
15107 flatProp = {
15108 // colours are simple, so just create the flat property instead of expensive string parsing
15109 bypass: prop.bypass,
15110 // we're a bypass if the mapping property is a bypass
15111 name: prop.name,
15112 value: clr,
15113 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15114 };
15115 } else if (type.number) {
15116 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15117 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15118 } else {
15119 return false; // can only map to colours and numbers
15120 }
15121
15122 if (!flatProp) {
15123 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15124 printMappingErr();
15125 return false;
15126 }
15127
15128 flatProp.mapping = prop; // keep a reference to the mapping
15129
15130 prop = flatProp; // the flattened (mapped) property is the one we want
15131
15132 break;
15133 }
15134 // direct mapping
15135
15136 case types.data:
15137 {
15138 // flatten the field (e.g. data.foo.bar)
15139 var _fields = prop.field.split('.');
15140
15141 var _fieldVal = _p.data;
15142
15143 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15144 var _field = _fields[_i3];
15145 _fieldVal = _fieldVal[_field];
15146 }
15147
15148 if (_fieldVal != null) {
15149 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15150 }
15151
15152 if (!flatProp) {
15153 // if we can't flatten the property, then don't apply and fall back on the existing style
15154 printMappingErr();
15155 return false;
15156 }
15157
15158 flatProp.mapping = prop; // keep a reference to the mapping
15159
15160 prop = flatProp; // the flattened (mapped) property is the one we want
15161
15162 break;
15163 }
15164
15165 case types.fn:
15166 {
15167 var fn = prop.value;
15168 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15169
15170 prop.prevFnValue = fnRetVal;
15171
15172 if (fnRetVal == null) {
15173 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15174 return false;
15175 }
15176
15177 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15178
15179 if (!flatProp) {
15180 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15181 return false;
15182 }
15183
15184 flatProp.mapping = copy(prop); // keep a reference to the mapping
15185
15186 prop = flatProp; // the flattened (mapped) property is the one we want
15187
15188 break;
15189 }
15190
15191 case undefined:
15192 break;
15193 // just set the property
15194
15195 default:
15196 return false;
15197 // not a valid mapping
15198 } // if the property is a bypass property, then link the resultant property to the original one
15199
15200
15201 if (propIsBypass) {
15202 if (origPropIsBypass) {
15203 // then this bypass overrides the existing one
15204 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15205 } else {
15206 // then link the orig prop to the new bypass
15207 prop.bypassed = origProp;
15208 }
15209
15210 style[prop.name] = prop; // and set
15211 } else {
15212 // prop is not bypass
15213 if (origPropIsBypass) {
15214 // then keep the orig prop (since it's a bypass) and link to the new prop
15215 origProp.bypassed = prop;
15216 } else {
15217 // then just replace the old prop with the new one
15218 style[prop.name] = prop;
15219 }
15220 }
15221
15222 checkTriggers();
15223 return true;
15224};
15225
15226styfn.cleanElements = function (eles, keepBypasses) {
15227 for (var i = 0; i < eles.length; i++) {
15228 var ele = eles[i];
15229 this.clearStyleHints(ele);
15230 ele.dirtyCompoundBoundsCache();
15231 ele.dirtyBoundingBoxCache();
15232
15233 if (!keepBypasses) {
15234 ele._private.style = {};
15235 } else {
15236 var style = ele._private.style;
15237 var propNames = Object.keys(style);
15238
15239 for (var j = 0; j < propNames.length; j++) {
15240 var propName = propNames[j];
15241 var eleProp = style[propName];
15242
15243 if (eleProp != null) {
15244 if (eleProp.bypass) {
15245 eleProp.bypassed = null;
15246 } else {
15247 style[propName] = null;
15248 }
15249 }
15250 }
15251 }
15252 }
15253}; // updates the visual style for all elements (useful for manual style modification after init)
15254
15255
15256styfn.update = function () {
15257 var cy = this._private.cy;
15258 var eles = cy.mutableElements();
15259 eles.updateStyle();
15260}; // diffProps : { name => { prev, next } }
15261
15262
15263styfn.updateTransitions = function (ele, diffProps) {
15264 var self = this;
15265 var _p = ele._private;
15266 var props = ele.pstyle('transition-property').value;
15267 var duration = ele.pstyle('transition-duration').pfValue;
15268 var delay = ele.pstyle('transition-delay').pfValue;
15269
15270 if (props.length > 0 && duration > 0) {
15271 var style = {}; // build up the style to animate towards
15272
15273 var anyPrev = false;
15274
15275 for (var i = 0; i < props.length; i++) {
15276 var prop = props[i];
15277 var styProp = ele.pstyle(prop);
15278 var diffProp = diffProps[prop];
15279
15280 if (!diffProp) {
15281 continue;
15282 }
15283
15284 var prevProp = diffProp.prev;
15285 var fromProp = prevProp;
15286 var toProp = diffProp.next != null ? diffProp.next : styProp;
15287 var diff = false;
15288 var initVal = void 0;
15289 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15290
15291 if (!fromProp) {
15292 continue;
15293 } // consider px values
15294
15295
15296 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15297 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15298
15299 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15300 } else if (number(fromProp.value) && number(toProp.value)) {
15301 diff = toProp.value - fromProp.value; // nonzero is truthy
15302
15303 initVal = fromProp.value + initDt * diff; // consider colour values
15304 } else if (array(fromProp.value) && array(toProp.value)) {
15305 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15306 initVal = fromProp.strValue;
15307 } // the previous value is good for an animation only if it's different
15308
15309
15310 if (diff) {
15311 style[prop] = toProp.strValue; // to val
15312
15313 this.applyBypass(ele, prop, initVal); // from val
15314
15315 anyPrev = true;
15316 }
15317 } // end if props allow ani
15318 // can't transition if there's nothing previous to transition from
15319
15320
15321 if (!anyPrev) {
15322 return;
15323 }
15324
15325 _p.transitioning = true;
15326 new Promise$1(function (resolve) {
15327 if (delay > 0) {
15328 ele.delayAnimation(delay).play().promise().then(resolve);
15329 } else {
15330 resolve();
15331 }
15332 }).then(function () {
15333 return ele.animation({
15334 style: style,
15335 duration: duration,
15336 easing: ele.pstyle('transition-timing-function').value,
15337 queue: false
15338 }).play().promise();
15339 }).then(function () {
15340 // if( !isBypass ){
15341 self.removeBypasses(ele, props);
15342 ele.emitAndNotify('style'); // }
15343
15344 _p.transitioning = false;
15345 });
15346 } else if (_p.transitioning) {
15347 this.removeBypasses(ele, props);
15348 ele.emitAndNotify('style');
15349 _p.transitioning = false;
15350 }
15351};
15352
15353styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15354 var prop = this.properties[name];
15355 var triggerCheck = getTrigger(prop);
15356
15357 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15358 onTrigger(prop);
15359 }
15360};
15361
15362styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15363 var _this = this;
15364
15365 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15366 return prop.triggersZOrder;
15367 }, function () {
15368 _this._private.cy.notify('zorder', ele);
15369 });
15370};
15371
15372styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15373 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15374 return prop.triggersBounds;
15375 }, function (prop) {
15376 ele.dirtyCompoundBoundsCache();
15377 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15378 // then dirty the pll edge bb cache as well
15379
15380 if ( // only for beziers -- so performance of other edges isn't affected
15381 prop.triggersBoundsOfParallelBeziers && (name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') || name === 'display' && (fromValue === 'none' || toValue === 'none'))) {
15382 ele.parallelEdges().forEach(function (pllEdge) {
15383 if (pllEdge.isBundledBezier()) {
15384 pllEdge.dirtyBoundingBoxCache();
15385 }
15386 });
15387 }
15388 });
15389};
15390
15391styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15392 ele.dirtyStyleCache();
15393 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15394 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15395};
15396
15397var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15398// returns true iff application was successful for at least 1 specified property
15399
15400styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15401 var self = this;
15402 var props = [];
15403 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15404
15405 if (name === '*' || name === '**') {
15406 // apply to all property names
15407 if (value !== undefined) {
15408 for (var i = 0; i < self.properties.length; i++) {
15409 var prop = self.properties[i];
15410 var _name = prop.name;
15411 var parsedProp = this.parse(_name, value, true);
15412
15413 if (parsedProp) {
15414 props.push(parsedProp);
15415 }
15416 }
15417 }
15418 } else if (string(name)) {
15419 // then parse the single property
15420 var _parsedProp = this.parse(name, value, true);
15421
15422 if (_parsedProp) {
15423 props.push(_parsedProp);
15424 }
15425 } else if (plainObject(name)) {
15426 // then parse each property
15427 var specifiedProps = name;
15428 updateTransitions = value;
15429 var names = Object.keys(specifiedProps);
15430
15431 for (var _i = 0; _i < names.length; _i++) {
15432 var _name2 = names[_i];
15433 var _value = specifiedProps[_name2];
15434
15435 if (_value === undefined) {
15436 // try camel case name too
15437 _value = specifiedProps[dash2camel(_name2)];
15438 }
15439
15440 if (_value !== undefined) {
15441 var _parsedProp2 = this.parse(_name2, _value, true);
15442
15443 if (_parsedProp2) {
15444 props.push(_parsedProp2);
15445 }
15446 }
15447 }
15448 } else {
15449 // can't do anything without well defined properties
15450 return false;
15451 } // we've failed if there are no valid properties
15452
15453
15454 if (props.length === 0) {
15455 return false;
15456 } // now, apply the bypass properties on the elements
15457
15458
15459 var ret = false; // return true if at least one succesful bypass applied
15460
15461 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15462 // for each ele
15463 var ele = eles[_i2];
15464 var diffProps = {};
15465 var diffProp = void 0;
15466
15467 for (var j = 0; j < props.length; j++) {
15468 // for each prop
15469 var _prop = props[j];
15470
15471 if (updateTransitions) {
15472 var prevProp = ele.pstyle(_prop.name);
15473 diffProp = diffProps[_prop.name] = {
15474 prev: prevProp
15475 };
15476 }
15477
15478 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15479
15480 if (updateTransitions) {
15481 diffProp.next = ele.pstyle(_prop.name);
15482 }
15483 } // for props
15484
15485
15486 if (ret) {
15487 this.updateStyleHints(ele);
15488 }
15489
15490 if (updateTransitions) {
15491 this.updateTransitions(ele, diffProps, isBypass);
15492 }
15493 } // for eles
15494
15495
15496 return ret;
15497}; // only useful in specific cases like animation
15498
15499
15500styfn$1.overrideBypass = function (eles, name, value) {
15501 name = camel2dash(name);
15502
15503 for (var i = 0; i < eles.length; i++) {
15504 var ele = eles[i];
15505 var prop = ele._private.style[name];
15506 var type = this.properties[name].type;
15507 var isColor = type.color;
15508 var isMulti = type.mutiple;
15509 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15510
15511 if (!prop || !prop.bypass) {
15512 // need a bypass if one doesn't exist
15513 this.applyBypass(ele, name, value);
15514 } else {
15515 prop.value = value;
15516
15517 if (prop.pfValue != null) {
15518 prop.pfValue = value;
15519 }
15520
15521 if (isColor) {
15522 prop.strValue = 'rgb(' + value.join(',') + ')';
15523 } else if (isMulti) {
15524 prop.strValue = value.join(' ');
15525 } else {
15526 prop.strValue = '' + value;
15527 }
15528
15529 this.updateStyleHints(ele);
15530 }
15531
15532 this.checkTriggers(ele, name, oldValue, value);
15533 }
15534};
15535
15536styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15537 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15538};
15539
15540styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15541 var isBypass = true;
15542
15543 for (var j = 0; j < eles.length; j++) {
15544 var ele = eles[j];
15545 var diffProps = {};
15546
15547 for (var i = 0; i < props.length; i++) {
15548 var name = props[i];
15549 var prop = this.properties[name];
15550 var prevProp = ele.pstyle(prop.name);
15551
15552 if (!prevProp || !prevProp.bypass) {
15553 // if a bypass doesn't exist for the prop, nothing needs to be removed
15554 continue;
15555 }
15556
15557 var value = ''; // empty => remove bypass
15558
15559 var parsedProp = this.parse(name, value, true);
15560 var diffProp = diffProps[prop.name] = {
15561 prev: prevProp
15562 };
15563 this.applyParsedProperty(ele, parsedProp);
15564 diffProp.next = ele.pstyle(prop.name);
15565 } // for props
15566
15567
15568 this.updateStyleHints(ele);
15569
15570 if (updateTransitions) {
15571 this.updateTransitions(ele, diffProps, isBypass);
15572 }
15573 } // for eles
15574
15575};
15576
15577var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15578
15579styfn$2.getEmSizeInPixels = function () {
15580 var px = this.containerCss('font-size');
15581
15582 if (px != null) {
15583 return parseFloat(px);
15584 } else {
15585 return 1; // for headless
15586 }
15587}; // gets css property from the core container
15588
15589
15590styfn$2.containerCss = function (propName) {
15591 var cy = this._private.cy;
15592 var domElement = cy.container();
15593
15594 if (window$1 && domElement && window$1.getComputedStyle) {
15595 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15596 }
15597};
15598
15599var styfn$3 = {}; // gets the rendered style for an element
15600
15601styfn$3.getRenderedStyle = function (ele, prop) {
15602 if (prop) {
15603 return this.getStylePropertyValue(ele, prop, true);
15604 } else {
15605 return this.getRawStyle(ele, true);
15606 }
15607}; // gets the raw style for an element
15608
15609
15610styfn$3.getRawStyle = function (ele, isRenderedVal) {
15611 var self = this;
15612 ele = ele[0]; // insure it's an element
15613
15614 if (ele) {
15615 var rstyle = {};
15616
15617 for (var i = 0; i < self.properties.length; i++) {
15618 var prop = self.properties[i];
15619 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15620
15621 if (val != null) {
15622 rstyle[prop.name] = val;
15623 rstyle[dash2camel(prop.name)] = val;
15624 }
15625 }
15626
15627 return rstyle;
15628 }
15629};
15630
15631styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15632 var pstyle = ele.pstyle(property)[subproperty][index];
15633 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15634};
15635
15636styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15637 var self = this;
15638 ele = ele[0]; // insure it's an element
15639
15640 if (ele) {
15641 var prop = self.properties[propName];
15642
15643 if (prop.alias) {
15644 prop = prop.pointsTo;
15645 }
15646
15647 var type = prop.type;
15648 var styleProp = ele.pstyle(prop.name);
15649
15650 if (styleProp) {
15651 var value = styleProp.value,
15652 units = styleProp.units,
15653 strValue = styleProp.strValue;
15654
15655 if (isRenderedVal && type.number && value != null && number(value)) {
15656 var zoom = ele.cy().zoom();
15657
15658 var getRenderedValue = function getRenderedValue(val) {
15659 return val * zoom;
15660 };
15661
15662 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15663 return getRenderedValue(val) + units;
15664 };
15665
15666 var isArrayValue = array(value);
15667 var haveUnits = isArrayValue ? units.every(function (u) {
15668 return u != null;
15669 }) : units != null;
15670
15671 if (haveUnits) {
15672 if (isArrayValue) {
15673 return value.map(function (v, i) {
15674 return getValueStringWithUnits(v, units[i]);
15675 }).join(' ');
15676 } else {
15677 return getValueStringWithUnits(value, units);
15678 }
15679 } else {
15680 if (isArrayValue) {
15681 return value.map(function (v) {
15682 return string(v) ? v : '' + getRenderedValue(v);
15683 }).join(' ');
15684 } else {
15685 return '' + getRenderedValue(value);
15686 }
15687 }
15688 } else if (strValue != null) {
15689 return strValue;
15690 }
15691 }
15692
15693 return null;
15694 }
15695};
15696
15697styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15698 var rstyle = {};
15699
15700 for (var i = 0; i < aniProps.length; i++) {
15701 var aniProp = aniProps[i];
15702 var name = aniProp.name;
15703 var styleProp = ele.pstyle(name);
15704
15705 if (styleProp !== undefined) {
15706 // then make a prop of it
15707 if (plainObject(styleProp)) {
15708 styleProp = this.parse(name, styleProp.strValue);
15709 } else {
15710 styleProp = this.parse(name, styleProp);
15711 }
15712 }
15713
15714 if (styleProp) {
15715 rstyle[name] = styleProp;
15716 }
15717 }
15718
15719 return rstyle;
15720};
15721
15722styfn$3.getPropsList = function (propsObj) {
15723 var self = this;
15724 var rstyle = [];
15725 var style = propsObj;
15726 var props = self.properties;
15727
15728 if (style) {
15729 var names = Object.keys(style);
15730
15731 for (var i = 0; i < names.length; i++) {
15732 var name = names[i];
15733 var val = style[name];
15734 var prop = props[name] || props[camel2dash(name)];
15735 var styleProp = this.parse(prop.name, val);
15736
15737 if (styleProp) {
15738 rstyle.push(styleProp);
15739 }
15740 }
15741 }
15742
15743 return rstyle;
15744};
15745
15746styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15747 var hash = seed.slice();
15748 var name, val, strVal, chVal;
15749 var i, j;
15750
15751 for (i = 0; i < propNames.length; i++) {
15752 name = propNames[i];
15753 val = ele.pstyle(name, false);
15754
15755 if (val == null) {
15756 continue;
15757 } else if (val.pfValue != null) {
15758 hash[0] = hashInt(chVal, hash[0]);
15759 hash[1] = hashIntAlt(chVal, hash[1]);
15760 } else {
15761 strVal = val.strValue;
15762
15763 for (j = 0; j < strVal.length; j++) {
15764 chVal = strVal.charCodeAt(j);
15765 hash[0] = hashInt(chVal, hash[0]);
15766 hash[1] = hashIntAlt(chVal, hash[1]);
15767 }
15768 }
15769 }
15770
15771 return hash;
15772};
15773
15774styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15775
15776var styfn$4 = {};
15777
15778styfn$4.appendFromJson = function (json) {
15779 var style = this;
15780
15781 for (var i = 0; i < json.length; i++) {
15782 var context = json[i];
15783 var selector = context.selector;
15784 var props = context.style || context.css;
15785 var names = Object.keys(props);
15786 style.selector(selector); // apply selector
15787
15788 for (var j = 0; j < names.length; j++) {
15789 var name = names[j];
15790 var value = props[name];
15791 style.css(name, value); // apply property
15792 }
15793 }
15794
15795 return style;
15796}; // accessible cy.style() function
15797
15798
15799styfn$4.fromJson = function (json) {
15800 var style = this;
15801 style.resetToDefault();
15802 style.appendFromJson(json);
15803 return style;
15804}; // get json from cy.style() api
15805
15806
15807styfn$4.json = function () {
15808 var json = [];
15809
15810 for (var i = this.defaultLength; i < this.length; i++) {
15811 var cxt = this[i];
15812 var selector = cxt.selector;
15813 var props = cxt.properties;
15814 var css = {};
15815
15816 for (var j = 0; j < props.length; j++) {
15817 var prop = props[j];
15818 css[prop.name] = prop.strValue;
15819 }
15820
15821 json.push({
15822 selector: !selector ? 'core' : selector.toString(),
15823 style: css
15824 });
15825 }
15826
15827 return json;
15828};
15829
15830var styfn$5 = {};
15831
15832styfn$5.appendFromString = function (string) {
15833 var self = this;
15834 var style = this;
15835 var remaining = '' + string;
15836 var selAndBlockStr;
15837 var blockRem;
15838 var propAndValStr; // remove comments from the style string
15839
15840 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15841
15842 function removeSelAndBlockFromRemaining() {
15843 // remove the parsed selector and block from the remaining text to parse
15844 if (remaining.length > selAndBlockStr.length) {
15845 remaining = remaining.substr(selAndBlockStr.length);
15846 } else {
15847 remaining = '';
15848 }
15849 }
15850
15851 function removePropAndValFromRem() {
15852 // remove the parsed property and value from the remaining block text to parse
15853 if (blockRem.length > propAndValStr.length) {
15854 blockRem = blockRem.substr(propAndValStr.length);
15855 } else {
15856 blockRem = '';
15857 }
15858 }
15859
15860 for (;;) {
15861 var nothingLeftToParse = remaining.match(/^\s*$/);
15862
15863 if (nothingLeftToParse) {
15864 break;
15865 }
15866
15867 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15868
15869 if (!selAndBlock) {
15870 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15871 break;
15872 }
15873
15874 selAndBlockStr = selAndBlock[0]; // parse the selector
15875
15876 var selectorStr = selAndBlock[1];
15877
15878 if (selectorStr !== 'core') {
15879 var selector = new Selector(selectorStr);
15880
15881 if (selector.invalid) {
15882 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15883
15884 removeSelAndBlockFromRemaining();
15885 continue;
15886 }
15887 } // parse the block of properties and values
15888
15889
15890 var blockStr = selAndBlock[2];
15891 var invalidBlock = false;
15892 blockRem = blockStr;
15893 var props = [];
15894
15895 for (;;) {
15896 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15897
15898 if (_nothingLeftToParse) {
15899 break;
15900 }
15901
15902 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15903
15904 if (!propAndVal) {
15905 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15906 invalidBlock = true;
15907 break;
15908 }
15909
15910 propAndValStr = propAndVal[0];
15911 var propStr = propAndVal[1];
15912 var valStr = propAndVal[2];
15913 var prop = self.properties[propStr];
15914
15915 if (!prop) {
15916 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15917
15918 removePropAndValFromRem();
15919 continue;
15920 }
15921
15922 var parsedProp = style.parse(propStr, valStr);
15923
15924 if (!parsedProp) {
15925 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15926
15927 removePropAndValFromRem();
15928 continue;
15929 }
15930
15931 props.push({
15932 name: propStr,
15933 val: valStr
15934 });
15935 removePropAndValFromRem();
15936 }
15937
15938 if (invalidBlock) {
15939 removeSelAndBlockFromRemaining();
15940 break;
15941 } // put the parsed block in the style
15942
15943
15944 style.selector(selectorStr);
15945
15946 for (var i = 0; i < props.length; i++) {
15947 var _prop = props[i];
15948 style.css(_prop.name, _prop.val);
15949 }
15950
15951 removeSelAndBlockFromRemaining();
15952 }
15953
15954 return style;
15955};
15956
15957styfn$5.fromString = function (string) {
15958 var style = this;
15959 style.resetToDefault();
15960 style.appendFromString(string);
15961 return style;
15962};
15963
15964var styfn$6 = {};
15965
15966(function () {
15967 var number = number$1;
15968 var rgba = rgbaNoBackRefs;
15969 var hsla = hslaNoBackRefs;
15970 var hex3$1 = hex3;
15971 var hex6$1 = hex6;
15972
15973 var data = function data(prefix) {
15974 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15975 };
15976
15977 var mapData = function mapData(prefix) {
15978 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15979 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15980 };
15981
15982 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15983
15984 styfn$6.types = {
15985 time: {
15986 number: true,
15987 min: 0,
15988 units: 's|ms',
15989 implicitUnits: 'ms'
15990 },
15991 percent: {
15992 number: true,
15993 min: 0,
15994 max: 100,
15995 units: '%',
15996 implicitUnits: '%'
15997 },
15998 percentages: {
15999 number: true,
16000 min: 0,
16001 max: 100,
16002 units: '%',
16003 implicitUnits: '%',
16004 multiple: true
16005 },
16006 zeroOneNumber: {
16007 number: true,
16008 min: 0,
16009 max: 1,
16010 unitless: true
16011 },
16012 zeroOneNumbers: {
16013 number: true,
16014 min: 0,
16015 max: 1,
16016 unitless: true,
16017 multiple: true
16018 },
16019 nOneOneNumber: {
16020 number: true,
16021 min: -1,
16022 max: 1,
16023 unitless: true
16024 },
16025 nonNegativeInt: {
16026 number: true,
16027 min: 0,
16028 integer: true,
16029 unitless: true
16030 },
16031 position: {
16032 enums: ['parent', 'origin']
16033 },
16034 nodeSize: {
16035 number: true,
16036 min: 0,
16037 enums: ['label']
16038 },
16039 number: {
16040 number: true,
16041 unitless: true
16042 },
16043 numbers: {
16044 number: true,
16045 unitless: true,
16046 multiple: true
16047 },
16048 positiveNumber: {
16049 number: true,
16050 unitless: true,
16051 min: 0,
16052 strictMin: true
16053 },
16054 size: {
16055 number: true,
16056 min: 0
16057 },
16058 bidirectionalSize: {
16059 number: true
16060 },
16061 // allows negative
16062 bidirectionalSizeMaybePercent: {
16063 number: true,
16064 allowPercent: true
16065 },
16066 // allows negative
16067 bidirectionalSizes: {
16068 number: true,
16069 multiple: true
16070 },
16071 // allows negative
16072 sizeMaybePercent: {
16073 number: true,
16074 min: 0,
16075 allowPercent: true
16076 },
16077 axisDirection: {
16078 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16079 },
16080 paddingRelativeTo: {
16081 enums: ['width', 'height', 'average', 'min', 'max']
16082 },
16083 bgWH: {
16084 number: true,
16085 min: 0,
16086 allowPercent: true,
16087 enums: ['auto'],
16088 multiple: true
16089 },
16090 bgPos: {
16091 number: true,
16092 allowPercent: true,
16093 multiple: true
16094 },
16095 bgRelativeTo: {
16096 enums: ['inner', 'include-padding'],
16097 multiple: true
16098 },
16099 bgRepeat: {
16100 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16101 multiple: true
16102 },
16103 bgFit: {
16104 enums: ['none', 'contain', 'cover'],
16105 multiple: true
16106 },
16107 bgCrossOrigin: {
16108 enums: ['anonymous', 'use-credentials'],
16109 multiple: true
16110 },
16111 bgClip: {
16112 enums: ['none', 'node'],
16113 multiple: true
16114 },
16115 bgContainment: {
16116 enums: ['inside', 'over'],
16117 multiple: true
16118 },
16119 color: {
16120 color: true
16121 },
16122 colors: {
16123 color: true,
16124 multiple: true
16125 },
16126 fill: {
16127 enums: ['solid', 'linear-gradient', 'radial-gradient']
16128 },
16129 bool: {
16130 enums: ['yes', 'no']
16131 },
16132 bools: {
16133 enums: ['yes', 'no'],
16134 multiple: true
16135 },
16136 lineStyle: {
16137 enums: ['solid', 'dotted', 'dashed']
16138 },
16139 lineCap: {
16140 enums: ['butt', 'round', 'square']
16141 },
16142 borderStyle: {
16143 enums: ['solid', 'dotted', 'dashed', 'double']
16144 },
16145 curveStyle: {
16146 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'straight-triangle', 'taxi']
16147 },
16148 fontFamily: {
16149 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16150 },
16151 fontStyle: {
16152 enums: ['italic', 'normal', 'oblique']
16153 },
16154 fontWeight: {
16155 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16156 },
16157 textDecoration: {
16158 enums: ['none', 'underline', 'overline', 'line-through']
16159 },
16160 textTransform: {
16161 enums: ['none', 'uppercase', 'lowercase']
16162 },
16163 textWrap: {
16164 enums: ['none', 'wrap', 'ellipsis']
16165 },
16166 textOverflowWrap: {
16167 enums: ['whitespace', 'anywhere']
16168 },
16169 textBackgroundShape: {
16170 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16171 },
16172 nodeShape: {
16173 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']
16174 },
16175 overlayShape: {
16176 enums: ['roundrectangle', 'round-rectangle', 'ellipse']
16177 },
16178 compoundIncludeLabels: {
16179 enums: ['include', 'exclude']
16180 },
16181 arrowShape: {
16182 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16183 },
16184 arrowFill: {
16185 enums: ['filled', 'hollow']
16186 },
16187 display: {
16188 enums: ['element', 'none']
16189 },
16190 visibility: {
16191 enums: ['hidden', 'visible']
16192 },
16193 zCompoundDepth: {
16194 enums: ['bottom', 'orphan', 'auto', 'top']
16195 },
16196 zIndexCompare: {
16197 enums: ['auto', 'manual']
16198 },
16199 valign: {
16200 enums: ['top', 'center', 'bottom']
16201 },
16202 halign: {
16203 enums: ['left', 'center', 'right']
16204 },
16205 justification: {
16206 enums: ['left', 'center', 'right', 'auto']
16207 },
16208 text: {
16209 string: true
16210 },
16211 data: {
16212 mapping: true,
16213 regex: data('data')
16214 },
16215 layoutData: {
16216 mapping: true,
16217 regex: data('layoutData')
16218 },
16219 scratch: {
16220 mapping: true,
16221 regex: data('scratch')
16222 },
16223 mapData: {
16224 mapping: true,
16225 regex: mapData('mapData')
16226 },
16227 mapLayoutData: {
16228 mapping: true,
16229 regex: mapData('mapLayoutData')
16230 },
16231 mapScratch: {
16232 mapping: true,
16233 regex: mapData('mapScratch')
16234 },
16235 fn: {
16236 mapping: true,
16237 fn: true
16238 },
16239 url: {
16240 regexes: urlRegexes,
16241 singleRegexMatchValue: true
16242 },
16243 urls: {
16244 regexes: urlRegexes,
16245 singleRegexMatchValue: true,
16246 multiple: true
16247 },
16248 propList: {
16249 propList: true
16250 },
16251 angle: {
16252 number: true,
16253 units: 'deg|rad',
16254 implicitUnits: 'rad'
16255 },
16256 textRotation: {
16257 number: true,
16258 units: 'deg|rad',
16259 implicitUnits: 'rad',
16260 enums: ['none', 'autorotate']
16261 },
16262 polygonPointList: {
16263 number: true,
16264 multiple: true,
16265 evenMultiple: true,
16266 min: -1,
16267 max: 1,
16268 unitless: true
16269 },
16270 edgeDistances: {
16271 enums: ['intersection', 'node-position']
16272 },
16273 edgeEndpoint: {
16274 number: true,
16275 multiple: true,
16276 units: '%|px|em|deg|rad',
16277 implicitUnits: 'px',
16278 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16279 singleEnum: true,
16280 validate: function validate(valArr, unitsArr) {
16281 switch (valArr.length) {
16282 case 2:
16283 // can be % or px only
16284 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16285
16286 case 1:
16287 // can be enum, deg, or rad only
16288 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16289
16290 default:
16291 return false;
16292 }
16293 }
16294 },
16295 easing: {
16296 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16297 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']
16298 },
16299 gradientDirection: {
16300 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']
16301 },
16302 boundsExpansion: {
16303 number: true,
16304 multiple: true,
16305 min: 0,
16306 validate: function validate(valArr) {
16307 var length = valArr.length;
16308 return length === 1 || length === 2 || length === 4;
16309 }
16310 }
16311 };
16312 var diff = {
16313 zeroNonZero: function zeroNonZero(val1, val2) {
16314 if ((val1 == null || val2 == null) && val1 !== val2) {
16315 return true; // null cases could represent any value
16316 }
16317
16318 if (val1 == 0 && val2 != 0) {
16319 return true;
16320 } else if (val1 != 0 && val2 == 0) {
16321 return true;
16322 } else {
16323 return false;
16324 }
16325 },
16326 any: function any(val1, val2) {
16327 return val1 != val2;
16328 },
16329 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16330 var empty1 = emptyString(str1);
16331 var empty2 = emptyString(str2);
16332 return empty1 && !empty2 || !empty1 && empty2;
16333 }
16334 }; // define visual style properties
16335 //
16336 // - n.b. adding a new group of props may require updates to updateStyleHints()
16337 // - adding new props to an existing group gets handled automatically
16338
16339 var t = styfn$6.types;
16340 var mainLabel = [{
16341 name: 'label',
16342 type: t.text,
16343 triggersBounds: diff.any,
16344 triggersZOrder: diff.emptyNonEmpty
16345 }, {
16346 name: 'text-rotation',
16347 type: t.textRotation,
16348 triggersBounds: diff.any
16349 }, {
16350 name: 'text-margin-x',
16351 type: t.bidirectionalSize,
16352 triggersBounds: diff.any
16353 }, {
16354 name: 'text-margin-y',
16355 type: t.bidirectionalSize,
16356 triggersBounds: diff.any
16357 }];
16358 var sourceLabel = [{
16359 name: 'source-label',
16360 type: t.text,
16361 triggersBounds: diff.any
16362 }, {
16363 name: 'source-text-rotation',
16364 type: t.textRotation,
16365 triggersBounds: diff.any
16366 }, {
16367 name: 'source-text-margin-x',
16368 type: t.bidirectionalSize,
16369 triggersBounds: diff.any
16370 }, {
16371 name: 'source-text-margin-y',
16372 type: t.bidirectionalSize,
16373 triggersBounds: diff.any
16374 }, {
16375 name: 'source-text-offset',
16376 type: t.size,
16377 triggersBounds: diff.any
16378 }];
16379 var targetLabel = [{
16380 name: 'target-label',
16381 type: t.text,
16382 triggersBounds: diff.any
16383 }, {
16384 name: 'target-text-rotation',
16385 type: t.textRotation,
16386 triggersBounds: diff.any
16387 }, {
16388 name: 'target-text-margin-x',
16389 type: t.bidirectionalSize,
16390 triggersBounds: diff.any
16391 }, {
16392 name: 'target-text-margin-y',
16393 type: t.bidirectionalSize,
16394 triggersBounds: diff.any
16395 }, {
16396 name: 'target-text-offset',
16397 type: t.size,
16398 triggersBounds: diff.any
16399 }];
16400 var labelDimensions = [{
16401 name: 'font-family',
16402 type: t.fontFamily,
16403 triggersBounds: diff.any
16404 }, {
16405 name: 'font-style',
16406 type: t.fontStyle,
16407 triggersBounds: diff.any
16408 }, {
16409 name: 'font-weight',
16410 type: t.fontWeight,
16411 triggersBounds: diff.any
16412 }, {
16413 name: 'font-size',
16414 type: t.size,
16415 triggersBounds: diff.any
16416 }, {
16417 name: 'text-transform',
16418 type: t.textTransform,
16419 triggersBounds: diff.any
16420 }, {
16421 name: 'text-wrap',
16422 type: t.textWrap,
16423 triggersBounds: diff.any
16424 }, {
16425 name: 'text-overflow-wrap',
16426 type: t.textOverflowWrap,
16427 triggersBounds: diff.any
16428 }, {
16429 name: 'text-max-width',
16430 type: t.size,
16431 triggersBounds: diff.any
16432 }, {
16433 name: 'text-outline-width',
16434 type: t.size,
16435 triggersBounds: diff.any
16436 }, {
16437 name: 'line-height',
16438 type: t.positiveNumber,
16439 triggersBounds: diff.any
16440 }];
16441 var commonLabel = [{
16442 name: 'text-valign',
16443 type: t.valign,
16444 triggersBounds: diff.any
16445 }, {
16446 name: 'text-halign',
16447 type: t.halign,
16448 triggersBounds: diff.any
16449 }, {
16450 name: 'color',
16451 type: t.color
16452 }, {
16453 name: 'text-outline-color',
16454 type: t.color
16455 }, {
16456 name: 'text-outline-opacity',
16457 type: t.zeroOneNumber
16458 }, {
16459 name: 'text-background-color',
16460 type: t.color
16461 }, {
16462 name: 'text-background-opacity',
16463 type: t.zeroOneNumber
16464 }, {
16465 name: 'text-background-padding',
16466 type: t.size,
16467 triggersBounds: diff.any
16468 }, {
16469 name: 'text-border-opacity',
16470 type: t.zeroOneNumber
16471 }, {
16472 name: 'text-border-color',
16473 type: t.color
16474 }, {
16475 name: 'text-border-width',
16476 type: t.size,
16477 triggersBounds: diff.any
16478 }, {
16479 name: 'text-border-style',
16480 type: t.borderStyle,
16481 triggersBounds: diff.any
16482 }, {
16483 name: 'text-background-shape',
16484 type: t.textBackgroundShape,
16485 triggersBounds: diff.any
16486 }, {
16487 name: 'text-justification',
16488 type: t.justification
16489 }];
16490 var behavior = [{
16491 name: 'events',
16492 type: t.bool
16493 }, {
16494 name: 'text-events',
16495 type: t.bool
16496 }];
16497 var visibility = [{
16498 name: 'display',
16499 type: t.display,
16500 triggersZOrder: diff.any,
16501 triggersBounds: diff.any,
16502 triggersBoundsOfParallelBeziers: true
16503 }, {
16504 name: 'visibility',
16505 type: t.visibility,
16506 triggersZOrder: diff.any
16507 }, {
16508 name: 'opacity',
16509 type: t.zeroOneNumber,
16510 triggersZOrder: diff.zeroNonZero
16511 }, {
16512 name: 'text-opacity',
16513 type: t.zeroOneNumber
16514 }, {
16515 name: 'min-zoomed-font-size',
16516 type: t.size
16517 }, {
16518 name: 'z-compound-depth',
16519 type: t.zCompoundDepth,
16520 triggersZOrder: diff.any
16521 }, {
16522 name: 'z-index-compare',
16523 type: t.zIndexCompare,
16524 triggersZOrder: diff.any
16525 }, {
16526 name: 'z-index',
16527 type: t.nonNegativeInt,
16528 triggersZOrder: diff.any
16529 }];
16530 var overlay = [{
16531 name: 'overlay-padding',
16532 type: t.size,
16533 triggersBounds: diff.any
16534 }, {
16535 name: 'overlay-color',
16536 type: t.color
16537 }, {
16538 name: 'overlay-opacity',
16539 type: t.zeroOneNumber,
16540 triggersBounds: diff.zeroNonZero
16541 }, {
16542 name: 'overlay-shape',
16543 type: t.overlayShape,
16544 triggersBounds: diff.any
16545 }];
16546 var underlay = [{
16547 name: 'underlay-padding',
16548 type: t.size,
16549 triggersBounds: diff.any
16550 }, {
16551 name: 'underlay-color',
16552 type: t.color
16553 }, {
16554 name: 'underlay-opacity',
16555 type: t.zeroOneNumber,
16556 triggersBounds: diff.zeroNonZero
16557 }, {
16558 name: 'underlay-shape',
16559 type: t.overlayShape,
16560 triggersBounds: diff.any
16561 }];
16562 var transition = [{
16563 name: 'transition-property',
16564 type: t.propList
16565 }, {
16566 name: 'transition-duration',
16567 type: t.time
16568 }, {
16569 name: 'transition-delay',
16570 type: t.time
16571 }, {
16572 name: 'transition-timing-function',
16573 type: t.easing
16574 }];
16575
16576 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16577 if (parsedProp.value === 'label') {
16578 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16579 } else {
16580 return parsedProp.pfValue;
16581 }
16582 };
16583
16584 var nodeBody = [{
16585 name: 'height',
16586 type: t.nodeSize,
16587 triggersBounds: diff.any,
16588 hashOverride: nodeSizeHashOverride
16589 }, {
16590 name: 'width',
16591 type: t.nodeSize,
16592 triggersBounds: diff.any,
16593 hashOverride: nodeSizeHashOverride
16594 }, {
16595 name: 'shape',
16596 type: t.nodeShape,
16597 triggersBounds: diff.any
16598 }, {
16599 name: 'shape-polygon-points',
16600 type: t.polygonPointList,
16601 triggersBounds: diff.any
16602 }, {
16603 name: 'background-color',
16604 type: t.color
16605 }, {
16606 name: 'background-fill',
16607 type: t.fill
16608 }, {
16609 name: 'background-opacity',
16610 type: t.zeroOneNumber
16611 }, {
16612 name: 'background-blacken',
16613 type: t.nOneOneNumber
16614 }, {
16615 name: 'background-gradient-stop-colors',
16616 type: t.colors
16617 }, {
16618 name: 'background-gradient-stop-positions',
16619 type: t.percentages
16620 }, {
16621 name: 'background-gradient-direction',
16622 type: t.gradientDirection
16623 }, {
16624 name: 'padding',
16625 type: t.sizeMaybePercent,
16626 triggersBounds: diff.any
16627 }, {
16628 name: 'padding-relative-to',
16629 type: t.paddingRelativeTo,
16630 triggersBounds: diff.any
16631 }, {
16632 name: 'bounds-expansion',
16633 type: t.boundsExpansion,
16634 triggersBounds: diff.any
16635 }];
16636 var nodeBorder = [{
16637 name: 'border-color',
16638 type: t.color
16639 }, {
16640 name: 'border-opacity',
16641 type: t.zeroOneNumber
16642 }, {
16643 name: 'border-width',
16644 type: t.size,
16645 triggersBounds: diff.any
16646 }, {
16647 name: 'border-style',
16648 type: t.borderStyle
16649 }];
16650 var backgroundImage = [{
16651 name: 'background-image',
16652 type: t.urls
16653 }, {
16654 name: 'background-image-crossorigin',
16655 type: t.bgCrossOrigin
16656 }, {
16657 name: 'background-image-opacity',
16658 type: t.zeroOneNumbers
16659 }, {
16660 name: 'background-image-containment',
16661 type: t.bgContainment
16662 }, {
16663 name: 'background-image-smoothing',
16664 type: t.bools
16665 }, {
16666 name: 'background-position-x',
16667 type: t.bgPos
16668 }, {
16669 name: 'background-position-y',
16670 type: t.bgPos
16671 }, {
16672 name: 'background-width-relative-to',
16673 type: t.bgRelativeTo
16674 }, {
16675 name: 'background-height-relative-to',
16676 type: t.bgRelativeTo
16677 }, {
16678 name: 'background-repeat',
16679 type: t.bgRepeat
16680 }, {
16681 name: 'background-fit',
16682 type: t.bgFit
16683 }, {
16684 name: 'background-clip',
16685 type: t.bgClip
16686 }, {
16687 name: 'background-width',
16688 type: t.bgWH
16689 }, {
16690 name: 'background-height',
16691 type: t.bgWH
16692 }, {
16693 name: 'background-offset-x',
16694 type: t.bgPos
16695 }, {
16696 name: 'background-offset-y',
16697 type: t.bgPos
16698 }];
16699 var compound = [{
16700 name: 'position',
16701 type: t.position,
16702 triggersBounds: diff.any
16703 }, {
16704 name: 'compound-sizing-wrt-labels',
16705 type: t.compoundIncludeLabels,
16706 triggersBounds: diff.any
16707 }, {
16708 name: 'min-width',
16709 type: t.size,
16710 triggersBounds: diff.any
16711 }, {
16712 name: 'min-width-bias-left',
16713 type: t.sizeMaybePercent,
16714 triggersBounds: diff.any
16715 }, {
16716 name: 'min-width-bias-right',
16717 type: t.sizeMaybePercent,
16718 triggersBounds: diff.any
16719 }, {
16720 name: 'min-height',
16721 type: t.size,
16722 triggersBounds: diff.any
16723 }, {
16724 name: 'min-height-bias-top',
16725 type: t.sizeMaybePercent,
16726 triggersBounds: diff.any
16727 }, {
16728 name: 'min-height-bias-bottom',
16729 type: t.sizeMaybePercent,
16730 triggersBounds: diff.any
16731 }];
16732 var edgeLine = [{
16733 name: 'line-style',
16734 type: t.lineStyle
16735 }, {
16736 name: 'line-color',
16737 type: t.color
16738 }, {
16739 name: 'line-fill',
16740 type: t.fill
16741 }, {
16742 name: 'line-cap',
16743 type: t.lineCap
16744 }, {
16745 name: 'line-opacity',
16746 type: t.zeroOneNumber
16747 }, {
16748 name: 'line-dash-pattern',
16749 type: t.numbers
16750 }, {
16751 name: 'line-dash-offset',
16752 type: t.number
16753 }, {
16754 name: 'line-gradient-stop-colors',
16755 type: t.colors
16756 }, {
16757 name: 'line-gradient-stop-positions',
16758 type: t.percentages
16759 }, {
16760 name: 'curve-style',
16761 type: t.curveStyle,
16762 triggersBounds: diff.any,
16763 triggersBoundsOfParallelBeziers: true
16764 }, {
16765 name: 'haystack-radius',
16766 type: t.zeroOneNumber,
16767 triggersBounds: diff.any
16768 }, {
16769 name: 'source-endpoint',
16770 type: t.edgeEndpoint,
16771 triggersBounds: diff.any
16772 }, {
16773 name: 'target-endpoint',
16774 type: t.edgeEndpoint,
16775 triggersBounds: diff.any
16776 }, {
16777 name: 'control-point-step-size',
16778 type: t.size,
16779 triggersBounds: diff.any
16780 }, {
16781 name: 'control-point-distances',
16782 type: t.bidirectionalSizes,
16783 triggersBounds: diff.any
16784 }, {
16785 name: 'control-point-weights',
16786 type: t.numbers,
16787 triggersBounds: diff.any
16788 }, {
16789 name: 'segment-distances',
16790 type: t.bidirectionalSizes,
16791 triggersBounds: diff.any
16792 }, {
16793 name: 'segment-weights',
16794 type: t.numbers,
16795 triggersBounds: diff.any
16796 }, {
16797 name: 'taxi-turn',
16798 type: t.bidirectionalSizeMaybePercent,
16799 triggersBounds: diff.any
16800 }, {
16801 name: 'taxi-turn-min-distance',
16802 type: t.size,
16803 triggersBounds: diff.any
16804 }, {
16805 name: 'taxi-direction',
16806 type: t.axisDirection,
16807 triggersBounds: diff.any
16808 }, {
16809 name: 'edge-distances',
16810 type: t.edgeDistances,
16811 triggersBounds: diff.any
16812 }, {
16813 name: 'arrow-scale',
16814 type: t.positiveNumber,
16815 triggersBounds: diff.any
16816 }, {
16817 name: 'loop-direction',
16818 type: t.angle,
16819 triggersBounds: diff.any
16820 }, {
16821 name: 'loop-sweep',
16822 type: t.angle,
16823 triggersBounds: diff.any
16824 }, {
16825 name: 'source-distance-from-node',
16826 type: t.size,
16827 triggersBounds: diff.any
16828 }, {
16829 name: 'target-distance-from-node',
16830 type: t.size,
16831 triggersBounds: diff.any
16832 }];
16833 var ghost = [{
16834 name: 'ghost',
16835 type: t.bool,
16836 triggersBounds: diff.any
16837 }, {
16838 name: 'ghost-offset-x',
16839 type: t.bidirectionalSize,
16840 triggersBounds: diff.any
16841 }, {
16842 name: 'ghost-offset-y',
16843 type: t.bidirectionalSize,
16844 triggersBounds: diff.any
16845 }, {
16846 name: 'ghost-opacity',
16847 type: t.zeroOneNumber
16848 }];
16849 var core = [{
16850 name: 'selection-box-color',
16851 type: t.color
16852 }, {
16853 name: 'selection-box-opacity',
16854 type: t.zeroOneNumber
16855 }, {
16856 name: 'selection-box-border-color',
16857 type: t.color
16858 }, {
16859 name: 'selection-box-border-width',
16860 type: t.size
16861 }, {
16862 name: 'active-bg-color',
16863 type: t.color
16864 }, {
16865 name: 'active-bg-opacity',
16866 type: t.zeroOneNumber
16867 }, {
16868 name: 'active-bg-size',
16869 type: t.size
16870 }, {
16871 name: 'outside-texture-bg-color',
16872 type: t.color
16873 }, {
16874 name: 'outside-texture-bg-opacity',
16875 type: t.zeroOneNumber
16876 }]; // pie backgrounds for nodes
16877
16878 var pie = [];
16879 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16880
16881 pie.push({
16882 name: 'pie-size',
16883 type: t.sizeMaybePercent
16884 });
16885
16886 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16887 pie.push({
16888 name: 'pie-' + i + '-background-color',
16889 type: t.color
16890 });
16891 pie.push({
16892 name: 'pie-' + i + '-background-size',
16893 type: t.percent
16894 });
16895 pie.push({
16896 name: 'pie-' + i + '-background-opacity',
16897 type: t.zeroOneNumber
16898 });
16899 } // edge arrows
16900
16901
16902 var edgeArrow = [];
16903 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16904 [{
16905 name: 'arrow-shape',
16906 type: t.arrowShape,
16907 triggersBounds: diff.any
16908 }, {
16909 name: 'arrow-color',
16910 type: t.color
16911 }, {
16912 name: 'arrow-fill',
16913 type: t.arrowFill
16914 }].forEach(function (prop) {
16915 arrowPrefixes.forEach(function (prefix) {
16916 var name = prefix + '-' + prop.name;
16917 var type = prop.type,
16918 triggersBounds = prop.triggersBounds;
16919 edgeArrow.push({
16920 name: name,
16921 type: type,
16922 triggersBounds: triggersBounds
16923 });
16924 });
16925 }, {});
16926 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, underlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16927 var propGroups = styfn$6.propertyGroups = {
16928 // common to all eles
16929 behavior: behavior,
16930 transition: transition,
16931 visibility: visibility,
16932 overlay: overlay,
16933 underlay: underlay,
16934 ghost: ghost,
16935 // labels
16936 commonLabel: commonLabel,
16937 labelDimensions: labelDimensions,
16938 mainLabel: mainLabel,
16939 sourceLabel: sourceLabel,
16940 targetLabel: targetLabel,
16941 // node props
16942 nodeBody: nodeBody,
16943 nodeBorder: nodeBorder,
16944 backgroundImage: backgroundImage,
16945 pie: pie,
16946 compound: compound,
16947 // edge props
16948 edgeLine: edgeLine,
16949 edgeArrow: edgeArrow,
16950 core: core
16951 };
16952 var propGroupNames = styfn$6.propertyGroupNames = {};
16953 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16954 propGroupKeys.forEach(function (key) {
16955 propGroupNames[key] = propGroups[key].map(function (prop) {
16956 return prop.name;
16957 });
16958 propGroups[key].forEach(function (prop) {
16959 return prop.groupKey = key;
16960 });
16961 }); // define aliases
16962
16963 var aliases = styfn$6.aliases = [{
16964 name: 'content',
16965 pointsTo: 'label'
16966 }, {
16967 name: 'control-point-distance',
16968 pointsTo: 'control-point-distances'
16969 }, {
16970 name: 'control-point-weight',
16971 pointsTo: 'control-point-weights'
16972 }, {
16973 name: 'edge-text-rotation',
16974 pointsTo: 'text-rotation'
16975 }, {
16976 name: 'padding-left',
16977 pointsTo: 'padding'
16978 }, {
16979 name: 'padding-right',
16980 pointsTo: 'padding'
16981 }, {
16982 name: 'padding-top',
16983 pointsTo: 'padding'
16984 }, {
16985 name: 'padding-bottom',
16986 pointsTo: 'padding'
16987 }]; // list of property names
16988
16989 styfn$6.propertyNames = props.map(function (p) {
16990 return p.name;
16991 }); // allow access of properties by name ( e.g. style.properties.height )
16992
16993 for (var _i = 0; _i < props.length; _i++) {
16994 var prop = props[_i];
16995 props[prop.name] = prop; // allow lookup by name
16996 } // map aliases
16997
16998
16999 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17000 var alias = aliases[_i2];
17001 var pointsToProp = props[alias.pointsTo];
17002 var aliasProp = {
17003 name: alias.name,
17004 alias: true,
17005 pointsTo: pointsToProp
17006 }; // add alias prop for parsing
17007
17008 props.push(aliasProp);
17009 props[alias.name] = aliasProp; // allow lookup by name
17010 }
17011})();
17012
17013styfn$6.getDefaultProperty = function (name) {
17014 return this.getDefaultProperties()[name];
17015};
17016
17017styfn$6.getDefaultProperties = function () {
17018 var _p = this._private;
17019
17020 if (_p.defaultProperties != null) {
17021 return _p.defaultProperties;
17022 }
17023
17024 var rawProps = extend({
17025 // core props
17026 'selection-box-color': '#ddd',
17027 'selection-box-opacity': 0.65,
17028 'selection-box-border-color': '#aaa',
17029 'selection-box-border-width': 1,
17030 'active-bg-color': 'black',
17031 'active-bg-opacity': 0.15,
17032 'active-bg-size': 30,
17033 'outside-texture-bg-color': '#000',
17034 'outside-texture-bg-opacity': 0.125,
17035 // common node/edge props
17036 'events': 'yes',
17037 'text-events': 'no',
17038 'text-valign': 'top',
17039 'text-halign': 'center',
17040 'text-justification': 'auto',
17041 'line-height': 1,
17042 'color': '#000',
17043 'text-outline-color': '#000',
17044 'text-outline-width': 0,
17045 'text-outline-opacity': 1,
17046 'text-opacity': 1,
17047 'text-decoration': 'none',
17048 'text-transform': 'none',
17049 'text-wrap': 'none',
17050 'text-overflow-wrap': 'whitespace',
17051 'text-max-width': 9999,
17052 'text-background-color': '#000',
17053 'text-background-opacity': 0,
17054 'text-background-shape': 'rectangle',
17055 'text-background-padding': 0,
17056 'text-border-opacity': 0,
17057 'text-border-width': 0,
17058 'text-border-style': 'solid',
17059 'text-border-color': '#000',
17060 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17061 'font-style': 'normal',
17062 'font-weight': 'normal',
17063 'font-size': 16,
17064 'min-zoomed-font-size': 0,
17065 'text-rotation': 'none',
17066 'source-text-rotation': 'none',
17067 'target-text-rotation': 'none',
17068 'visibility': 'visible',
17069 'display': 'element',
17070 'opacity': 1,
17071 'z-compound-depth': 'auto',
17072 'z-index-compare': 'auto',
17073 'z-index': 0,
17074 'label': '',
17075 'text-margin-x': 0,
17076 'text-margin-y': 0,
17077 'source-label': '',
17078 'source-text-offset': 0,
17079 'source-text-margin-x': 0,
17080 'source-text-margin-y': 0,
17081 'target-label': '',
17082 'target-text-offset': 0,
17083 'target-text-margin-x': 0,
17084 'target-text-margin-y': 0,
17085 'overlay-opacity': 0,
17086 'overlay-color': '#000',
17087 'overlay-padding': 10,
17088 'overlay-shape': 'round-rectangle',
17089 'underlay-opacity': 0,
17090 'underlay-color': '#000',
17091 'underlay-padding': 10,
17092 'underlay-shape': 'round-rectangle',
17093 'transition-property': 'none',
17094 'transition-duration': 0,
17095 'transition-delay': 0,
17096 'transition-timing-function': 'linear',
17097 // node props
17098 'background-blacken': 0,
17099 'background-color': '#999',
17100 'background-fill': 'solid',
17101 'background-opacity': 1,
17102 'background-image': 'none',
17103 'background-image-crossorigin': 'anonymous',
17104 'background-image-opacity': 1,
17105 'background-image-containment': 'inside',
17106 'background-image-smoothing': 'yes',
17107 'background-position-x': '50%',
17108 'background-position-y': '50%',
17109 'background-offset-x': 0,
17110 'background-offset-y': 0,
17111 'background-width-relative-to': 'include-padding',
17112 'background-height-relative-to': 'include-padding',
17113 'background-repeat': 'no-repeat',
17114 'background-fit': 'none',
17115 'background-clip': 'node',
17116 'background-width': 'auto',
17117 'background-height': 'auto',
17118 'border-color': '#000',
17119 'border-opacity': 1,
17120 'border-width': 0,
17121 'border-style': 'solid',
17122 'height': 30,
17123 'width': 30,
17124 'shape': 'ellipse',
17125 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17126 'bounds-expansion': 0,
17127 // node gradient
17128 'background-gradient-direction': 'to-bottom',
17129 'background-gradient-stop-colors': '#999',
17130 'background-gradient-stop-positions': '0%',
17131 // ghost props
17132 'ghost': 'no',
17133 'ghost-offset-y': 0,
17134 'ghost-offset-x': 0,
17135 'ghost-opacity': 0,
17136 // compound props
17137 'padding': 0,
17138 'padding-relative-to': 'width',
17139 'position': 'origin',
17140 'compound-sizing-wrt-labels': 'include',
17141 'min-width': 0,
17142 'min-width-bias-left': 0,
17143 'min-width-bias-right': 0,
17144 'min-height': 0,
17145 'min-height-bias-top': 0,
17146 'min-height-bias-bottom': 0
17147 }, {
17148 // node pie bg
17149 'pie-size': '100%'
17150 }, [{
17151 name: 'pie-{{i}}-background-color',
17152 value: 'black'
17153 }, {
17154 name: 'pie-{{i}}-background-size',
17155 value: '0%'
17156 }, {
17157 name: 'pie-{{i}}-background-opacity',
17158 value: 1
17159 }].reduce(function (css, prop) {
17160 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17161 var name = prop.name.replace('{{i}}', i);
17162 var val = prop.value;
17163 css[name] = val;
17164 }
17165
17166 return css;
17167 }, {}), {
17168 // edge props
17169 'line-style': 'solid',
17170 'line-color': '#999',
17171 'line-fill': 'solid',
17172 'line-cap': 'butt',
17173 'line-opacity': 1,
17174 'line-gradient-stop-colors': '#999',
17175 'line-gradient-stop-positions': '0%',
17176 'control-point-step-size': 40,
17177 'control-point-weights': 0.5,
17178 'segment-weights': 0.5,
17179 'segment-distances': 20,
17180 'taxi-turn': '50%',
17181 'taxi-turn-min-distance': 10,
17182 'taxi-direction': 'auto',
17183 'edge-distances': 'intersection',
17184 'curve-style': 'haystack',
17185 'haystack-radius': 0,
17186 'arrow-scale': 1,
17187 'loop-direction': '-45deg',
17188 'loop-sweep': '-90deg',
17189 'source-distance-from-node': 0,
17190 'target-distance-from-node': 0,
17191 'source-endpoint': 'outside-to-node',
17192 'target-endpoint': 'outside-to-node',
17193 'line-dash-pattern': [6, 3],
17194 'line-dash-offset': 0
17195 }, [{
17196 name: 'arrow-shape',
17197 value: 'none'
17198 }, {
17199 name: 'arrow-color',
17200 value: '#999'
17201 }, {
17202 name: 'arrow-fill',
17203 value: 'filled'
17204 }].reduce(function (css, prop) {
17205 styfn$6.arrowPrefixes.forEach(function (prefix) {
17206 var name = prefix + '-' + prop.name;
17207 var val = prop.value;
17208 css[name] = val;
17209 });
17210 return css;
17211 }, {}));
17212 var parsedProps = {};
17213
17214 for (var i = 0; i < this.properties.length; i++) {
17215 var prop = this.properties[i];
17216
17217 if (prop.pointsTo) {
17218 continue;
17219 }
17220
17221 var name = prop.name;
17222 var val = rawProps[name];
17223 var parsedProp = this.parse(name, val);
17224 parsedProps[name] = parsedProp;
17225 }
17226
17227 _p.defaultProperties = parsedProps;
17228 return _p.defaultProperties;
17229};
17230
17231styfn$6.addDefaultStylesheet = function () {
17232 this.selector(':parent').css({
17233 'shape': 'rectangle',
17234 'padding': 10,
17235 'background-color': '#eee',
17236 'border-color': '#ccc',
17237 'border-width': 1
17238 }).selector('edge').css({
17239 'width': 3
17240 }).selector(':loop').css({
17241 'curve-style': 'bezier'
17242 }).selector('edge:compound').css({
17243 'curve-style': 'bezier',
17244 'source-endpoint': 'outside-to-line',
17245 'target-endpoint': 'outside-to-line'
17246 }).selector(':selected').css({
17247 'background-color': '#0169D9',
17248 'line-color': '#0169D9',
17249 'source-arrow-color': '#0169D9',
17250 'target-arrow-color': '#0169D9',
17251 'mid-source-arrow-color': '#0169D9',
17252 'mid-target-arrow-color': '#0169D9'
17253 }).selector(':parent:selected').css({
17254 'background-color': '#CCE1F9',
17255 'border-color': '#aec8e5'
17256 }).selector(':active').css({
17257 'overlay-color': 'black',
17258 'overlay-padding': 10,
17259 'overlay-opacity': 0.25
17260 });
17261 this.defaultLength = this.length;
17262};
17263
17264var styfn$7 = {}; // a caching layer for property parsing
17265
17266styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17267 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17268
17269 if (fn(value)) {
17270 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17271 }
17272
17273 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17274 var bypassKey = propIsBypass ? 't' : 'f';
17275 var valueKey = '' + value;
17276 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17277 var propCache = self.propCache = self.propCache || [];
17278 var ret;
17279
17280 if (!(ret = propCache[argHash])) {
17281 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17282 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17283 // - mappings can't be shared b/c mappings are per-element
17284
17285
17286 if (propIsBypass || propIsFlat === 'mapping') {
17287 // need a copy since props are mutated later in their lifecycles
17288 ret = copy(ret);
17289
17290 if (ret) {
17291 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17292 }
17293 }
17294
17295 return ret;
17296};
17297
17298styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17299 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17300
17301 if (!prop && value != null) {
17302 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17303 }
17304
17305 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17306 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17307 }
17308
17309 return prop;
17310}; // parse a property; return null on invalid; return parsed property otherwise
17311// fields :
17312// - name : the name of the property
17313// - value : the parsed, native-typed value of the property
17314// - strValue : a string value that represents the property value in valid css
17315// - bypass : true iff the property is a bypass property
17316
17317
17318styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17319 var self = this;
17320 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17321
17322 var property = self.properties[name];
17323 var passedValue = value;
17324 var types = self.types;
17325
17326 if (!property) {
17327 return null;
17328 } // return null on property of unknown name
17329
17330
17331 if (value === undefined) {
17332 return null;
17333 } // can't assign undefined
17334 // the property may be an alias
17335
17336
17337 if (property.alias) {
17338 property = property.pointsTo;
17339 name = property.name;
17340 }
17341
17342 var valueIsString = string(value);
17343
17344 if (valueIsString) {
17345 // trim the value to make parsing easier
17346 value = value.trim();
17347 }
17348
17349 var type = property.type;
17350
17351 if (!type) {
17352 return null;
17353 } // no type, no luck
17354 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17355
17356
17357 if (propIsBypass && (value === '' || value === null)) {
17358 return {
17359 name: name,
17360 value: value,
17361 bypass: true,
17362 deleteBypass: true
17363 };
17364 } // check if value is a function used as a mapper
17365
17366
17367 if (fn(value)) {
17368 return {
17369 name: name,
17370 value: value,
17371 strValue: 'fn',
17372 mapped: types.fn,
17373 bypass: propIsBypass
17374 };
17375 } // check if value is mapped
17376
17377
17378 var data, mapData;
17379
17380 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))) {
17381 if (propIsBypass) {
17382 return false;
17383 } // mappers not allowed in bypass
17384
17385
17386 var mapped = types.data;
17387 return {
17388 name: name,
17389 value: data,
17390 strValue: '' + value,
17391 mapped: mapped,
17392 field: data[1],
17393 bypass: propIsBypass
17394 };
17395 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17396 if (propIsBypass) {
17397 return false;
17398 } // mappers not allowed in bypass
17399
17400
17401 if (type.multiple) {
17402 return false;
17403 } // impossible to map to num
17404
17405
17406 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17407
17408 if (!(type.color || type.number)) {
17409 return false;
17410 }
17411
17412 var valueMin = this.parse(name, mapData[4]); // parse to validate
17413
17414 if (!valueMin || valueMin.mapped) {
17415 return false;
17416 } // can't be invalid or mapped
17417
17418
17419 var valueMax = this.parse(name, mapData[5]); // parse to validate
17420
17421 if (!valueMax || valueMax.mapped) {
17422 return false;
17423 } // can't be invalid or mapped
17424 // check if valueMin and valueMax are the same
17425
17426
17427 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17428 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17429 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17430 } else if (type.color) {
17431 var c1 = valueMin.value;
17432 var c2 = valueMax.value;
17433 var same = c1[0] === c2[0] // red
17434 && c1[1] === c2[1] // green
17435 && c1[2] === c2[2] // blue
17436 && ( // optional alpha
17437 c1[3] === c2[3] // same alpha outright
17438 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17439 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17440 );
17441
17442 if (same) {
17443 return false;
17444 } // can't make a mapper without a range
17445
17446 }
17447
17448 return {
17449 name: name,
17450 value: mapData,
17451 strValue: '' + value,
17452 mapped: _mapped,
17453 field: mapData[1],
17454 fieldMin: parseFloat(mapData[2]),
17455 // min & max are numeric
17456 fieldMax: parseFloat(mapData[3]),
17457 valueMin: valueMin.value,
17458 valueMax: valueMax.value,
17459 bypass: propIsBypass
17460 };
17461 }
17462
17463 if (type.multiple && propIsFlat !== 'multiple') {
17464 var vals;
17465
17466 if (valueIsString) {
17467 vals = value.split(/\s+/);
17468 } else if (array(value)) {
17469 vals = value;
17470 } else {
17471 vals = [value];
17472 }
17473
17474 if (type.evenMultiple && vals.length % 2 !== 0) {
17475 return null;
17476 }
17477
17478 var valArr = [];
17479 var unitsArr = [];
17480 var pfValArr = [];
17481 var strVal = '';
17482 var hasEnum = false;
17483
17484 for (var i = 0; i < vals.length; i++) {
17485 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17486 hasEnum = hasEnum || string(p.value);
17487 valArr.push(p.value);
17488 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17489 unitsArr.push(p.units);
17490 strVal += (i > 0 ? ' ' : '') + p.strValue;
17491 }
17492
17493 if (type.validate && !type.validate(valArr, unitsArr)) {
17494 return null;
17495 }
17496
17497 if (type.singleEnum && hasEnum) {
17498 if (valArr.length === 1 && string(valArr[0])) {
17499 return {
17500 name: name,
17501 value: valArr[0],
17502 strValue: valArr[0],
17503 bypass: propIsBypass
17504 };
17505 } else {
17506 return null;
17507 }
17508 }
17509
17510 return {
17511 name: name,
17512 value: valArr,
17513 pfValue: pfValArr,
17514 strValue: strVal,
17515 bypass: propIsBypass,
17516 units: unitsArr
17517 };
17518 } // several types also allow enums
17519
17520
17521 var checkEnums = function checkEnums() {
17522 for (var _i = 0; _i < type.enums.length; _i++) {
17523 var en = type.enums[_i];
17524
17525 if (en === value) {
17526 return {
17527 name: name,
17528 value: value,
17529 strValue: '' + value,
17530 bypass: propIsBypass
17531 };
17532 }
17533 }
17534
17535 return null;
17536 }; // check the type and return the appropriate object
17537
17538
17539 if (type.number) {
17540 var units;
17541 var implicitUnits = 'px'; // not set => px
17542
17543 if (type.units) {
17544 // use specified units if set
17545 units = type.units;
17546 }
17547
17548 if (type.implicitUnits) {
17549 implicitUnits = type.implicitUnits;
17550 }
17551
17552 if (!type.unitless) {
17553 if (valueIsString) {
17554 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17555
17556 if (units) {
17557 unitsRegex = units;
17558 } // only allow explicit units if so set
17559
17560
17561 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17562
17563 if (match) {
17564 value = match[1];
17565 units = match[2] || implicitUnits;
17566 }
17567 } else if (!units || type.implicitUnits) {
17568 units = implicitUnits; // implicitly px if unspecified
17569 }
17570 }
17571
17572 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17573
17574 if (isNaN(value) && type.enums === undefined) {
17575 return null;
17576 } // check if this number type also accepts special keywords in place of numbers
17577 // (i.e. `left`, `auto`, etc)
17578
17579
17580 if (isNaN(value) && type.enums !== undefined) {
17581 value = passedValue;
17582 return checkEnums();
17583 } // check if value must be an integer
17584
17585
17586 if (type.integer && !integer(value)) {
17587 return null;
17588 } // check value is within range
17589
17590
17591 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17592 return null;
17593 }
17594
17595 var ret = {
17596 name: name,
17597 value: value,
17598 strValue: '' + value + (units ? units : ''),
17599 units: units,
17600 bypass: propIsBypass
17601 }; // normalise value in pixels
17602
17603 if (type.unitless || units !== 'px' && units !== 'em') {
17604 ret.pfValue = value;
17605 } else {
17606 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17607 } // normalise value in ms
17608
17609
17610 if (units === 'ms' || units === 's') {
17611 ret.pfValue = units === 'ms' ? value : 1000 * value;
17612 } // normalise value in rad
17613
17614
17615 if (units === 'deg' || units === 'rad') {
17616 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17617 } // normalize value in %
17618
17619
17620 if (units === '%') {
17621 ret.pfValue = value / 100;
17622 }
17623
17624 return ret;
17625 } else if (type.propList) {
17626 var props = [];
17627 var propsStr = '' + value;
17628
17629 if (propsStr === 'none') ; else {
17630 // go over each prop
17631 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17632
17633 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17634 var propName = propsSplit[_i2].trim();
17635
17636 if (self.properties[propName]) {
17637 props.push(propName);
17638 } else {
17639 warn('`' + propName + '` is not a valid property name');
17640 }
17641 }
17642
17643 if (props.length === 0) {
17644 return null;
17645 }
17646 }
17647
17648 return {
17649 name: name,
17650 value: props,
17651 strValue: props.length === 0 ? 'none' : props.join(' '),
17652 bypass: propIsBypass
17653 };
17654 } else if (type.color) {
17655 var tuple = color2tuple(value);
17656
17657 if (!tuple) {
17658 return null;
17659 }
17660
17661 return {
17662 name: name,
17663 value: tuple,
17664 pfValue: tuple,
17665 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17666 // n.b. no spaces b/c of multiple support
17667 bypass: propIsBypass
17668 };
17669 } else if (type.regex || type.regexes) {
17670 // first check enums
17671 if (type.enums) {
17672 var enumProp = checkEnums();
17673
17674 if (enumProp) {
17675 return enumProp;
17676 }
17677 }
17678
17679 var regexes = type.regexes ? type.regexes : [type.regex];
17680
17681 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17682 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17683
17684 var m = regex.exec(value);
17685
17686 if (m) {
17687 // regex matches
17688 return {
17689 name: name,
17690 value: type.singleRegexMatchValue ? m[1] : m,
17691 strValue: '' + value,
17692 bypass: propIsBypass
17693 };
17694 }
17695 }
17696
17697 return null; // didn't match any
17698 } else if (type.string) {
17699 // just return
17700 return {
17701 name: name,
17702 value: '' + value,
17703 strValue: '' + value,
17704 bypass: propIsBypass
17705 };
17706 } else if (type.enums) {
17707 // check enums last because it's a combo type in others
17708 return checkEnums();
17709 } else {
17710 return null; // not a type we can handle
17711 }
17712};
17713
17714var Style = function Style(cy) {
17715 if (!(this instanceof Style)) {
17716 return new Style(cy);
17717 }
17718
17719 if (!core(cy)) {
17720 error('A style must have a core reference');
17721 return;
17722 }
17723
17724 this._private = {
17725 cy: cy,
17726 coreStyle: {}
17727 };
17728 this.length = 0;
17729 this.resetToDefault();
17730};
17731
17732var styfn$8 = Style.prototype;
17733
17734styfn$8.instanceString = function () {
17735 return 'style';
17736}; // remove all contexts
17737
17738
17739styfn$8.clear = function () {
17740 var _p = this._private;
17741 var cy = _p.cy;
17742 var eles = cy.elements();
17743
17744 for (var i = 0; i < this.length; i++) {
17745 this[i] = undefined;
17746 }
17747
17748 this.length = 0;
17749 _p.contextStyles = {};
17750 _p.propDiffs = {};
17751 this.cleanElements(eles, true);
17752 eles.forEach(function (ele) {
17753 var ele_p = ele[0]._private;
17754 ele_p.styleDirty = true;
17755 ele_p.appliedInitStyle = false;
17756 });
17757 return this; // chaining
17758};
17759
17760styfn$8.resetToDefault = function () {
17761 this.clear();
17762 this.addDefaultStylesheet();
17763 return this;
17764}; // builds a style object for the 'core' selector
17765
17766
17767styfn$8.core = function (propName) {
17768 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17769}; // create a new context from the specified selector string and switch to that context
17770
17771
17772styfn$8.selector = function (selectorStr) {
17773 // 'core' is a special case and does not need a selector
17774 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17775 var i = this.length++; // new context means new index
17776
17777 this[i] = {
17778 selector: selector,
17779 properties: [],
17780 mappedProperties: [],
17781 index: i
17782 };
17783 return this; // chaining
17784}; // add one or many css rules to the current context
17785
17786
17787styfn$8.css = function () {
17788 var self = this;
17789 var args = arguments;
17790
17791 if (args.length === 1) {
17792 var map = args[0];
17793
17794 for (var i = 0; i < self.properties.length; i++) {
17795 var prop = self.properties[i];
17796 var mapVal = map[prop.name];
17797
17798 if (mapVal === undefined) {
17799 mapVal = map[dash2camel(prop.name)];
17800 }
17801
17802 if (mapVal !== undefined) {
17803 this.cssRule(prop.name, mapVal);
17804 }
17805 }
17806 } else if (args.length === 2) {
17807 this.cssRule(args[0], args[1]);
17808 } // do nothing if args are invalid
17809
17810
17811 return this; // chaining
17812};
17813
17814styfn$8.style = styfn$8.css; // add a single css rule to the current context
17815
17816styfn$8.cssRule = function (name, value) {
17817 // name-value pair
17818 var property = this.parse(name, value); // add property to current context if valid
17819
17820 if (property) {
17821 var i = this.length - 1;
17822 this[i].properties.push(property);
17823 this[i].properties[property.name] = property; // allow access by name as well
17824
17825 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17826 this._private.hasPie = true;
17827 }
17828
17829 if (property.mapped) {
17830 this[i].mappedProperties.push(property);
17831 } // add to core style if necessary
17832
17833
17834 var currentSelectorIsCore = !this[i].selector;
17835
17836 if (currentSelectorIsCore) {
17837 this._private.coreStyle[property.name] = property;
17838 }
17839 }
17840
17841 return this; // chaining
17842};
17843
17844styfn$8.append = function (style) {
17845 if (stylesheet(style)) {
17846 style.appendToStyle(this);
17847 } else if (array(style)) {
17848 this.appendFromJson(style);
17849 } else if (string(style)) {
17850 this.appendFromString(style);
17851 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17852
17853
17854 return this;
17855}; // static function
17856
17857
17858Style.fromJson = function (cy, json) {
17859 var style = new Style(cy);
17860 style.fromJson(json);
17861 return style;
17862};
17863
17864Style.fromString = function (cy, string) {
17865 return new Style(cy).fromString(string);
17866};
17867
17868[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17869 extend(styfn$8, props);
17870});
17871Style.types = styfn$8.types;
17872Style.properties = styfn$8.properties;
17873Style.propertyGroups = styfn$8.propertyGroups;
17874Style.propertyGroupNames = styfn$8.propertyGroupNames;
17875Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17876
17877var corefn$7 = {
17878 style: function style(newStyle) {
17879 if (newStyle) {
17880 var s = this.setStyle(newStyle);
17881 s.update();
17882 }
17883
17884 return this._private.style;
17885 },
17886 setStyle: function setStyle(style) {
17887 var _p = this._private;
17888
17889 if (stylesheet(style)) {
17890 _p.style = style.generateStyle(this);
17891 } else if (array(style)) {
17892 _p.style = Style.fromJson(this, style);
17893 } else if (string(style)) {
17894 _p.style = Style.fromString(this, style);
17895 } else {
17896 _p.style = Style(this);
17897 }
17898
17899 return _p.style;
17900 },
17901 // e.g. cy.data() changed => recalc ele mappers
17902 updateStyle: function updateStyle() {
17903 this.mutableElements().updateStyle(); // just send to all eles
17904 }
17905};
17906
17907var defaultSelectionType = 'single';
17908var corefn$8 = {
17909 autolock: function autolock(bool) {
17910 if (bool !== undefined) {
17911 this._private.autolock = bool ? true : false;
17912 } else {
17913 return this._private.autolock;
17914 }
17915
17916 return this; // chaining
17917 },
17918 autoungrabify: function autoungrabify(bool) {
17919 if (bool !== undefined) {
17920 this._private.autoungrabify = bool ? true : false;
17921 } else {
17922 return this._private.autoungrabify;
17923 }
17924
17925 return this; // chaining
17926 },
17927 autounselectify: function autounselectify(bool) {
17928 if (bool !== undefined) {
17929 this._private.autounselectify = bool ? true : false;
17930 } else {
17931 return this._private.autounselectify;
17932 }
17933
17934 return this; // chaining
17935 },
17936 selectionType: function selectionType(selType) {
17937 var _p = this._private;
17938
17939 if (_p.selectionType == null) {
17940 _p.selectionType = defaultSelectionType;
17941 }
17942
17943 if (selType !== undefined) {
17944 if (selType === 'additive' || selType === 'single') {
17945 _p.selectionType = selType;
17946 }
17947 } else {
17948 return _p.selectionType;
17949 }
17950
17951 return this;
17952 },
17953 panningEnabled: function panningEnabled(bool) {
17954 if (bool !== undefined) {
17955 this._private.panningEnabled = bool ? true : false;
17956 } else {
17957 return this._private.panningEnabled;
17958 }
17959
17960 return this; // chaining
17961 },
17962 userPanningEnabled: function userPanningEnabled(bool) {
17963 if (bool !== undefined) {
17964 this._private.userPanningEnabled = bool ? true : false;
17965 } else {
17966 return this._private.userPanningEnabled;
17967 }
17968
17969 return this; // chaining
17970 },
17971 zoomingEnabled: function zoomingEnabled(bool) {
17972 if (bool !== undefined) {
17973 this._private.zoomingEnabled = bool ? true : false;
17974 } else {
17975 return this._private.zoomingEnabled;
17976 }
17977
17978 return this; // chaining
17979 },
17980 userZoomingEnabled: function userZoomingEnabled(bool) {
17981 if (bool !== undefined) {
17982 this._private.userZoomingEnabled = bool ? true : false;
17983 } else {
17984 return this._private.userZoomingEnabled;
17985 }
17986
17987 return this; // chaining
17988 },
17989 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17990 if (bool !== undefined) {
17991 this._private.boxSelectionEnabled = bool ? true : false;
17992 } else {
17993 return this._private.boxSelectionEnabled;
17994 }
17995
17996 return this; // chaining
17997 },
17998 pan: function pan() {
17999 var args = arguments;
18000 var pan = this._private.pan;
18001 var dim, val, dims, x, y;
18002
18003 switch (args.length) {
18004 case 0:
18005 // .pan()
18006 return pan;
18007
18008 case 1:
18009 if (string(args[0])) {
18010 // .pan('x')
18011 dim = args[0];
18012 return pan[dim];
18013 } else if (plainObject(args[0])) {
18014 // .pan({ x: 0, y: 100 })
18015 if (!this._private.panningEnabled) {
18016 return this;
18017 }
18018
18019 dims = args[0];
18020 x = dims.x;
18021 y = dims.y;
18022
18023 if (number(x)) {
18024 pan.x = x;
18025 }
18026
18027 if (number(y)) {
18028 pan.y = y;
18029 }
18030
18031 this.emit('pan viewport');
18032 }
18033
18034 break;
18035
18036 case 2:
18037 // .pan('x', 100)
18038 if (!this._private.panningEnabled) {
18039 return this;
18040 }
18041
18042 dim = args[0];
18043 val = args[1];
18044
18045 if ((dim === 'x' || dim === 'y') && number(val)) {
18046 pan[dim] = val;
18047 }
18048
18049 this.emit('pan viewport');
18050 break;
18051 // invalid
18052 }
18053
18054 this.notify('viewport');
18055 return this; // chaining
18056 },
18057 panBy: function panBy(arg0, arg1) {
18058 var args = arguments;
18059 var pan = this._private.pan;
18060 var dim, val, dims, x, y;
18061
18062 if (!this._private.panningEnabled) {
18063 return this;
18064 }
18065
18066 switch (args.length) {
18067 case 1:
18068 if (plainObject(arg0)) {
18069 // .panBy({ x: 0, y: 100 })
18070 dims = args[0];
18071 x = dims.x;
18072 y = dims.y;
18073
18074 if (number(x)) {
18075 pan.x += x;
18076 }
18077
18078 if (number(y)) {
18079 pan.y += y;
18080 }
18081
18082 this.emit('pan viewport');
18083 }
18084
18085 break;
18086
18087 case 2:
18088 // .panBy('x', 100)
18089 dim = arg0;
18090 val = arg1;
18091
18092 if ((dim === 'x' || dim === 'y') && number(val)) {
18093 pan[dim] += val;
18094 }
18095
18096 this.emit('pan viewport');
18097 break;
18098 // invalid
18099 }
18100
18101 this.notify('viewport');
18102 return this; // chaining
18103 },
18104 fit: function fit(elements, padding) {
18105 var viewportState = this.getFitViewport(elements, padding);
18106
18107 if (viewportState) {
18108 var _p = this._private;
18109 _p.zoom = viewportState.zoom;
18110 _p.pan = viewportState.pan;
18111 this.emit('pan zoom viewport');
18112 this.notify('viewport');
18113 }
18114
18115 return this; // chaining
18116 },
18117 getFitViewport: function getFitViewport(elements, padding) {
18118 if (number(elements) && padding === undefined) {
18119 // elements is optional
18120 padding = elements;
18121 elements = undefined;
18122 }
18123
18124 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18125 return;
18126 }
18127
18128 var bb;
18129
18130 if (string(elements)) {
18131 var sel = elements;
18132 elements = this.$(sel);
18133 } else if (boundingBox(elements)) {
18134 // assume bb
18135 var bbe = elements;
18136 bb = {
18137 x1: bbe.x1,
18138 y1: bbe.y1,
18139 x2: bbe.x2,
18140 y2: bbe.y2
18141 };
18142 bb.w = bb.x2 - bb.x1;
18143 bb.h = bb.y2 - bb.y1;
18144 } else if (!elementOrCollection(elements)) {
18145 elements = this.mutableElements();
18146 }
18147
18148 if (elementOrCollection(elements) && elements.empty()) {
18149 return;
18150 } // can't fit to nothing
18151
18152
18153 bb = bb || elements.boundingBox();
18154 var w = this.width();
18155 var h = this.height();
18156 var zoom;
18157 padding = number(padding) ? padding : 0;
18158
18159 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18160 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18161
18162 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18163 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18164 var pan = {
18165 // now pan to middle
18166 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18167 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18168 };
18169 return {
18170 zoom: zoom,
18171 pan: pan
18172 };
18173 }
18174
18175 return;
18176 },
18177 zoomRange: function zoomRange(min, max) {
18178 var _p = this._private;
18179
18180 if (max == null) {
18181 var opts = min;
18182 min = opts.min;
18183 max = opts.max;
18184 }
18185
18186 if (number(min) && number(max) && min <= max) {
18187 _p.minZoom = min;
18188 _p.maxZoom = max;
18189 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18190 _p.minZoom = min;
18191 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18192 _p.maxZoom = max;
18193 }
18194
18195 return this;
18196 },
18197 minZoom: function minZoom(zoom) {
18198 if (zoom === undefined) {
18199 return this._private.minZoom;
18200 } else {
18201 return this.zoomRange({
18202 min: zoom
18203 });
18204 }
18205 },
18206 maxZoom: function maxZoom(zoom) {
18207 if (zoom === undefined) {
18208 return this._private.maxZoom;
18209 } else {
18210 return this.zoomRange({
18211 max: zoom
18212 });
18213 }
18214 },
18215 getZoomedViewport: function getZoomedViewport(params) {
18216 var _p = this._private;
18217 var currentPan = _p.pan;
18218 var currentZoom = _p.zoom;
18219 var pos; // in rendered px
18220
18221 var zoom;
18222 var bail = false;
18223
18224 if (!_p.zoomingEnabled) {
18225 // zooming disabled
18226 bail = true;
18227 }
18228
18229 if (number(params)) {
18230 // then set the zoom
18231 zoom = params;
18232 } else if (plainObject(params)) {
18233 // then zoom about a point
18234 zoom = params.level;
18235
18236 if (params.position != null) {
18237 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18238 } else if (params.renderedPosition != null) {
18239 pos = params.renderedPosition;
18240 }
18241
18242 if (pos != null && !_p.panningEnabled) {
18243 // panning disabled
18244 bail = true;
18245 }
18246 } // crop zoom
18247
18248
18249 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18250 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18251
18252 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18253 return null;
18254 }
18255
18256 if (pos != null) {
18257 // set zoom about position
18258 var pan1 = currentPan;
18259 var zoom1 = currentZoom;
18260 var zoom2 = zoom;
18261 var pan2 = {
18262 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18263 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18264 };
18265 return {
18266 zoomed: true,
18267 panned: true,
18268 zoom: zoom2,
18269 pan: pan2
18270 };
18271 } else {
18272 // just set the zoom
18273 return {
18274 zoomed: true,
18275 panned: false,
18276 zoom: zoom,
18277 pan: currentPan
18278 };
18279 }
18280 },
18281 zoom: function zoom(params) {
18282 if (params === undefined) {
18283 // get
18284 return this._private.zoom;
18285 } else {
18286 // set
18287 var vp = this.getZoomedViewport(params);
18288 var _p = this._private;
18289
18290 if (vp == null || !vp.zoomed) {
18291 return this;
18292 }
18293
18294 _p.zoom = vp.zoom;
18295
18296 if (vp.panned) {
18297 _p.pan.x = vp.pan.x;
18298 _p.pan.y = vp.pan.y;
18299 }
18300
18301 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18302 this.notify('viewport');
18303 return this; // chaining
18304 }
18305 },
18306 viewport: function viewport(opts) {
18307 var _p = this._private;
18308 var zoomDefd = true;
18309 var panDefd = true;
18310 var events = []; // to trigger
18311
18312 var zoomFailed = false;
18313 var panFailed = false;
18314
18315 if (!opts) {
18316 return this;
18317 }
18318
18319 if (!number(opts.zoom)) {
18320 zoomDefd = false;
18321 }
18322
18323 if (!plainObject(opts.pan)) {
18324 panDefd = false;
18325 }
18326
18327 if (!zoomDefd && !panDefd) {
18328 return this;
18329 }
18330
18331 if (zoomDefd) {
18332 var z = opts.zoom;
18333
18334 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18335 zoomFailed = true;
18336 } else {
18337 _p.zoom = z;
18338 events.push('zoom');
18339 }
18340 }
18341
18342 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18343 var p = opts.pan;
18344
18345 if (number(p.x)) {
18346 _p.pan.x = p.x;
18347 panFailed = false;
18348 }
18349
18350 if (number(p.y)) {
18351 _p.pan.y = p.y;
18352 panFailed = false;
18353 }
18354
18355 if (!panFailed) {
18356 events.push('pan');
18357 }
18358 }
18359
18360 if (events.length > 0) {
18361 events.push('viewport');
18362 this.emit(events.join(' '));
18363 this.notify('viewport');
18364 }
18365
18366 return this; // chaining
18367 },
18368 center: function center(elements) {
18369 var pan = this.getCenterPan(elements);
18370
18371 if (pan) {
18372 this._private.pan = pan;
18373 this.emit('pan viewport');
18374 this.notify('viewport');
18375 }
18376
18377 return this; // chaining
18378 },
18379 getCenterPan: function getCenterPan(elements, zoom) {
18380 if (!this._private.panningEnabled) {
18381 return;
18382 }
18383
18384 if (string(elements)) {
18385 var selector = elements;
18386 elements = this.mutableElements().filter(selector);
18387 } else if (!elementOrCollection(elements)) {
18388 elements = this.mutableElements();
18389 }
18390
18391 if (elements.length === 0) {
18392 return;
18393 } // can't centre pan to nothing
18394
18395
18396 var bb = elements.boundingBox();
18397 var w = this.width();
18398 var h = this.height();
18399 zoom = zoom === undefined ? this._private.zoom : zoom;
18400 var pan = {
18401 // middle
18402 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18403 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18404 };
18405 return pan;
18406 },
18407 reset: function reset() {
18408 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18409 return this;
18410 }
18411
18412 this.viewport({
18413 pan: {
18414 x: 0,
18415 y: 0
18416 },
18417 zoom: 1
18418 });
18419 return this; // chaining
18420 },
18421 invalidateSize: function invalidateSize() {
18422 this._private.sizeCache = null;
18423 },
18424 size: function size() {
18425 var _p = this._private;
18426 var container = _p.container;
18427 return _p.sizeCache = _p.sizeCache || (container ? function () {
18428 var style = window$1.getComputedStyle(container);
18429
18430 var val = function val(name) {
18431 return parseFloat(style.getPropertyValue(name));
18432 };
18433
18434 return {
18435 width: container.clientWidth - val('padding-left') - val('padding-right'),
18436 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18437 };
18438 }() : {
18439 // fallback if no container (not 0 b/c can be used for dividing etc)
18440 width: 1,
18441 height: 1
18442 });
18443 },
18444 width: function width() {
18445 return this.size().width;
18446 },
18447 height: function height() {
18448 return this.size().height;
18449 },
18450 extent: function extent() {
18451 var pan = this._private.pan;
18452 var zoom = this._private.zoom;
18453 var rb = this.renderedExtent();
18454 var b = {
18455 x1: (rb.x1 - pan.x) / zoom,
18456 x2: (rb.x2 - pan.x) / zoom,
18457 y1: (rb.y1 - pan.y) / zoom,
18458 y2: (rb.y2 - pan.y) / zoom
18459 };
18460 b.w = b.x2 - b.x1;
18461 b.h = b.y2 - b.y1;
18462 return b;
18463 },
18464 renderedExtent: function renderedExtent() {
18465 var width = this.width();
18466 var height = this.height();
18467 return {
18468 x1: 0,
18469 y1: 0,
18470 x2: width,
18471 y2: height,
18472 w: width,
18473 h: height
18474 };
18475 },
18476 multiClickDebounceTime: function multiClickDebounceTime(_int) {
18477 if (_int) this._private.multiClickDebounceTime = _int;else return this._private.multiClickDebounceTime;
18478 return this; // chaining
18479 }
18480}; // aliases
18481
18482corefn$8.centre = corefn$8.center; // backwards compatibility
18483
18484corefn$8.autolockNodes = corefn$8.autolock;
18485corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18486
18487var fn$6 = {
18488 data: define$3.data({
18489 field: 'data',
18490 bindingEvent: 'data',
18491 allowBinding: true,
18492 allowSetting: true,
18493 settingEvent: 'data',
18494 settingTriggersEvent: true,
18495 triggerFnName: 'trigger',
18496 allowGetting: true,
18497 updateStyle: true
18498 }),
18499 removeData: define$3.removeData({
18500 field: 'data',
18501 event: 'data',
18502 triggerFnName: 'trigger',
18503 triggerEvent: true,
18504 updateStyle: true
18505 }),
18506 scratch: define$3.data({
18507 field: 'scratch',
18508 bindingEvent: 'scratch',
18509 allowBinding: true,
18510 allowSetting: true,
18511 settingEvent: 'scratch',
18512 settingTriggersEvent: true,
18513 triggerFnName: 'trigger',
18514 allowGetting: true,
18515 updateStyle: true
18516 }),
18517 removeScratch: define$3.removeData({
18518 field: 'scratch',
18519 event: 'scratch',
18520 triggerFnName: 'trigger',
18521 triggerEvent: true,
18522 updateStyle: true
18523 })
18524}; // aliases
18525
18526fn$6.attr = fn$6.data;
18527fn$6.removeAttr = fn$6.removeData;
18528
18529var Core = function Core(opts) {
18530 var cy = this;
18531 opts = extend({}, opts);
18532 var container = opts.container; // allow for passing a wrapped jquery object
18533 // e.g. cytoscape({ container: $('#cy') })
18534
18535 if (container && !htmlElement(container) && htmlElement(container[0])) {
18536 container = container[0];
18537 }
18538
18539 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18540
18541 reg = reg || {};
18542
18543 if (reg && reg.cy) {
18544 reg.cy.destroy();
18545 reg = {}; // old instance => replace reg completely
18546 }
18547
18548 var readies = reg.readies = reg.readies || [];
18549
18550 if (container) {
18551 container._cyreg = reg;
18552 } // make sure container assoc'd reg points to this cy
18553
18554
18555 reg.cy = cy;
18556 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18557 var options = opts;
18558 options.layout = extend({
18559 name: head ? 'grid' : 'null'
18560 }, options.layout);
18561 options.renderer = extend({
18562 name: head ? 'canvas' : 'null'
18563 }, options.renderer);
18564
18565 var defVal = function defVal(def, val, altVal) {
18566 if (val !== undefined) {
18567 return val;
18568 } else if (altVal !== undefined) {
18569 return altVal;
18570 } else {
18571 return def;
18572 }
18573 };
18574
18575 var _p = this._private = {
18576 container: container,
18577 // html dom ele container
18578 ready: false,
18579 // whether ready has been triggered
18580 options: options,
18581 // cached options
18582 elements: new Collection(this),
18583 // elements in the graph
18584 listeners: [],
18585 // list of listeners
18586 aniEles: new Collection(this),
18587 // elements being animated
18588 data: options.data || {},
18589 // data for the core
18590 scratch: {},
18591 // scratch object for core
18592 layout: null,
18593 renderer: null,
18594 destroyed: false,
18595 // whether destroy was called
18596 notificationsEnabled: true,
18597 // whether notifications are sent to the renderer
18598 minZoom: 1e-50,
18599 maxZoom: 1e50,
18600 zoomingEnabled: defVal(true, options.zoomingEnabled),
18601 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18602 panningEnabled: defVal(true, options.panningEnabled),
18603 userPanningEnabled: defVal(true, options.userPanningEnabled),
18604 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18605 autolock: defVal(false, options.autolock, options.autolockNodes),
18606 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18607 autounselectify: defVal(false, options.autounselectify),
18608 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18609 zoom: number(options.zoom) ? options.zoom : 1,
18610 pan: {
18611 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18612 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18613 },
18614 animation: {
18615 // object for currently-running animations
18616 current: [],
18617 queue: []
18618 },
18619 hasCompoundNodes: false,
18620 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
18621 };
18622
18623 this.createEmitter(); // set selection type
18624
18625 this.selectionType(options.selectionType); // init zoom bounds
18626
18627 this.zoomRange({
18628 min: options.minZoom,
18629 max: options.maxZoom
18630 });
18631
18632 var loadExtData = function loadExtData(extData, next) {
18633 var anyIsPromise = extData.some(promise);
18634
18635 if (anyIsPromise) {
18636 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18637 } else {
18638 next(extData); // exec synchronously for convenience
18639 }
18640 }; // start with the default stylesheet so we have something before loading an external stylesheet
18641
18642
18643 if (_p.styleEnabled) {
18644 cy.setStyle([]);
18645 } // create the renderer
18646
18647
18648 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18649
18650 cy.initRenderer(rendererOptions);
18651
18652 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18653 cy.notifications(false); // remove old elements
18654
18655 var oldEles = cy.mutableElements();
18656
18657 if (oldEles.length > 0) {
18658 oldEles.remove();
18659 }
18660
18661 if (elements != null) {
18662 if (plainObject(elements) || array(elements)) {
18663 cy.add(elements);
18664 }
18665 }
18666
18667 cy.one('layoutready', function (e) {
18668 cy.notifications(true);
18669 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18670
18671 cy.one('load', onload);
18672 cy.emitAndNotify('load');
18673 }).one('layoutstop', function () {
18674 cy.one('done', ondone);
18675 cy.emit('done');
18676 });
18677 var layoutOpts = extend({}, cy._private.options.layout);
18678 layoutOpts.eles = cy.elements();
18679 cy.layout(layoutOpts).run();
18680 };
18681
18682 loadExtData([options.style, options.elements], function (thens) {
18683 var initStyle = thens[0];
18684 var initEles = thens[1]; // init style
18685
18686 if (_p.styleEnabled) {
18687 cy.style().append(initStyle);
18688 } // initial load
18689
18690
18691 setElesAndLayout(initEles, function () {
18692 // onready
18693 cy.startAnimationLoop();
18694 _p.ready = true; // if a ready callback is specified as an option, the bind it
18695
18696 if (fn(options.ready)) {
18697 cy.on('ready', options.ready);
18698 } // bind all the ready handlers registered before creating this instance
18699
18700
18701 for (var i = 0; i < readies.length; i++) {
18702 var fn$1 = readies[i];
18703 cy.on('ready', fn$1);
18704 }
18705
18706 if (reg) {
18707 reg.readies = [];
18708 } // 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
18709
18710
18711 cy.emit('ready');
18712 }, options.done);
18713 });
18714};
18715
18716var corefn$9 = Core.prototype; // short alias
18717
18718extend(corefn$9, {
18719 instanceString: function instanceString() {
18720 return 'core';
18721 },
18722 isReady: function isReady() {
18723 return this._private.ready;
18724 },
18725 destroyed: function destroyed() {
18726 return this._private.destroyed;
18727 },
18728 ready: function ready(fn) {
18729 if (this.isReady()) {
18730 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18731 } else {
18732 this.on('ready', fn);
18733 }
18734
18735 return this;
18736 },
18737 destroy: function destroy() {
18738 var cy = this;
18739 if (cy.destroyed()) return;
18740 cy.stopAnimationLoop();
18741 cy.destroyRenderer();
18742 this.emit('destroy');
18743 cy._private.destroyed = true;
18744 return cy;
18745 },
18746 hasElementWithId: function hasElementWithId(id) {
18747 return this._private.elements.hasElementWithId(id);
18748 },
18749 getElementById: function getElementById(id) {
18750 return this._private.elements.getElementById(id);
18751 },
18752 hasCompoundNodes: function hasCompoundNodes() {
18753 return this._private.hasCompoundNodes;
18754 },
18755 headless: function headless() {
18756 return this._private.renderer.isHeadless();
18757 },
18758 styleEnabled: function styleEnabled() {
18759 return this._private.styleEnabled;
18760 },
18761 addToPool: function addToPool(eles) {
18762 this._private.elements.merge(eles);
18763
18764 return this; // chaining
18765 },
18766 removeFromPool: function removeFromPool(eles) {
18767 this._private.elements.unmerge(eles);
18768
18769 return this;
18770 },
18771 container: function container() {
18772 return this._private.container || null;
18773 },
18774 mount: function mount(container) {
18775 if (container == null) {
18776 return;
18777 }
18778
18779 var cy = this;
18780 var _p = cy._private;
18781 var options = _p.options;
18782
18783 if (!htmlElement(container) && htmlElement(container[0])) {
18784 container = container[0];
18785 }
18786
18787 cy.stopAnimationLoop();
18788 cy.destroyRenderer();
18789 _p.container = container;
18790 _p.styleEnabled = true;
18791 cy.invalidateSize();
18792 cy.initRenderer(extend({}, options, options.renderer, {
18793 // allow custom renderer name to be re-used, otherwise use canvas
18794 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18795 }));
18796 cy.startAnimationLoop();
18797 cy.style(options.style);
18798 cy.emit('mount');
18799 return cy;
18800 },
18801 unmount: function unmount() {
18802 var cy = this;
18803 cy.stopAnimationLoop();
18804 cy.destroyRenderer();
18805 cy.initRenderer({
18806 name: 'null'
18807 });
18808 cy.emit('unmount');
18809 return cy;
18810 },
18811 options: function options() {
18812 return copy(this._private.options);
18813 },
18814 json: function json(obj) {
18815 var cy = this;
18816 var _p = cy._private;
18817 var eles = cy.mutableElements();
18818
18819 var getFreshRef = function getFreshRef(ele) {
18820 return cy.getElementById(ele.id());
18821 };
18822
18823 if (plainObject(obj)) {
18824 // set
18825 cy.startBatch();
18826
18827 if (obj.elements) {
18828 var idInJson = {};
18829
18830 var updateEles = function updateEles(jsons, gr) {
18831 var toAdd = [];
18832 var toMod = [];
18833
18834 for (var i = 0; i < jsons.length; i++) {
18835 var json = jsons[i];
18836
18837 if (!json.data.id) {
18838 warn('cy.json() cannot handle elements without an ID attribute');
18839 continue;
18840 }
18841
18842 var id = '' + json.data.id; // id must be string
18843
18844 var ele = cy.getElementById(id);
18845 idInJson[id] = true;
18846
18847 if (ele.length !== 0) {
18848 // existing element should be updated
18849 toMod.push({
18850 ele: ele,
18851 json: json
18852 });
18853 } else {
18854 // otherwise should be added
18855 if (gr) {
18856 json.group = gr;
18857 toAdd.push(json);
18858 } else {
18859 toAdd.push(json);
18860 }
18861 }
18862 }
18863
18864 cy.add(toAdd);
18865
18866 for (var _i = 0; _i < toMod.length; _i++) {
18867 var _toMod$_i = toMod[_i],
18868 _ele = _toMod$_i.ele,
18869 _json = _toMod$_i.json;
18870
18871 _ele.json(_json);
18872 }
18873 };
18874
18875 if (array(obj.elements)) {
18876 // elements: []
18877 updateEles(obj.elements);
18878 } else {
18879 // elements: { nodes: [], edges: [] }
18880 var grs = ['nodes', 'edges'];
18881
18882 for (var i = 0; i < grs.length; i++) {
18883 var gr = grs[i];
18884 var elements = obj.elements[gr];
18885
18886 if (array(elements)) {
18887 updateEles(elements, gr);
18888 }
18889 }
18890 }
18891
18892 var parentsToRemove = cy.collection();
18893 eles.filter(function (ele) {
18894 return !idInJson[ele.id()];
18895 }).forEach(function (ele) {
18896 if (ele.isParent()) {
18897 parentsToRemove.merge(ele);
18898 } else {
18899 ele.remove();
18900 }
18901 }); // so that children are not removed w/parent
18902
18903 parentsToRemove.forEach(function (ele) {
18904 return ele.children().move({
18905 parent: null
18906 });
18907 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18908
18909 parentsToRemove.forEach(function (ele) {
18910 return getFreshRef(ele).remove();
18911 });
18912 }
18913
18914 if (obj.style) {
18915 cy.style(obj.style);
18916 }
18917
18918 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18919 cy.zoom(obj.zoom);
18920 }
18921
18922 if (obj.pan) {
18923 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18924 cy.pan(obj.pan);
18925 }
18926 }
18927
18928 if (obj.data) {
18929 cy.data(obj.data);
18930 }
18931
18932 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify', 'multiClickDebounceTime'];
18933
18934 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18935 var f = fields[_i2];
18936
18937 if (obj[f] != null) {
18938 cy[f](obj[f]);
18939 }
18940 }
18941
18942 cy.endBatch();
18943 return this; // chaining
18944 } else {
18945 // get
18946 var flat = !!obj;
18947 var json = {};
18948
18949 if (flat) {
18950 json.elements = this.elements().map(function (ele) {
18951 return ele.json();
18952 });
18953 } else {
18954 json.elements = {};
18955 eles.forEach(function (ele) {
18956 var group = ele.group();
18957
18958 if (!json.elements[group]) {
18959 json.elements[group] = [];
18960 }
18961
18962 json.elements[group].push(ele.json());
18963 });
18964 }
18965
18966 if (this._private.styleEnabled) {
18967 json.style = cy.style().json();
18968 }
18969
18970 json.data = copy(cy.data());
18971 var options = _p.options;
18972 json.zoomingEnabled = _p.zoomingEnabled;
18973 json.userZoomingEnabled = _p.userZoomingEnabled;
18974 json.zoom = _p.zoom;
18975 json.minZoom = _p.minZoom;
18976 json.maxZoom = _p.maxZoom;
18977 json.panningEnabled = _p.panningEnabled;
18978 json.userPanningEnabled = _p.userPanningEnabled;
18979 json.pan = copy(_p.pan);
18980 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18981 json.renderer = copy(options.renderer);
18982 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18983 json.textureOnViewport = options.textureOnViewport;
18984 json.wheelSensitivity = options.wheelSensitivity;
18985 json.motionBlur = options.motionBlur;
18986 json.multiClickDebounceTime = options.multiClickDebounceTime;
18987 return json;
18988 }
18989 }
18990});
18991corefn$9.$id = corefn$9.getElementById;
18992[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18993 extend(corefn$9, props);
18994});
18995
18996/* eslint-disable no-unused-vars */
18997
18998var defaults$9 = {
18999 fit: true,
19000 // whether to fit the viewport to the graph
19001 directed: false,
19002 // whether the tree is directed downwards (or edges can point in any direction if false)
19003 padding: 30,
19004 // padding on fit
19005 circle: false,
19006 // put depths in concentric circles if true, put depths top down if false
19007 grid: false,
19008 // whether to create an even grid into which the DAG is placed (circle:false only)
19009 spacingFactor: 1.75,
19010 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19011 boundingBox: undefined,
19012 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19013 avoidOverlap: true,
19014 // prevents node overlap, may overflow boundingBox if not enough space
19015 nodeDimensionsIncludeLabels: false,
19016 // Excludes the label when calculating node bounding boxes for the layout algorithm
19017 roots: undefined,
19018 // the roots of the trees
19019 maximal: false,
19020 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
19021 depthSort: undefined,
19022 // a sorting function to order nodes at equal depth. e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19023 animate: false,
19024 // whether to transition the node positions
19025 animationDuration: 500,
19026 // duration of animation in ms if enabled
19027 animationEasing: undefined,
19028 // easing of animation if enabled,
19029 animateFilter: function animateFilter(node, i) {
19030 return true;
19031 },
19032 // 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
19033 ready: undefined,
19034 // callback on layoutready
19035 stop: undefined,
19036 // callback on layoutstop
19037 transform: function transform(node, position) {
19038 return position;
19039 } // transform a given node position. Useful for changing flow direction in discrete layouts
19040
19041};
19042/* eslint-enable */
19043
19044var getInfo = function getInfo(ele) {
19045 return ele.scratch('breadthfirst');
19046};
19047
19048var setInfo = function setInfo(ele, obj) {
19049 return ele.scratch('breadthfirst', obj);
19050};
19051
19052function BreadthFirstLayout(options) {
19053 this.options = extend({}, defaults$9, options);
19054}
19055
19056BreadthFirstLayout.prototype.run = function () {
19057 var params = this.options;
19058 var options = params;
19059 var cy = params.cy;
19060 var eles = options.eles;
19061 var nodes = eles.nodes().filter(function (n) {
19062 return !n.isParent();
19063 });
19064 var graph = eles;
19065 var directed = options.directed;
19066 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
19067
19068 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19069 x1: 0,
19070 y1: 0,
19071 w: cy.width(),
19072 h: cy.height()
19073 });
19074 var roots;
19075
19076 if (elementOrCollection(options.roots)) {
19077 roots = options.roots;
19078 } else if (array(options.roots)) {
19079 var rootsArray = [];
19080
19081 for (var i = 0; i < options.roots.length; i++) {
19082 var id = options.roots[i];
19083 var ele = cy.getElementById(id);
19084 rootsArray.push(ele);
19085 }
19086
19087 roots = cy.collection(rootsArray);
19088 } else if (string(options.roots)) {
19089 roots = cy.$(options.roots);
19090 } else {
19091 if (directed) {
19092 roots = nodes.roots();
19093 } else {
19094 var components = eles.components();
19095 roots = cy.collection();
19096
19097 var _loop = function _loop(_i) {
19098 var comp = components[_i];
19099 var maxDegree = comp.maxDegree(false);
19100 var compRoots = comp.filter(function (ele) {
19101 return ele.degree(false) === maxDegree;
19102 });
19103 roots = roots.add(compRoots);
19104 };
19105
19106 for (var _i = 0; _i < components.length; _i++) {
19107 _loop(_i);
19108 }
19109 }
19110 }
19111
19112 var depths = [];
19113 var foundByBfs = {};
19114
19115 var addToDepth = function addToDepth(ele, d) {
19116 if (depths[d] == null) {
19117 depths[d] = [];
19118 }
19119
19120 var i = depths[d].length;
19121 depths[d].push(ele);
19122 setInfo(ele, {
19123 index: i,
19124 depth: d
19125 });
19126 };
19127
19128 var changeDepth = function changeDepth(ele, newDepth) {
19129 var _getInfo = getInfo(ele),
19130 depth = _getInfo.depth,
19131 index = _getInfo.index;
19132
19133 depths[depth][index] = null;
19134 addToDepth(ele, newDepth);
19135 }; // find the depths of the nodes
19136
19137
19138 graph.bfs({
19139 roots: roots,
19140 directed: options.directed,
19141 visit: function visit(node, edge, pNode, i, depth) {
19142 var ele = node[0];
19143 var id = ele.id();
19144 addToDepth(ele, depth);
19145 foundByBfs[id] = true;
19146 }
19147 }); // check for nodes not found by bfs
19148
19149 var orphanNodes = [];
19150
19151 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19152 var _ele = nodes[_i2];
19153
19154 if (foundByBfs[_ele.id()]) {
19155 continue;
19156 } else {
19157 orphanNodes.push(_ele);
19158 }
19159 } // assign the nodes a depth and index
19160
19161
19162 var assignDepthsAt = function assignDepthsAt(i) {
19163 var eles = depths[i];
19164
19165 for (var j = 0; j < eles.length; j++) {
19166 var _ele2 = eles[j];
19167
19168 if (_ele2 == null) {
19169 eles.splice(j, 1);
19170 j--;
19171 continue;
19172 }
19173
19174 setInfo(_ele2, {
19175 depth: i,
19176 index: j
19177 });
19178 }
19179 };
19180
19181 var assignDepths = function assignDepths() {
19182 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19183 assignDepthsAt(_i3);
19184 }
19185 };
19186
19187 var adjustMaximally = function adjustMaximally(ele, shifted) {
19188 var eInfo = getInfo(ele);
19189 var incomers = ele.incomers().filter(function (el) {
19190 return el.isNode() && eles.has(el);
19191 });
19192 var maxDepth = -1;
19193 var id = ele.id();
19194
19195 for (var k = 0; k < incomers.length; k++) {
19196 var incmr = incomers[k];
19197 var iInfo = getInfo(incmr);
19198 maxDepth = Math.max(maxDepth, iInfo.depth);
19199 }
19200
19201 if (eInfo.depth <= maxDepth) {
19202 if (shifted[id]) {
19203 return null;
19204 }
19205
19206 changeDepth(ele, maxDepth + 1);
19207 shifted[id] = true;
19208 return true;
19209 }
19210
19211 return false;
19212 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19213
19214
19215 if (directed && maximal) {
19216 var Q = [];
19217 var shifted = {};
19218
19219 var enqueue = function enqueue(n) {
19220 return Q.push(n);
19221 };
19222
19223 var dequeue = function dequeue() {
19224 return Q.shift();
19225 };
19226
19227 nodes.forEach(function (n) {
19228 return Q.push(n);
19229 });
19230
19231 while (Q.length > 0) {
19232 var _ele3 = dequeue();
19233
19234 var didShift = adjustMaximally(_ele3, shifted);
19235
19236 if (didShift) {
19237 _ele3.outgoers().filter(function (el) {
19238 return el.isNode() && eles.has(el);
19239 }).forEach(enqueue);
19240 } else if (didShift === null) {
19241 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19242 break; // exit on failure
19243 }
19244 }
19245 }
19246
19247 assignDepths(); // clear holes
19248 // find min distance we need to leave between nodes
19249
19250 var minDistance = 0;
19251
19252 if (options.avoidOverlap) {
19253 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19254 var n = nodes[_i4];
19255 var nbb = n.layoutDimensions(options);
19256 var w = nbb.w;
19257 var h = nbb.h;
19258 minDistance = Math.max(minDistance, w, h);
19259 }
19260 } // get the weighted percent for an element based on its connectivity to other levels
19261
19262
19263 var cachedWeightedPercent = {};
19264
19265 var getWeightedPercent = function getWeightedPercent(ele) {
19266 if (cachedWeightedPercent[ele.id()]) {
19267 return cachedWeightedPercent[ele.id()];
19268 }
19269
19270 var eleDepth = getInfo(ele).depth;
19271 var neighbors = ele.neighborhood();
19272 var percent = 0;
19273 var samples = 0;
19274
19275 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19276 var neighbor = neighbors[_i5];
19277
19278 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19279 continue;
19280 }
19281
19282 var bf = getInfo(neighbor);
19283
19284 if (bf == null) {
19285 continue;
19286 }
19287
19288 var index = bf.index;
19289 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19290
19291 if (index == null || depth == null) {
19292 continue;
19293 }
19294
19295 var nDepth = depths[depth].length;
19296
19297 if (depth < eleDepth) {
19298 // only get influenced by elements above
19299 percent += index / nDepth;
19300 samples++;
19301 }
19302 }
19303
19304 samples = Math.max(1, samples);
19305 percent = percent / samples;
19306
19307 if (samples === 0) {
19308 // put lone nodes at the start
19309 percent = 0;
19310 }
19311
19312 cachedWeightedPercent[ele.id()] = percent;
19313 return percent;
19314 }; // rearrange the indices in each depth level based on connectivity
19315
19316
19317 var sortFn = function sortFn(a, b) {
19318 var apct = getWeightedPercent(a);
19319 var bpct = getWeightedPercent(b);
19320 var diff = apct - bpct;
19321
19322 if (diff === 0) {
19323 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19324 } else {
19325 return diff;
19326 }
19327 };
19328
19329 if (options.depthSort !== undefined) {
19330 sortFn = options.depthSort;
19331 } // sort each level to make connected nodes closer
19332
19333
19334 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19335 depths[_i6].sort(sortFn);
19336
19337 assignDepthsAt(_i6);
19338 } // assign orphan nodes to a new top-level depth
19339
19340
19341 var orphanDepth = [];
19342
19343 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19344 orphanDepth.push(orphanNodes[_i7]);
19345 }
19346
19347 depths.unshift(orphanDepth);
19348 assignDepths();
19349 var biggestDepthSize = 0;
19350
19351 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19352 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19353 }
19354
19355 var center = {
19356 x: bb.x1 + bb.w / 2,
19357 y: bb.x1 + bb.h / 2
19358 };
19359 var maxDepthSize = depths.reduce(function (max, eles) {
19360 return Math.max(max, eles.length);
19361 }, 0);
19362
19363 var getPosition = function getPosition(ele) {
19364 var _getInfo2 = getInfo(ele),
19365 depth = _getInfo2.depth,
19366 index = _getInfo2.index;
19367
19368 var depthSize = depths[depth].length;
19369 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19370 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19371 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19372 radiusStepSize = Math.max(radiusStepSize, minDistance);
19373
19374 if (!options.circle) {
19375 var epos = {
19376 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19377 y: (depth + 1) * distanceY
19378 };
19379 return epos;
19380 } else {
19381 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19382 var theta = 2 * Math.PI / depths[depth].length * index;
19383
19384 if (depth === 0 && depths[0].length === 1) {
19385 radius = 1;
19386 }
19387
19388 return {
19389 x: center.x + radius * Math.cos(theta),
19390 y: center.y + radius * Math.sin(theta)
19391 };
19392 }
19393 };
19394
19395 eles.nodes().layoutPositions(this, options, getPosition);
19396 return this; // chaining
19397};
19398
19399var defaults$a = {
19400 fit: true,
19401 // whether to fit the viewport to the graph
19402 padding: 30,
19403 // the padding on fit
19404 boundingBox: undefined,
19405 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19406 avoidOverlap: true,
19407 // prevents node overlap, may overflow boundingBox and radius if not enough space
19408 nodeDimensionsIncludeLabels: false,
19409 // Excludes the label when calculating node bounding boxes for the layout algorithm
19410 spacingFactor: undefined,
19411 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19412 radius: undefined,
19413 // the radius of the circle
19414 startAngle: 3 / 2 * Math.PI,
19415 // where nodes start in radians
19416 sweep: undefined,
19417 // how many radians should be between the first and last node (defaults to full circle)
19418 clockwise: true,
19419 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19420 sort: undefined,
19421 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19422 animate: false,
19423 // whether to transition the node positions
19424 animationDuration: 500,
19425 // duration of animation in ms if enabled
19426 animationEasing: undefined,
19427 // easing of animation if enabled
19428 animateFilter: function animateFilter(node, i) {
19429 return true;
19430 },
19431 // 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
19432 ready: undefined,
19433 // callback on layoutready
19434 stop: undefined,
19435 // callback on layoutstop
19436 transform: function transform(node, position) {
19437 return position;
19438 } // transform a given node position. Useful for changing flow direction in discrete layouts
19439
19440};
19441
19442function CircleLayout(options) {
19443 this.options = extend({}, defaults$a, options);
19444}
19445
19446CircleLayout.prototype.run = function () {
19447 var params = this.options;
19448 var options = params;
19449 var cy = params.cy;
19450 var eles = options.eles;
19451 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19452 var nodes = eles.nodes().not(':parent');
19453
19454 if (options.sort) {
19455 nodes = nodes.sort(options.sort);
19456 }
19457
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 sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19469 var dTheta = sweep / Math.max(1, nodes.length - 1);
19470 var r;
19471 var minDistance = 0;
19472
19473 for (var i = 0; i < nodes.length; i++) {
19474 var n = nodes[i];
19475 var nbb = n.layoutDimensions(options);
19476 var w = nbb.w;
19477 var h = nbb.h;
19478 minDistance = Math.max(minDistance, w, h);
19479 }
19480
19481 if (number(options.radius)) {
19482 r = options.radius;
19483 } else if (nodes.length <= 1) {
19484 r = 0;
19485 } else {
19486 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19487 } // calculate the radius
19488
19489
19490 if (nodes.length > 1 && options.avoidOverlap) {
19491 // but only if more than one node (can't overlap)
19492 minDistance *= 1.75; // just to have some nice spacing
19493
19494 var dcos = Math.cos(dTheta) - Math.cos(0);
19495 var dsin = Math.sin(dTheta) - Math.sin(0);
19496 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19497
19498 r = Math.max(rMin, r);
19499 }
19500
19501 var getPos = function getPos(ele, i) {
19502 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19503 var rx = r * Math.cos(theta);
19504 var ry = r * Math.sin(theta);
19505 var pos = {
19506 x: center.x + rx,
19507 y: center.y + ry
19508 };
19509 return pos;
19510 };
19511
19512 eles.nodes().layoutPositions(this, options, getPos);
19513 return this; // chaining
19514};
19515
19516var defaults$b = {
19517 fit: true,
19518 // whether to fit the viewport to the graph
19519 padding: 30,
19520 // the padding on fit
19521 startAngle: 3 / 2 * Math.PI,
19522 // where nodes start in radians
19523 sweep: undefined,
19524 // how many radians should be between the first and last node (defaults to full circle)
19525 clockwise: true,
19526 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19527 equidistant: false,
19528 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19529 minNodeSpacing: 10,
19530 // min spacing between outside of nodes (used for radius adjustment)
19531 boundingBox: undefined,
19532 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19533 avoidOverlap: true,
19534 // prevents node overlap, may overflow boundingBox if not enough space
19535 nodeDimensionsIncludeLabels: false,
19536 // Excludes the label when calculating node bounding boxes for the layout algorithm
19537 height: undefined,
19538 // height of layout area (overrides container height)
19539 width: undefined,
19540 // width of layout area (overrides container width)
19541 spacingFactor: undefined,
19542 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19543 concentric: function concentric(node) {
19544 // returns numeric value for each node, placing higher nodes in levels towards the centre
19545 return node.degree();
19546 },
19547 levelWidth: function levelWidth(nodes) {
19548 // the variation of concentric values in each level
19549 return nodes.maxDegree() / 4;
19550 },
19551 animate: false,
19552 // whether to transition the node positions
19553 animationDuration: 500,
19554 // duration of animation in ms if enabled
19555 animationEasing: undefined,
19556 // easing of animation if enabled
19557 animateFilter: function animateFilter(node, i) {
19558 return true;
19559 },
19560 // 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
19561 ready: undefined,
19562 // callback on layoutready
19563 stop: undefined,
19564 // callback on layoutstop
19565 transform: function transform(node, position) {
19566 return position;
19567 } // transform a given node position. Useful for changing flow direction in discrete layouts
19568
19569};
19570
19571function ConcentricLayout(options) {
19572 this.options = extend({}, defaults$b, options);
19573}
19574
19575ConcentricLayout.prototype.run = function () {
19576 var params = this.options;
19577 var options = params;
19578 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19579 var cy = params.cy;
19580 var eles = options.eles;
19581 var nodes = eles.nodes().not(':parent');
19582 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19583 x1: 0,
19584 y1: 0,
19585 w: cy.width(),
19586 h: cy.height()
19587 });
19588 var center = {
19589 x: bb.x1 + bb.w / 2,
19590 y: bb.y1 + bb.h / 2
19591 };
19592 var nodeValues = []; // { node, value }
19593
19594 var maxNodeSize = 0;
19595
19596 for (var i = 0; i < nodes.length; i++) {
19597 var node = nodes[i];
19598 var value = void 0; // calculate the node value
19599
19600 value = options.concentric(node);
19601 nodeValues.push({
19602 value: value,
19603 node: node
19604 }); // for style mapping
19605
19606 node._private.scratch.concentric = value;
19607 } // in case we used the `concentric` in style
19608
19609
19610 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19611
19612 for (var _i = 0; _i < nodes.length; _i++) {
19613 var _node = nodes[_i];
19614
19615 var nbb = _node.layoutDimensions(options);
19616
19617 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19618 } // sort node values in descreasing order
19619
19620
19621 nodeValues.sort(function (a, b) {
19622 return b.value - a.value;
19623 });
19624 var levelWidth = options.levelWidth(nodes); // put the values into levels
19625
19626 var levels = [[]];
19627 var currentLevel = levels[0];
19628
19629 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19630 var val = nodeValues[_i2];
19631
19632 if (currentLevel.length > 0) {
19633 var diff = Math.abs(currentLevel[0].value - val.value);
19634
19635 if (diff >= levelWidth) {
19636 currentLevel = [];
19637 levels.push(currentLevel);
19638 }
19639 }
19640
19641 currentLevel.push(val);
19642 } // create positions from levels
19643
19644
19645 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19646
19647 if (!options.avoidOverlap) {
19648 // then strictly constrain to bb
19649 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19650 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19651 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19652 minDist = Math.min(minDist, rStep);
19653 } // find the metrics for each level
19654
19655
19656 var r = 0;
19657
19658 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19659 var level = levels[_i3];
19660 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19661 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19662
19663 if (level.length > 1 && options.avoidOverlap) {
19664 // but only if more than one node (can't overlap)
19665 var dcos = Math.cos(dTheta) - Math.cos(0);
19666 var dsin = Math.sin(dTheta) - Math.sin(0);
19667 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19668
19669 r = Math.max(rMin, r);
19670 }
19671
19672 level.r = r;
19673 r += minDist;
19674 }
19675
19676 if (options.equidistant) {
19677 var rDeltaMax = 0;
19678 var _r = 0;
19679
19680 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19681 var _level = levels[_i4];
19682 var rDelta = _level.r - _r;
19683 rDeltaMax = Math.max(rDeltaMax, rDelta);
19684 }
19685
19686 _r = 0;
19687
19688 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19689 var _level2 = levels[_i5];
19690
19691 if (_i5 === 0) {
19692 _r = _level2.r;
19693 }
19694
19695 _level2.r = _r;
19696 _r += rDeltaMax;
19697 }
19698 } // calculate the node positions
19699
19700
19701 var pos = {}; // id => position
19702
19703 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19704 var _level3 = levels[_i6];
19705 var _dTheta = _level3.dTheta;
19706 var _r2 = _level3.r;
19707
19708 for (var j = 0; j < _level3.length; j++) {
19709 var _val = _level3[j];
19710 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19711 var p = {
19712 x: center.x + _r2 * Math.cos(theta),
19713 y: center.y + _r2 * Math.sin(theta)
19714 };
19715 pos[_val.node.id()] = p;
19716 }
19717 } // position the nodes
19718
19719
19720 eles.nodes().layoutPositions(this, options, function (ele) {
19721 var id = ele.id();
19722 return pos[id];
19723 });
19724 return this; // chaining
19725};
19726
19727/*
19728The CoSE layout was written by Gerardo Huck.
19729https://www.linkedin.com/in/gerardohuck/
19730
19731Based on the following article:
19732http://dl.acm.org/citation.cfm?id=1498047
19733
19734Modifications tracked on Github.
19735*/
19736var DEBUG;
19737/**
19738 * @brief : default layout options
19739 */
19740
19741var defaults$c = {
19742 // Called on `layoutready`
19743 ready: function ready() {},
19744 // Called on `layoutstop`
19745 stop: function stop() {},
19746 // Whether to animate while running the layout
19747 // true : Animate continuously as the layout is running
19748 // false : Just show the end result
19749 // 'end' : Animate with the end result, from the initial positions to the end positions
19750 animate: true,
19751 // Easing of the animation for animate:'end'
19752 animationEasing: undefined,
19753 // The duration of the animation for animate:'end'
19754 animationDuration: undefined,
19755 // A function that determines whether the node should be animated
19756 // All nodes animated by default on animate enabled
19757 // Non-animated nodes are positioned immediately when the layout starts
19758 animateFilter: function animateFilter(node, i) {
19759 return true;
19760 },
19761 // The layout animates only after this many milliseconds for animate:true
19762 // (prevents flashing on fast runs)
19763 animationThreshold: 250,
19764 // Number of iterations between consecutive screen positions update
19765 refresh: 20,
19766 // Whether to fit the network view after when done
19767 fit: true,
19768 // Padding on fit
19769 padding: 30,
19770 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19771 boundingBox: undefined,
19772 // Excludes the label when calculating node bounding boxes for the layout algorithm
19773 nodeDimensionsIncludeLabels: false,
19774 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19775 randomize: false,
19776 // Extra spacing between components in non-compound graphs
19777 componentSpacing: 40,
19778 // Node repulsion (non overlapping) multiplier
19779 nodeRepulsion: function nodeRepulsion(node) {
19780 return 2048;
19781 },
19782 // Node repulsion (overlapping) multiplier
19783 nodeOverlap: 4,
19784 // Ideal edge (non nested) length
19785 idealEdgeLength: function idealEdgeLength(edge) {
19786 return 32;
19787 },
19788 // Divisor to compute edge forces
19789 edgeElasticity: function edgeElasticity(edge) {
19790 return 32;
19791 },
19792 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19793 nestingFactor: 1.2,
19794 // Gravity force (constant)
19795 gravity: 1,
19796 // Maximum number of iterations to perform
19797 numIter: 1000,
19798 // Initial temperature (maximum node displacement)
19799 initialTemp: 1000,
19800 // Cooling factor (how the temperature is reduced between consecutive iterations
19801 coolingFactor: 0.99,
19802 // Lower temperature threshold (below this point the layout will end)
19803 minTemp: 1.0
19804};
19805/**
19806 * @brief : constructor
19807 * @arg options : object containing layout options
19808 */
19809
19810function CoseLayout(options) {
19811 this.options = extend({}, defaults$c, options);
19812 this.options.layout = this;
19813}
19814/**
19815 * @brief : runs the layout
19816 */
19817
19818
19819CoseLayout.prototype.run = function () {
19820 var options = this.options;
19821 var cy = options.cy;
19822 var layout = this;
19823 layout.stopped = false;
19824
19825 if (options.animate === true || options.animate === false) {
19826 layout.emit({
19827 type: 'layoutstart',
19828 layout: layout
19829 });
19830 } // Set DEBUG - Global variable
19831
19832
19833 if (true === options.debug) {
19834 DEBUG = true;
19835 } else {
19836 DEBUG = false;
19837 } // Initialize layout info
19838
19839
19840 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19841
19842 if (DEBUG) {
19843 printLayoutInfo(layoutInfo);
19844 } // If required, randomize node positions
19845
19846
19847 if (options.randomize) {
19848 randomizePositions(layoutInfo);
19849 }
19850
19851 var startTime = performanceNow();
19852
19853 var refresh = function refresh() {
19854 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19855
19856 if (true === options.fit) {
19857 cy.fit(options.padding);
19858 }
19859 };
19860
19861 var mainLoop = function mainLoop(i) {
19862 if (layout.stopped || i >= options.numIter) {
19863 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19864 return false;
19865 } // Do one step in the phisical simulation
19866
19867
19868 step$1(layoutInfo, options); // Update temperature
19869
19870 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19871
19872 if (layoutInfo.temperature < options.minTemp) {
19873 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19874 return false;
19875 }
19876
19877 return true;
19878 };
19879
19880 var done = function done() {
19881 if (options.animate === true || options.animate === false) {
19882 refresh(); // Layout has finished
19883
19884 layout.one('layoutstop', options.stop);
19885 layout.emit({
19886 type: 'layoutstop',
19887 layout: layout
19888 });
19889 } else {
19890 var nodes = options.eles.nodes();
19891 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19892 nodes.layoutPositions(layout, options, getScaledPos);
19893 }
19894 };
19895
19896 var i = 0;
19897 var loopRet = true;
19898
19899 if (options.animate === true) {
19900 var frame = function frame() {
19901 var f = 0;
19902
19903 while (loopRet && f < options.refresh) {
19904 loopRet = mainLoop(i);
19905 i++;
19906 f++;
19907 }
19908
19909 if (!loopRet) {
19910 // it's done
19911 separateComponents(layoutInfo, options);
19912 done();
19913 } else {
19914 var now = performanceNow();
19915
19916 if (now - startTime >= options.animationThreshold) {
19917 refresh();
19918 }
19919
19920 requestAnimationFrame(frame);
19921 }
19922 };
19923
19924 frame();
19925 } else {
19926 while (loopRet) {
19927 loopRet = mainLoop(i);
19928 i++;
19929 }
19930
19931 separateComponents(layoutInfo, options);
19932 done();
19933 }
19934
19935 return this; // chaining
19936};
19937/**
19938 * @brief : called on continuous layouts to stop them before they finish
19939 */
19940
19941
19942CoseLayout.prototype.stop = function () {
19943 this.stopped = true;
19944
19945 if (this.thread) {
19946 this.thread.stop();
19947 }
19948
19949 this.emit('layoutstop');
19950 return this; // chaining
19951};
19952
19953CoseLayout.prototype.destroy = function () {
19954 if (this.thread) {
19955 this.thread.stop();
19956 }
19957
19958 return this; // chaining
19959};
19960/**
19961 * @brief : Creates an object which is contains all the data
19962 * used in the layout process
19963 * @arg cy : cytoscape.js object
19964 * @return : layoutInfo object initialized
19965 */
19966
19967
19968var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19969 // Shortcut
19970 var edges = options.eles.edges();
19971 var nodes = options.eles.nodes();
19972 var layoutInfo = {
19973 isCompound: cy.hasCompoundNodes(),
19974 layoutNodes: [],
19975 idToIndex: {},
19976 nodeSize: nodes.size(),
19977 graphSet: [],
19978 indexToGraph: [],
19979 layoutEdges: [],
19980 edgeSize: edges.size(),
19981 temperature: options.initialTemp,
19982 clientWidth: cy.width(),
19983 clientHeight: cy.width(),
19984 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19985 x1: 0,
19986 y1: 0,
19987 w: cy.width(),
19988 h: cy.height()
19989 })
19990 };
19991 var components = options.eles.components();
19992 var id2cmptId = {};
19993
19994 for (var i = 0; i < components.length; i++) {
19995 var component = components[i];
19996
19997 for (var j = 0; j < component.length; j++) {
19998 var node = component[j];
19999 id2cmptId[node.id()] = i;
20000 }
20001 } // Iterate over all nodes, creating layout nodes
20002
20003
20004 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20005 var n = nodes[i];
20006 var nbb = n.layoutDimensions(options);
20007 var tempNode = {};
20008 tempNode.isLocked = n.locked();
20009 tempNode.id = n.data('id');
20010 tempNode.parentId = n.data('parent');
20011 tempNode.cmptId = id2cmptId[n.id()];
20012 tempNode.children = [];
20013 tempNode.positionX = n.position('x');
20014 tempNode.positionY = n.position('y');
20015 tempNode.offsetX = 0;
20016 tempNode.offsetY = 0;
20017 tempNode.height = nbb.w;
20018 tempNode.width = nbb.h;
20019 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20020 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20021 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20022 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20023 tempNode.padLeft = parseFloat(n.style('padding'));
20024 tempNode.padRight = parseFloat(n.style('padding'));
20025 tempNode.padTop = parseFloat(n.style('padding'));
20026 tempNode.padBottom = parseFloat(n.style('padding')); // forces
20027
20028 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
20029
20030 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
20031
20032 layoutInfo.idToIndex[tempNode.id] = i;
20033 } // Inline implementation of a queue, used for traversing the graph in BFS order
20034
20035
20036 var queue = [];
20037 var start = 0; // Points to the start the queue
20038
20039 var end = -1; // Points to the end of the queue
20040
20041 var tempGraph = []; // Second pass to add child information and
20042 // initialize queue for hierarchical traversal
20043
20044 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20045 var n = layoutInfo.layoutNodes[i];
20046 var p_id = n.parentId; // Check if node n has a parent node
20047
20048 if (null != p_id) {
20049 // Add node Id to parent's list of children
20050 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20051 } else {
20052 // If a node doesn't have a parent, then it's in the root graph
20053 queue[++end] = n.id;
20054 tempGraph.push(n.id);
20055 }
20056 } // Add root graph to graphSet
20057
20058
20059 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
20060
20061 while (start <= end) {
20062 // Get the node to visit and remove it from queue
20063 var node_id = queue[start++];
20064 var node_ix = layoutInfo.idToIndex[node_id];
20065 var node = layoutInfo.layoutNodes[node_ix];
20066 var children = node.children;
20067
20068 if (children.length > 0) {
20069 // Add children nodes as a new graph to graph set
20070 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
20071
20072 for (var i = 0; i < children.length; i++) {
20073 queue[++end] = children[i];
20074 }
20075 }
20076 } // Create indexToGraph map
20077
20078
20079 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20080 var graph = layoutInfo.graphSet[i];
20081
20082 for (var j = 0; j < graph.length; j++) {
20083 var index = layoutInfo.idToIndex[graph[j]];
20084 layoutInfo.indexToGraph[index] = i;
20085 }
20086 } // Iterate over all edges, creating Layout Edges
20087
20088
20089 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20090 var e = edges[i];
20091 var tempEdge = {};
20092 tempEdge.id = e.data('id');
20093 tempEdge.sourceId = e.data('source');
20094 tempEdge.targetId = e.data('target'); // Compute ideal length
20095
20096 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20097 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
20098
20099 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20100 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20101 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20102 var targetGraph = layoutInfo.indexToGraph[targetIx];
20103
20104 if (sourceGraph != targetGraph) {
20105 // Find lowest common graph ancestor
20106 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
20107
20108 var lcaGraph = layoutInfo.graphSet[lca];
20109 var depth = 0; // Source depth
20110
20111 var tempNode = layoutInfo.layoutNodes[sourceIx];
20112
20113 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20114 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20115 depth++;
20116 } // Target depth
20117
20118
20119 tempNode = layoutInfo.layoutNodes[targetIx];
20120
20121 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20122 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20123 depth++;
20124 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20125 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20126 // ". Depth: " + depth);
20127 // Update idealLength
20128
20129
20130 idealLength *= depth * options.nestingFactor;
20131 }
20132
20133 tempEdge.idealLength = idealLength;
20134 tempEdge.elasticity = elasticity;
20135 layoutInfo.layoutEdges.push(tempEdge);
20136 } // Finally, return layoutInfo object
20137
20138
20139 return layoutInfo;
20140};
20141/**
20142 * @brief : This function finds the index of the lowest common
20143 * graph ancestor between 2 nodes in the subtree
20144 * (from the graph hierarchy induced tree) whose
20145 * root is graphIx
20146 *
20147 * @arg node1: node1's ID
20148 * @arg node2: node2's ID
20149 * @arg layoutInfo: layoutInfo object
20150 *
20151 */
20152
20153
20154var findLCA = function findLCA(node1, node2, layoutInfo) {
20155 // Find their common ancester, starting from the root graph
20156 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20157
20158 if (2 > res.count) {
20159 // If aux function couldn't find the common ancester,
20160 // then it is the root graph
20161 return 0;
20162 } else {
20163 return res.graph;
20164 }
20165};
20166/**
20167 * @brief : Auxiliary function used for LCA computation
20168 *
20169 * @arg node1 : node1's ID
20170 * @arg node2 : node2's ID
20171 * @arg graphIx : subgraph index
20172 * @arg layoutInfo : layoutInfo object
20173 *
20174 * @return : object of the form {count: X, graph: Y}, where:
20175 * X is the number of ancesters (max: 2) found in
20176 * graphIx (and it's subgraphs),
20177 * Y is the graph index of the lowest graph containing
20178 * all X nodes
20179 */
20180
20181
20182var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20183 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20184
20185 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20186 return {
20187 count: 2,
20188 graph: graphIx
20189 };
20190 } // Make recursive calls for all subgraphs
20191
20192
20193 var c = 0;
20194
20195 for (var i = 0; i < graph.length; i++) {
20196 var nodeId = graph[i];
20197 var nodeIx = layoutInfo.idToIndex[nodeId];
20198 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20199
20200 if (0 === children.length) {
20201 continue;
20202 }
20203
20204 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20205 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20206
20207 if (0 === result.count) {
20208 // Neither node1 nor node2 are present in this subgraph
20209 continue;
20210 } else if (1 === result.count) {
20211 // One of (node1, node2) is present in this subgraph
20212 c++;
20213
20214 if (2 === c) {
20215 // We've already found both nodes, no need to keep searching
20216 break;
20217 }
20218 } else {
20219 // Both nodes are present in this subgraph
20220 return result;
20221 }
20222 }
20223
20224 return {
20225 count: c,
20226 graph: graphIx
20227 };
20228};
20229/**
20230 * @brief: printsLayoutInfo into js console
20231 * Only used for debbuging
20232 */
20233
20234
20235if (false) {
20236 var printLayoutInfo;
20237}
20238/**
20239 * @brief : Randomizes the position of all nodes
20240 */
20241
20242
20243var randomizePositions = function randomizePositions(layoutInfo, cy) {
20244 var width = layoutInfo.clientWidth;
20245 var height = layoutInfo.clientHeight;
20246
20247 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20248 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20249
20250 if (0 === n.children.length && !n.isLocked) {
20251 n.positionX = Math.random() * width;
20252 n.positionY = Math.random() * height;
20253 }
20254 }
20255};
20256
20257var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20258 var bb = layoutInfo.boundingBox;
20259 var coseBB = {
20260 x1: Infinity,
20261 x2: -Infinity,
20262 y1: Infinity,
20263 y2: -Infinity
20264 };
20265
20266 if (options.boundingBox) {
20267 nodes.forEach(function (node) {
20268 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20269 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20270 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20271 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20272 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20273 });
20274 coseBB.w = coseBB.x2 - coseBB.x1;
20275 coseBB.h = coseBB.y2 - coseBB.y1;
20276 }
20277
20278 return function (ele, i) {
20279 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20280
20281 if (options.boundingBox) {
20282 // then add extra bounding box constraint
20283 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20284 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20285 return {
20286 x: bb.x1 + pctX * bb.w,
20287 y: bb.y1 + pctY * bb.h
20288 };
20289 } else {
20290 return {
20291 x: lnode.positionX,
20292 y: lnode.positionY
20293 };
20294 }
20295 };
20296};
20297/**
20298 * @brief : Updates the positions of nodes in the network
20299 * @arg layoutInfo : LayoutInfo object
20300 * @arg cy : Cytoscape object
20301 * @arg options : Layout options
20302 */
20303
20304
20305var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20306 // var s = 'Refreshing positions';
20307 // logDebug(s);
20308 var layout = options.layout;
20309 var nodes = options.eles.nodes();
20310 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20311 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20312
20313 if (true !== layoutInfo.ready) {
20314 // s = 'Triggering layoutready';
20315 // logDebug(s);
20316 layoutInfo.ready = true;
20317 layout.one('layoutready', options.ready);
20318 layout.emit({
20319 type: 'layoutready',
20320 layout: this
20321 });
20322 }
20323};
20324/**
20325 * @brief : Logs a debug message in JS console, if DEBUG is ON
20326 */
20327// var logDebug = function(text) {
20328// if (DEBUG) {
20329// console.debug(text);
20330// }
20331// };
20332
20333/**
20334 * @brief : Performs one iteration of the physical simulation
20335 * @arg layoutInfo : LayoutInfo object already initialized
20336 * @arg cy : Cytoscape object
20337 * @arg options : Layout options
20338 */
20339
20340
20341var step$1 = function step(layoutInfo, options, _step) {
20342 // var s = "\n\n###############################";
20343 // s += "\nSTEP: " + step;
20344 // s += "\n###############################\n";
20345 // logDebug(s);
20346 // Calculate node repulsions
20347 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20348
20349 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20350
20351 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20352
20353 propagateForces(layoutInfo); // Update positions based on calculated forces
20354
20355 updatePositions(layoutInfo);
20356};
20357/**
20358 * @brief : Computes the node repulsion forces
20359 */
20360
20361
20362var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20363 // Go through each of the graphs in graphSet
20364 // Nodes only repel each other if they belong to the same graph
20365 // var s = 'calculateNodeForces';
20366 // logDebug(s);
20367 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20368 var graph = layoutInfo.graphSet[i];
20369 var numNodes = graph.length; // s = "Set: " + graph.toString();
20370 // logDebug(s);
20371 // Now get all the pairs of nodes
20372 // Only get each pair once, (A, B) = (B, A)
20373
20374 for (var j = 0; j < numNodes; j++) {
20375 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20376
20377 for (var k = j + 1; k < numNodes; k++) {
20378 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20379 nodeRepulsion(node1, node2, layoutInfo, options);
20380 }
20381 }
20382 }
20383};
20384
20385var randomDistance = function randomDistance(max) {
20386 return -max + 2 * max * Math.random();
20387};
20388/**
20389 * @brief : Compute the node repulsion forces between a pair of nodes
20390 */
20391
20392
20393var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20394 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20395 var cmptId1 = node1.cmptId;
20396 var cmptId2 = node2.cmptId;
20397
20398 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20399 return;
20400 } // Get direction of line connecting both node centers
20401
20402
20403 var directionX = node2.positionX - node1.positionX;
20404 var directionY = node2.positionY - node1.positionY;
20405 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20406 // If both centers are the same, apply a random force
20407
20408 if (0 === directionX && 0 === directionY) {
20409 directionX = randomDistance(maxRandDist);
20410 directionY = randomDistance(maxRandDist);
20411 }
20412
20413 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20414
20415 if (overlap > 0) {
20416 // s += "\nNodes DO overlap.";
20417 // s += "\nOverlap: " + overlap;
20418 // If nodes overlap, repulsion force is proportional
20419 // to the overlap
20420 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20421
20422 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20423
20424 var forceX = force * directionX / distance;
20425 var forceY = force * directionY / distance;
20426 } else {
20427 // s += "\nNodes do NOT overlap.";
20428 // If there's no overlap, force is inversely proportional
20429 // to squared distance
20430 // Get clipping points for both nodes
20431 var point1 = findClippingPoint(node1, directionX, directionY);
20432 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20433
20434 var distanceX = point2.x - point1.x;
20435 var distanceY = point2.y - point1.y;
20436 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20437 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20438 // Compute the module and components of the force vector
20439
20440 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20441 var forceX = force * distanceX / distance;
20442 var forceY = force * distanceY / distance;
20443 } // Apply force
20444
20445
20446 if (!node1.isLocked) {
20447 node1.offsetX -= forceX;
20448 node1.offsetY -= forceY;
20449 }
20450
20451 if (!node2.isLocked) {
20452 node2.offsetX += forceX;
20453 node2.offsetY += forceY;
20454 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20455 // logDebug(s);
20456
20457
20458 return;
20459};
20460/**
20461 * @brief : Determines whether two nodes overlap or not
20462 * @return : Amount of overlapping (0 => no overlap)
20463 */
20464
20465
20466var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20467 if (dX > 0) {
20468 var overlapX = node1.maxX - node2.minX;
20469 } else {
20470 var overlapX = node2.maxX - node1.minX;
20471 }
20472
20473 if (dY > 0) {
20474 var overlapY = node1.maxY - node2.minY;
20475 } else {
20476 var overlapY = node2.maxY - node1.minY;
20477 }
20478
20479 if (overlapX >= 0 && overlapY >= 0) {
20480 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20481 } else {
20482 return 0;
20483 }
20484};
20485/**
20486 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20487 * the rectangular bounding box of it's source/target node
20488 */
20489
20490
20491var findClippingPoint = function findClippingPoint(node, dX, dY) {
20492 // Shorcuts
20493 var X = node.positionX;
20494 var Y = node.positionY;
20495 var H = node.height || 1;
20496 var W = node.width || 1;
20497 var dirSlope = dY / dX;
20498 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20499 // " . Height: " + H + ", Width: " + W +
20500 // "\nDirection " + dX + ", " + dY;
20501 //
20502 // Compute intersection
20503
20504 var res = {}; // Case: Vertical direction (up)
20505
20506 if (0 === dX && 0 < dY) {
20507 res.x = X; // s += "\nUp direction";
20508
20509 res.y = Y + H / 2;
20510 return res;
20511 } // Case: Vertical direction (down)
20512
20513
20514 if (0 === dX && 0 > dY) {
20515 res.x = X;
20516 res.y = Y + H / 2; // s += "\nDown direction";
20517
20518 return res;
20519 } // Case: Intersects the right border
20520
20521
20522 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20523 res.x = X + W / 2;
20524 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20525
20526 return res;
20527 } // Case: Intersects the left border
20528
20529
20530 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20531 res.x = X - W / 2;
20532 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20533
20534 return res;
20535 } // Case: Intersects the top border
20536
20537
20538 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20539 res.x = X + H * dX / 2 / dY;
20540 res.y = Y + H / 2; // s += "\nTop border";
20541
20542 return res;
20543 } // Case: Intersects the bottom border
20544
20545
20546 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20547 res.x = X - H * dX / 2 / dY;
20548 res.y = Y - H / 2; // s += "\nBottom border";
20549
20550 return res;
20551 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20552 // logDebug(s);
20553
20554
20555 return res;
20556};
20557/**
20558 * @brief : Calculates all edge forces
20559 */
20560
20561
20562var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20563 // Iterate over all edges
20564 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20565 // Get edge, source & target nodes
20566 var edge = layoutInfo.layoutEdges[i];
20567 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20568 var source = layoutInfo.layoutNodes[sourceIx];
20569 var targetIx = layoutInfo.idToIndex[edge.targetId];
20570 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20571
20572 var directionX = target.positionX - source.positionX;
20573 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20574 // A random force has already been applied as node repulsion
20575
20576 if (0 === directionX && 0 === directionY) {
20577 continue;
20578 } // Get clipping points for both nodes
20579
20580
20581 var point1 = findClippingPoint(source, directionX, directionY);
20582 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20583 var lx = point2.x - point1.x;
20584 var ly = point2.y - point1.y;
20585 var l = Math.sqrt(lx * lx + ly * ly);
20586 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20587
20588 if (0 !== l) {
20589 var forceX = force * lx / l;
20590 var forceY = force * ly / l;
20591 } else {
20592 var forceX = 0;
20593 var forceY = 0;
20594 } // Add this force to target and source nodes
20595
20596
20597 if (!source.isLocked) {
20598 source.offsetX += forceX;
20599 source.offsetY += forceY;
20600 }
20601
20602 if (!target.isLocked) {
20603 target.offsetX -= forceX;
20604 target.offsetY -= forceY;
20605 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20606 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20607 // logDebug(s);
20608
20609 }
20610};
20611/**
20612 * @brief : Computes gravity forces for all nodes
20613 */
20614
20615
20616var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20617 if (options.gravity === 0) {
20618 return;
20619 }
20620
20621 var distThreshold = 1; // var s = 'calculateGravityForces';
20622 // logDebug(s);
20623
20624 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20625 var graph = layoutInfo.graphSet[i];
20626 var numNodes = graph.length; // s = "Set: " + graph.toString();
20627 // logDebug(s);
20628 // Compute graph center
20629
20630 if (0 === i) {
20631 var centerX = layoutInfo.clientHeight / 2;
20632 var centerY = layoutInfo.clientWidth / 2;
20633 } else {
20634 // Get Parent node for this graph, and use its position as center
20635 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20636 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20637 var centerX = parent.positionX;
20638 var centerY = parent.positionY;
20639 } // s = "Center found at: " + centerX + ", " + centerY;
20640 // logDebug(s);
20641 // Apply force to all nodes in graph
20642
20643
20644 for (var j = 0; j < numNodes; j++) {
20645 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20646
20647 if (node.isLocked) {
20648 continue;
20649 }
20650
20651 var dx = centerX - node.positionX;
20652 var dy = centerY - node.positionY;
20653 var d = Math.sqrt(dx * dx + dy * dy);
20654
20655 if (d > distThreshold) {
20656 var fx = options.gravity * dx / d;
20657 var fy = options.gravity * dy / d;
20658 node.offsetX += fx;
20659 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20660 } // s += ": skypped since it's too close to center";
20661 // logDebug(s);
20662
20663 }
20664 }
20665};
20666/**
20667 * @brief : This function propagates the existing offsets from
20668 * parent nodes to its descendents.
20669 * @arg layoutInfo : layoutInfo Object
20670 * @arg cy : cytoscape Object
20671 * @arg options : Layout options
20672 */
20673
20674
20675var propagateForces = function propagateForces(layoutInfo, options) {
20676 // Inline implementation of a queue, used for traversing the graph in BFS order
20677 var queue = [];
20678 var start = 0; // Points to the start the queue
20679
20680 var end = -1; // Points to the end of the queue
20681 // logDebug('propagateForces');
20682 // Start by visiting the nodes in the root graph
20683
20684 queue.push.apply(queue, layoutInfo.graphSet[0]);
20685 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20686
20687 while (start <= end) {
20688 // Get the node to visit and remove it from queue
20689 var nodeId = queue[start++];
20690 var nodeIndex = layoutInfo.idToIndex[nodeId];
20691 var node = layoutInfo.layoutNodes[nodeIndex];
20692 var children = node.children; // We only need to process the node if it's compound
20693
20694 if (0 < children.length && !node.isLocked) {
20695 var offX = node.offsetX;
20696 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20697 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20698 // s += "\n Children: " + children.toString();
20699 // logDebug(s);
20700
20701 for (var i = 0; i < children.length; i++) {
20702 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20703
20704 childNode.offsetX += offX;
20705 childNode.offsetY += offY; // Add children to queue to be visited
20706
20707 queue[++end] = children[i];
20708 } // Reset parent offsets
20709
20710
20711 node.offsetX = 0;
20712 node.offsetY = 0;
20713 }
20714 }
20715};
20716/**
20717 * @brief : Updates the layout model positions, based on
20718 * the accumulated forces
20719 */
20720
20721
20722var updatePositions = function updatePositions(layoutInfo, options) {
20723 // var s = 'Updating positions';
20724 // logDebug(s);
20725 // Reset boundaries for compound nodes
20726 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20727 var n = layoutInfo.layoutNodes[i];
20728
20729 if (0 < n.children.length) {
20730 // logDebug("Resetting boundaries of compound node: " + n.id);
20731 n.maxX = undefined;
20732 n.minX = undefined;
20733 n.maxY = undefined;
20734 n.minY = undefined;
20735 }
20736 }
20737
20738 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20739 var n = layoutInfo.layoutNodes[i];
20740
20741 if (0 < n.children.length || n.isLocked) {
20742 // No need to set compound or locked node position
20743 // logDebug("Skipping position update of node: " + n.id);
20744 continue;
20745 } // s = "Node: " + n.id + " Previous position: (" +
20746 // n.positionX + ", " + n.positionY + ").";
20747 // Limit displacement in order to improve stability
20748
20749
20750 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20751 n.positionX += tempForce.x;
20752 n.positionY += tempForce.y;
20753 n.offsetX = 0;
20754 n.offsetY = 0;
20755 n.minX = n.positionX - n.width;
20756 n.maxX = n.positionX + n.width;
20757 n.minY = n.positionY - n.height;
20758 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20759 // logDebug(s);
20760 // Update ancestry boudaries
20761
20762 updateAncestryBoundaries(n, layoutInfo);
20763 } // Update size, position of compund nodes
20764
20765
20766 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20767 var n = layoutInfo.layoutNodes[i];
20768
20769 if (0 < n.children.length && !n.isLocked) {
20770 n.positionX = (n.maxX + n.minX) / 2;
20771 n.positionY = (n.maxY + n.minY) / 2;
20772 n.width = n.maxX - n.minX;
20773 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20774 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20775 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20776 // logDebug(s);
20777 }
20778 }
20779};
20780/**
20781 * @brief : Limits a force (forceX, forceY) to be not
20782 * greater (in modulo) than max.
20783 8 Preserves force direction.
20784 */
20785
20786
20787var limitForce = function limitForce(forceX, forceY, max) {
20788 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20789 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20790
20791 if (force > max) {
20792 var res = {
20793 x: max * forceX / force,
20794 y: max * forceY / force
20795 };
20796 } else {
20797 var res = {
20798 x: forceX,
20799 y: forceY
20800 };
20801 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20802 // logDebug(s);
20803
20804
20805 return res;
20806};
20807/**
20808 * @brief : Function used for keeping track of compound node
20809 * sizes, since they should bound all their subnodes.
20810 */
20811
20812
20813var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20814 // var s = "Propagating new position/size of node " + node.id;
20815 var parentId = node.parentId;
20816
20817 if (null == parentId) {
20818 // If there's no parent, we are done
20819 // s += ". No parent node.";
20820 // logDebug(s);
20821 return;
20822 } // Get Parent Node
20823
20824
20825 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20826 var flag = false; // MaxX
20827
20828 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20829 p.maxX = node.maxX + p.padRight;
20830 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20831 } // MinX
20832
20833
20834 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20835 p.minX = node.minX - p.padLeft;
20836 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20837 } // MaxY
20838
20839
20840 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20841 p.maxY = node.maxY + p.padBottom;
20842 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20843 } // MinY
20844
20845
20846 if (null == p.minY || node.minY - p.padTop < p.minY) {
20847 p.minY = node.minY - p.padTop;
20848 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20849 } // If updated boundaries, propagate changes upward
20850
20851
20852 if (flag) {
20853 // logDebug(s);
20854 return updateAncestryBoundaries(p, layoutInfo);
20855 } // s += ". No changes in boundaries/position of parent node " + p.id;
20856 // logDebug(s);
20857
20858
20859 return;
20860};
20861
20862var separateComponents = function separateComponents(layoutInfo, options) {
20863 var nodes = layoutInfo.layoutNodes;
20864 var components = [];
20865
20866 for (var i = 0; i < nodes.length; i++) {
20867 var node = nodes[i];
20868 var cid = node.cmptId;
20869 var component = components[cid] = components[cid] || [];
20870 component.push(node);
20871 }
20872
20873 var totalA = 0;
20874
20875 for (var i = 0; i < components.length; i++) {
20876 var c = components[i];
20877
20878 if (!c) {
20879 continue;
20880 }
20881
20882 c.x1 = Infinity;
20883 c.x2 = -Infinity;
20884 c.y1 = Infinity;
20885 c.y2 = -Infinity;
20886
20887 for (var j = 0; j < c.length; j++) {
20888 var n = c[j];
20889 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20890 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20891 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20892 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20893 }
20894
20895 c.w = c.x2 - c.x1;
20896 c.h = c.y2 - c.y1;
20897 totalA += c.w * c.h;
20898 }
20899
20900 components.sort(function (c1, c2) {
20901 return c2.w * c2.h - c1.w * c1.h;
20902 });
20903 var x = 0;
20904 var y = 0;
20905 var usedW = 0;
20906 var rowH = 0;
20907 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20908
20909 for (var i = 0; i < components.length; i++) {
20910 var c = components[i];
20911
20912 if (!c) {
20913 continue;
20914 }
20915
20916 for (var j = 0; j < c.length; j++) {
20917 var n = c[j];
20918
20919 if (!n.isLocked) {
20920 n.positionX += x - c.x1;
20921 n.positionY += y - c.y1;
20922 }
20923 }
20924
20925 x += c.w + options.componentSpacing;
20926 usedW += c.w + options.componentSpacing;
20927 rowH = Math.max(rowH, c.h);
20928
20929 if (usedW > maxRowW) {
20930 y += rowH + options.componentSpacing;
20931 x = 0;
20932 usedW = 0;
20933 rowH = 0;
20934 }
20935 }
20936};
20937
20938var defaults$d = {
20939 fit: true,
20940 // whether to fit the viewport to the graph
20941 padding: 30,
20942 // padding used on fit
20943 boundingBox: undefined,
20944 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20945 avoidOverlap: true,
20946 // prevents node overlap, may overflow boundingBox if not enough space
20947 avoidOverlapPadding: 10,
20948 // extra spacing around nodes when avoidOverlap: true
20949 nodeDimensionsIncludeLabels: false,
20950 // Excludes the label when calculating node bounding boxes for the layout algorithm
20951 spacingFactor: undefined,
20952 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20953 condense: false,
20954 // uses all available space on false, uses minimal space on true
20955 rows: undefined,
20956 // force num of rows in the grid
20957 cols: undefined,
20958 // force num of columns in the grid
20959 position: function position(node) {},
20960 // returns { row, col } for element
20961 sort: undefined,
20962 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20963 animate: false,
20964 // whether to transition the node positions
20965 animationDuration: 500,
20966 // duration of animation in ms if enabled
20967 animationEasing: undefined,
20968 // easing of animation if enabled
20969 animateFilter: function animateFilter(node, i) {
20970 return true;
20971 },
20972 // 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
20973 ready: undefined,
20974 // callback on layoutready
20975 stop: undefined,
20976 // callback on layoutstop
20977 transform: function transform(node, position) {
20978 return position;
20979 } // transform a given node position. Useful for changing flow direction in discrete layouts
20980
20981};
20982
20983function GridLayout(options) {
20984 this.options = extend({}, defaults$d, options);
20985}
20986
20987GridLayout.prototype.run = function () {
20988 var params = this.options;
20989 var options = params;
20990 var cy = params.cy;
20991 var eles = options.eles;
20992 var nodes = eles.nodes().not(':parent');
20993
20994 if (options.sort) {
20995 nodes = nodes.sort(options.sort);
20996 }
20997
20998 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20999 x1: 0,
21000 y1: 0,
21001 w: cy.width(),
21002 h: cy.height()
21003 });
21004
21005 if (bb.h === 0 || bb.w === 0) {
21006 eles.nodes().layoutPositions(this, options, function (ele) {
21007 return {
21008 x: bb.x1,
21009 y: bb.y1
21010 };
21011 });
21012 } else {
21013 // width/height * splits^2 = cells where splits is number of times to split width
21014 var cells = nodes.size();
21015 var splits = Math.sqrt(cells * bb.h / bb.w);
21016 var rows = Math.round(splits);
21017 var cols = Math.round(bb.w / bb.h * splits);
21018
21019 var small = function small(val) {
21020 if (val == null) {
21021 return Math.min(rows, cols);
21022 } else {
21023 var min = Math.min(rows, cols);
21024
21025 if (min == rows) {
21026 rows = val;
21027 } else {
21028 cols = val;
21029 }
21030 }
21031 };
21032
21033 var large = function large(val) {
21034 if (val == null) {
21035 return Math.max(rows, cols);
21036 } else {
21037 var max = Math.max(rows, cols);
21038
21039 if (max == rows) {
21040 rows = val;
21041 } else {
21042 cols = val;
21043 }
21044 }
21045 };
21046
21047 var oRows = options.rows;
21048 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
21049
21050 if (oRows != null && oCols != null) {
21051 rows = oRows;
21052 cols = oCols;
21053 } else if (oRows != null && oCols == null) {
21054 rows = oRows;
21055 cols = Math.ceil(cells / rows);
21056 } else if (oRows == null && oCols != null) {
21057 cols = oCols;
21058 rows = Math.ceil(cells / cols);
21059 } // otherwise use the automatic values and adjust accordingly
21060 // if rounding was up, see if we can reduce rows or columns
21061 else if (cols * rows > cells) {
21062 var sm = small();
21063 var lg = large(); // reducing the small side takes away the most cells, so try it first
21064
21065 if ((sm - 1) * lg >= cells) {
21066 small(sm - 1);
21067 } else if ((lg - 1) * sm >= cells) {
21068 large(lg - 1);
21069 }
21070 } else {
21071 // if rounding was too low, add rows or columns
21072 while (cols * rows < cells) {
21073 var _sm = small();
21074
21075 var _lg = large(); // try to add to larger side first (adds less in multiplication)
21076
21077
21078 if ((_lg + 1) * _sm >= cells) {
21079 large(_lg + 1);
21080 } else {
21081 small(_sm + 1);
21082 }
21083 }
21084 }
21085
21086 var cellWidth = bb.w / cols;
21087 var cellHeight = bb.h / rows;
21088
21089 if (options.condense) {
21090 cellWidth = 0;
21091 cellHeight = 0;
21092 }
21093
21094 if (options.avoidOverlap) {
21095 for (var i = 0; i < nodes.length; i++) {
21096 var node = nodes[i];
21097 var pos = node._private.position;
21098
21099 if (pos.x == null || pos.y == null) {
21100 // for bb
21101 pos.x = 0;
21102 pos.y = 0;
21103 }
21104
21105 var nbb = node.layoutDimensions(options);
21106 var p = options.avoidOverlapPadding;
21107 var w = nbb.w + p;
21108 var h = nbb.h + p;
21109 cellWidth = Math.max(cellWidth, w);
21110 cellHeight = Math.max(cellHeight, h);
21111 }
21112 }
21113
21114 var cellUsed = {}; // e.g. 'c-0-2' => true
21115
21116 var used = function used(row, col) {
21117 return cellUsed['c-' + row + '-' + col] ? true : false;
21118 };
21119
21120 var use = function use(row, col) {
21121 cellUsed['c-' + row + '-' + col] = true;
21122 }; // to keep track of current cell position
21123
21124
21125 var row = 0;
21126 var col = 0;
21127
21128 var moveToNextCell = function moveToNextCell() {
21129 col++;
21130
21131 if (col >= cols) {
21132 col = 0;
21133 row++;
21134 }
21135 }; // get a cache of all the manual positions
21136
21137
21138 var id2manPos = {};
21139
21140 for (var _i = 0; _i < nodes.length; _i++) {
21141 var _node = nodes[_i];
21142 var rcPos = options.position(_node);
21143
21144 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21145 // must have at least row or col def'd
21146 var _pos = {
21147 row: rcPos.row,
21148 col: rcPos.col
21149 };
21150
21151 if (_pos.col === undefined) {
21152 // find unused col
21153 _pos.col = 0;
21154
21155 while (used(_pos.row, _pos.col)) {
21156 _pos.col++;
21157 }
21158 } else if (_pos.row === undefined) {
21159 // find unused row
21160 _pos.row = 0;
21161
21162 while (used(_pos.row, _pos.col)) {
21163 _pos.row++;
21164 }
21165 }
21166
21167 id2manPos[_node.id()] = _pos;
21168 use(_pos.row, _pos.col);
21169 }
21170 }
21171
21172 var getPos = function getPos(element, i) {
21173 var x, y;
21174
21175 if (element.locked() || element.isParent()) {
21176 return false;
21177 } // see if we have a manual position set
21178
21179
21180 var rcPos = id2manPos[element.id()];
21181
21182 if (rcPos) {
21183 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21184 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21185 } else {
21186 // otherwise set automatically
21187 while (used(row, col)) {
21188 moveToNextCell();
21189 }
21190
21191 x = col * cellWidth + cellWidth / 2 + bb.x1;
21192 y = row * cellHeight + cellHeight / 2 + bb.y1;
21193 use(row, col);
21194 moveToNextCell();
21195 }
21196
21197 return {
21198 x: x,
21199 y: y
21200 };
21201 };
21202
21203 nodes.layoutPositions(this, options, getPos);
21204 }
21205
21206 return this; // chaining
21207};
21208
21209var defaults$e = {
21210 ready: function ready() {},
21211 // on layoutready
21212 stop: function stop() {} // on layoutstop
21213
21214}; // constructor
21215// options : object containing layout options
21216
21217function NullLayout(options) {
21218 this.options = extend({}, defaults$e, options);
21219} // runs the layout
21220
21221
21222NullLayout.prototype.run = function () {
21223 var options = this.options;
21224 var eles = options.eles; // elements to consider in the layout
21225
21226 var layout = this; // cy is automatically populated for us in the constructor
21227 // (disable eslint for next line as this serves as example layout code to external developers)
21228 // eslint-disable-next-line no-unused-vars
21229
21230 var cy = options.cy;
21231 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21232 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21233
21234 eles.nodes().positions(function () {
21235 return {
21236 x: 0,
21237 y: 0
21238 };
21239 }); // trigger layoutready when each node has had its position set at least once
21240
21241 layout.one('layoutready', options.ready);
21242 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21243
21244 layout.one('layoutstop', options.stop);
21245 layout.emit('layoutstop');
21246 return this; // chaining
21247}; // called on continuous layouts to stop them before they finish
21248
21249
21250NullLayout.prototype.stop = function () {
21251 return this; // chaining
21252};
21253
21254var defaults$f = {
21255 positions: undefined,
21256 // map of (node id) => (position obj); or function(node){ return somPos; }
21257 zoom: undefined,
21258 // the zoom level to set (prob want fit = false if set)
21259 pan: undefined,
21260 // the pan level to set (prob want fit = false if set)
21261 fit: true,
21262 // whether to fit to viewport
21263 padding: 30,
21264 // padding on fit
21265 animate: false,
21266 // whether to transition the node positions
21267 animationDuration: 500,
21268 // duration of animation in ms if enabled
21269 animationEasing: undefined,
21270 // easing of animation if enabled
21271 animateFilter: function animateFilter(node, i) {
21272 return true;
21273 },
21274 // 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
21275 ready: undefined,
21276 // callback on layoutready
21277 stop: undefined,
21278 // callback on layoutstop
21279 transform: function transform(node, position) {
21280 return position;
21281 } // transform a given node position. Useful for changing flow direction in discrete layouts
21282
21283};
21284
21285function PresetLayout(options) {
21286 this.options = extend({}, defaults$f, options);
21287}
21288
21289PresetLayout.prototype.run = function () {
21290 var options = this.options;
21291 var eles = options.eles;
21292 var nodes = eles.nodes();
21293 var posIsFn = fn(options.positions);
21294
21295 function getPosition(node) {
21296 if (options.positions == null) {
21297 return copyPosition(node.position());
21298 }
21299
21300 if (posIsFn) {
21301 return options.positions(node);
21302 }
21303
21304 var pos = options.positions[node._private.data.id];
21305
21306 if (pos == null) {
21307 return null;
21308 }
21309
21310 return pos;
21311 }
21312
21313 nodes.layoutPositions(this, options, function (node, i) {
21314 var position = getPosition(node);
21315
21316 if (node.locked() || position == null) {
21317 return false;
21318 }
21319
21320 return position;
21321 });
21322 return this; // chaining
21323};
21324
21325var defaults$g = {
21326 fit: true,
21327 // whether to fit to viewport
21328 padding: 30,
21329 // fit padding
21330 boundingBox: undefined,
21331 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21332 animate: false,
21333 // whether to transition the node positions
21334 animationDuration: 500,
21335 // duration of animation in ms if enabled
21336 animationEasing: undefined,
21337 // easing of animation if enabled
21338 animateFilter: function animateFilter(node, i) {
21339 return true;
21340 },
21341 // 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
21342 ready: undefined,
21343 // callback on layoutready
21344 stop: undefined,
21345 // callback on layoutstop
21346 transform: function transform(node, position) {
21347 return position;
21348 } // transform a given node position. Useful for changing flow direction in discrete layouts
21349
21350};
21351
21352function RandomLayout(options) {
21353 this.options = extend({}, defaults$g, options);
21354}
21355
21356RandomLayout.prototype.run = function () {
21357 var options = this.options;
21358 var cy = options.cy;
21359 var eles = options.eles;
21360 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21361 x1: 0,
21362 y1: 0,
21363 w: cy.width(),
21364 h: cy.height()
21365 });
21366
21367 var getPos = function getPos(node, i) {
21368 return {
21369 x: bb.x1 + Math.round(Math.random() * bb.w),
21370 y: bb.y1 + Math.round(Math.random() * bb.h)
21371 };
21372 };
21373
21374 eles.nodes().layoutPositions(this, options, getPos);
21375 return this; // chaining
21376};
21377
21378var layout = [{
21379 name: 'breadthfirst',
21380 impl: BreadthFirstLayout
21381}, {
21382 name: 'circle',
21383 impl: CircleLayout
21384}, {
21385 name: 'concentric',
21386 impl: ConcentricLayout
21387}, {
21388 name: 'cose',
21389 impl: CoseLayout
21390}, {
21391 name: 'grid',
21392 impl: GridLayout
21393}, {
21394 name: 'null',
21395 impl: NullLayout
21396}, {
21397 name: 'preset',
21398 impl: PresetLayout
21399}, {
21400 name: 'random',
21401 impl: RandomLayout
21402}];
21403
21404function NullRenderer(options) {
21405 this.options = options;
21406 this.notifications = 0; // for testing
21407}
21408
21409var noop$1 = function noop() {};
21410
21411var throwImgErr = function throwImgErr() {
21412 throw new Error('A headless instance can not render images');
21413};
21414
21415NullRenderer.prototype = {
21416 recalculateRenderedStyle: noop$1,
21417 notify: function notify() {
21418 this.notifications++;
21419 },
21420 init: noop$1,
21421 isHeadless: function isHeadless() {
21422 return true;
21423 },
21424 png: throwImgErr,
21425 jpg: throwImgErr
21426};
21427
21428var BRp = {};
21429BRp.arrowShapeWidth = 0.3;
21430
21431BRp.registerArrowShapes = function () {
21432 var arrowShapes = this.arrowShapes = {};
21433 var renderer = this; // Contract for arrow shapes:
21434 // 0, 0 is arrow tip
21435 // (0, 1) is direction towards node
21436 // (1, 0) is right
21437 //
21438 // functional api:
21439 // collide: check x, y in shape
21440 // roughCollide: called before collide, no false negatives
21441 // draw: draw
21442 // spacing: dist(arrowTip, nodeBoundary)
21443 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21444
21445 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21446 var x1 = translation.x - size / 2 - padding;
21447 var x2 = translation.x + size / 2 + padding;
21448 var y1 = translation.y - size / 2 - padding;
21449 var y2 = translation.y + size / 2 + padding;
21450 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21451 return inside;
21452 };
21453
21454 var transform = function transform(x, y, size, angle, translation) {
21455 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21456 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21457 var xScaled = xRotated * size;
21458 var yScaled = yRotated * size;
21459 var xTranslated = xScaled + translation.x;
21460 var yTranslated = yScaled + translation.y;
21461 return {
21462 x: xTranslated,
21463 y: yTranslated
21464 };
21465 };
21466
21467 var transformPoints = function transformPoints(pts, size, angle, translation) {
21468 var retPts = [];
21469
21470 for (var i = 0; i < pts.length; i += 2) {
21471 var x = pts[i];
21472 var y = pts[i + 1];
21473 retPts.push(transform(x, y, size, angle, translation));
21474 }
21475
21476 return retPts;
21477 };
21478
21479 var pointsToArr = function pointsToArr(pts) {
21480 var ret = [];
21481
21482 for (var i = 0; i < pts.length; i++) {
21483 var p = pts[i];
21484 ret.push(p.x, p.y);
21485 }
21486
21487 return ret;
21488 };
21489
21490 var standardGap = function standardGap(edge) {
21491 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21492 };
21493
21494 var defineArrowShape = function defineArrowShape(name, defn) {
21495 if (string(defn)) {
21496 defn = arrowShapes[defn];
21497 }
21498
21499 arrowShapes[name] = extend({
21500 name: name,
21501 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21502 collide: function collide(x, y, size, angle, translation, padding) {
21503 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21504 var inside = pointInsidePolygonPoints(x, y, points);
21505 return inside;
21506 },
21507 roughCollide: bbCollide,
21508 draw: function draw(context, size, angle, translation) {
21509 var points = transformPoints(this.points, size, angle, translation);
21510 renderer.arrowShapeImpl('polygon')(context, points);
21511 },
21512 spacing: function spacing(edge) {
21513 return 0;
21514 },
21515 gap: standardGap
21516 }, defn);
21517 };
21518
21519 defineArrowShape('none', {
21520 collide: falsify,
21521 roughCollide: falsify,
21522 draw: noop,
21523 spacing: zeroify,
21524 gap: zeroify
21525 });
21526 defineArrowShape('triangle', {
21527 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21528 });
21529 defineArrowShape('arrow', 'triangle');
21530 defineArrowShape('triangle-backcurve', {
21531 points: arrowShapes['triangle'].points,
21532 controlPoint: [0, -0.15],
21533 roughCollide: bbCollide,
21534 draw: function draw(context, size, angle, translation, edgeWidth) {
21535 var ptsTrans = transformPoints(this.points, size, angle, translation);
21536 var ctrlPt = this.controlPoint;
21537 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21538 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21539 },
21540 gap: function gap(edge) {
21541 return standardGap(edge) * 0.8;
21542 }
21543 });
21544 defineArrowShape('triangle-tee', {
21545 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21546 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21547 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21548 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21549 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21550 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21551 return inside;
21552 },
21553 draw: function draw(context, size, angle, translation, edgeWidth) {
21554 var triPts = transformPoints(this.points, size, angle, translation);
21555 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21556 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21557 }
21558 });
21559 defineArrowShape('circle-triangle', {
21560 radius: 0.15,
21561 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21562 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21563 var t = translation;
21564 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21565 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21566 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21567 },
21568 draw: function draw(context, size, angle, translation, edgeWidth) {
21569 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21570 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21571 },
21572 spacing: function spacing(edge) {
21573 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21574 }
21575 });
21576 defineArrowShape('triangle-cross', {
21577 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21578 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21579 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21580 0.15, -0.4],
21581 crossLinePts: function crossLinePts(size, edgeWidth) {
21582 // shift points so that the distance between the cross points matches edge width
21583 var p = this.baseCrossLinePts.slice();
21584 var shiftFactor = edgeWidth / size;
21585 var y0 = 3;
21586 var y1 = 5;
21587 p[y0] = p[y0] - shiftFactor;
21588 p[y1] = p[y1] - shiftFactor;
21589 return p;
21590 },
21591 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21592 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21593 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21594 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21595 return inside;
21596 },
21597 draw: function draw(context, size, angle, translation, edgeWidth) {
21598 var triPts = transformPoints(this.points, size, angle, translation);
21599 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21600 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21601 }
21602 });
21603 defineArrowShape('vee', {
21604 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21605 gap: function gap(edge) {
21606 return standardGap(edge) * 0.525;
21607 }
21608 });
21609 defineArrowShape('circle', {
21610 radius: 0.15,
21611 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21612 var t = translation;
21613 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21614 return inside;
21615 },
21616 draw: function draw(context, size, angle, translation, edgeWidth) {
21617 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21618 },
21619 spacing: function spacing(edge) {
21620 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21621 }
21622 });
21623 defineArrowShape('tee', {
21624 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21625 spacing: function spacing(edge) {
21626 return 1;
21627 },
21628 gap: function gap(edge) {
21629 return 1;
21630 }
21631 });
21632 defineArrowShape('square', {
21633 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21634 });
21635 defineArrowShape('diamond', {
21636 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21637 gap: function gap(edge) {
21638 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21639 }
21640 });
21641 defineArrowShape('chevron', {
21642 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21643 gap: function gap(edge) {
21644 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21645 }
21646 });
21647};
21648
21649var BRp$1 = {}; // Project mouse
21650
21651BRp$1.projectIntoViewport = function (clientX, clientY) {
21652 var cy = this.cy;
21653 var offsets = this.findContainerClientCoords();
21654 var offsetLeft = offsets[0];
21655 var offsetTop = offsets[1];
21656 var scale = offsets[4];
21657 var pan = cy.pan();
21658 var zoom = cy.zoom();
21659 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21660 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21661 return [x, y];
21662};
21663
21664BRp$1.findContainerClientCoords = function () {
21665 if (this.containerBB) {
21666 return this.containerBB;
21667 }
21668
21669 var container = this.container;
21670 var rect = container.getBoundingClientRect();
21671 var style = window$1.getComputedStyle(container);
21672
21673 var styleValue = function styleValue(name) {
21674 return parseFloat(style.getPropertyValue(name));
21675 };
21676
21677 var padding = {
21678 left: styleValue('padding-left'),
21679 right: styleValue('padding-right'),
21680 top: styleValue('padding-top'),
21681 bottom: styleValue('padding-bottom')
21682 };
21683 var border = {
21684 left: styleValue('border-left-width'),
21685 right: styleValue('border-right-width'),
21686 top: styleValue('border-top-width'),
21687 bottom: styleValue('border-bottom-width')
21688 };
21689 var clientWidth = container.clientWidth;
21690 var clientHeight = container.clientHeight;
21691 var paddingHor = padding.left + padding.right;
21692 var paddingVer = padding.top + padding.bottom;
21693 var borderHor = border.left + border.right;
21694 var scale = rect.width / (clientWidth + borderHor);
21695 var unscaledW = clientWidth - paddingHor;
21696 var unscaledH = clientHeight - paddingVer;
21697 var left = rect.left + padding.left + border.left;
21698 var top = rect.top + padding.top + border.top;
21699 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21700};
21701
21702BRp$1.invalidateContainerClientCoordsCache = function () {
21703 this.containerBB = null;
21704};
21705
21706BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21707 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21708};
21709
21710BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21711 var self = this;
21712 var r = this;
21713 var eles = r.getCachedZSortedEles();
21714 var near = []; // 1 node max, 1 edge max
21715
21716 var zoom = r.cy.zoom();
21717 var hasCompounds = r.cy.hasCompoundNodes();
21718 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21719 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21720 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21721 var minSqDist = Infinity;
21722 var nearEdge;
21723 var nearNode;
21724
21725 if (interactiveElementsOnly) {
21726 eles = eles.interactive;
21727 }
21728
21729 function addEle(ele, sqDist) {
21730 if (ele.isNode()) {
21731 if (nearNode) {
21732 return; // can't replace node
21733 } else {
21734 nearNode = ele;
21735 near.push(ele);
21736 }
21737 }
21738
21739 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21740 if (nearEdge) {
21741 // then replace existing edge
21742 // can replace only if same z-index
21743 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) {
21744 for (var i = 0; i < near.length; i++) {
21745 if (near[i].isEdge()) {
21746 near[i] = ele;
21747 nearEdge = ele;
21748 minSqDist = sqDist != null ? sqDist : minSqDist;
21749 break;
21750 }
21751 }
21752 }
21753 } else {
21754 near.push(ele);
21755 nearEdge = ele;
21756 minSqDist = sqDist != null ? sqDist : minSqDist;
21757 }
21758 }
21759 }
21760
21761 function checkNode(node) {
21762 var width = node.outerWidth() + 2 * nodeThreshold;
21763 var height = node.outerHeight() + 2 * nodeThreshold;
21764 var hw = width / 2;
21765 var hh = height / 2;
21766 var pos = node.position();
21767
21768 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21769 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21770 ) {
21771 var shape = r.nodeShapes[self.getNodeShape(node)];
21772
21773 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21774 addEle(node, 0);
21775 return true;
21776 }
21777 }
21778 }
21779
21780 function checkEdge(edge) {
21781 var _p = edge._private;
21782 var rs = _p.rscratch;
21783 var styleWidth = edge.pstyle('width').pfValue;
21784 var scale = edge.pstyle('arrow-scale').value;
21785 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21786
21787 var widthSq = width * width;
21788 var width2 = width * 2;
21789 var src = _p.source;
21790 var tgt = _p.target;
21791 var sqDist;
21792
21793 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21794 var pts = rs.allpts;
21795
21796 for (var i = 0; i + 3 < pts.length; i += 2) {
21797 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]))) {
21798 addEle(edge, sqDist);
21799 return true;
21800 }
21801 }
21802 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21803 var pts = rs.allpts;
21804
21805 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21806 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]))) {
21807 addEle(edge, sqDist);
21808 return true;
21809 }
21810 }
21811 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21812
21813
21814 var src = src || _p.source;
21815 var tgt = tgt || _p.target;
21816 var arSize = self.getArrowWidth(styleWidth, scale);
21817 var arrows = [{
21818 name: 'source',
21819 x: rs.arrowStartX,
21820 y: rs.arrowStartY,
21821 angle: rs.srcArrowAngle
21822 }, {
21823 name: 'target',
21824 x: rs.arrowEndX,
21825 y: rs.arrowEndY,
21826 angle: rs.tgtArrowAngle
21827 }, {
21828 name: 'mid-source',
21829 x: rs.midX,
21830 y: rs.midY,
21831 angle: rs.midsrcArrowAngle
21832 }, {
21833 name: 'mid-target',
21834 x: rs.midX,
21835 y: rs.midY,
21836 angle: rs.midtgtArrowAngle
21837 }];
21838
21839 for (var i = 0; i < arrows.length; i++) {
21840 var ar = arrows[i];
21841 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21842 var edgeWidth = edge.pstyle('width').pfValue;
21843
21844 if (shape.roughCollide(x, y, arSize, ar.angle, {
21845 x: ar.x,
21846 y: ar.y
21847 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21848 x: ar.x,
21849 y: ar.y
21850 }, edgeWidth, edgeThreshold)) {
21851 addEle(edge);
21852 return true;
21853 }
21854 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21855
21856
21857 if (hasCompounds && near.length > 0) {
21858 checkNode(src);
21859 checkNode(tgt);
21860 }
21861 }
21862
21863 function preprop(obj, name, pre) {
21864 return getPrefixedProperty(obj, name, pre);
21865 }
21866
21867 function checkLabel(ele, prefix) {
21868 var _p = ele._private;
21869 var th = labelThreshold;
21870 var prefixDash;
21871
21872 if (prefix) {
21873 prefixDash = prefix + '-';
21874 } else {
21875 prefixDash = '';
21876 }
21877
21878 ele.boundingBox();
21879 var bb = _p.labelBounds[prefix || 'main'];
21880 var text = ele.pstyle(prefixDash + 'label').value;
21881 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21882
21883 if (!eventsEnabled || !text) {
21884 return;
21885 }
21886
21887 var lx = preprop(_p.rscratch, 'labelX', prefix);
21888 var ly = preprop(_p.rscratch, 'labelY', prefix);
21889 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21890 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21891 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21892 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21893
21894 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21895
21896 var ly1 = bb.y1 - th - oy;
21897 var ly2 = bb.y2 + th - oy;
21898
21899 if (theta) {
21900 var cos = Math.cos(theta);
21901 var sin = Math.sin(theta);
21902
21903 var rotate = function rotate(x, y) {
21904 x = x - lx;
21905 y = y - ly;
21906 return {
21907 x: x * cos - y * sin + lx,
21908 y: x * sin + y * cos + ly
21909 };
21910 };
21911
21912 var px1y1 = rotate(lx1, ly1);
21913 var px1y2 = rotate(lx1, ly2);
21914 var px2y1 = rotate(lx2, ly1);
21915 var px2y2 = rotate(lx2, ly2);
21916 var points = [// with the margin added after the rotation is applied
21917 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21918
21919 if (pointInsidePolygonPoints(x, y, points)) {
21920 addEle(ele);
21921 return true;
21922 }
21923 } else {
21924 // do a cheaper bb check
21925 if (inBoundingBox(bb, x, y)) {
21926 addEle(ele);
21927 return true;
21928 }
21929 }
21930 }
21931
21932 for (var i = eles.length - 1; i >= 0; i--) {
21933 // reverse order for precedence
21934 var ele = eles[i];
21935
21936 if (ele.isNode()) {
21937 checkNode(ele) || checkLabel(ele);
21938 } else {
21939 // then edge
21940 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21941 }
21942 }
21943
21944 return near;
21945}; // 'Give me everything from this box'
21946
21947
21948BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21949 var eles = this.getCachedZSortedEles().interactive;
21950 var box = [];
21951 var x1c = Math.min(x1, x2);
21952 var x2c = Math.max(x1, x2);
21953 var y1c = Math.min(y1, y2);
21954 var y2c = Math.max(y1, y2);
21955 x1 = x1c;
21956 x2 = x2c;
21957 y1 = y1c;
21958 y2 = y2c;
21959 var boxBb = makeBoundingBox({
21960 x1: x1,
21961 y1: y1,
21962 x2: x2,
21963 y2: y2
21964 });
21965
21966 for (var e = 0; e < eles.length; e++) {
21967 var ele = eles[e];
21968
21969 if (ele.isNode()) {
21970 var node = ele;
21971 var nodeBb = node.boundingBox({
21972 includeNodes: true,
21973 includeEdges: false,
21974 includeLabels: false
21975 });
21976
21977 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21978 box.push(node);
21979 }
21980 } else {
21981 var edge = ele;
21982 var _p = edge._private;
21983 var rs = _p.rscratch;
21984
21985 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21986 continue;
21987 }
21988
21989 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21990 continue;
21991 }
21992
21993 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21994 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21995 var allInside = true;
21996
21997 for (var i = 0; i < pts.length; i++) {
21998 if (!pointInBoundingBox(boxBb, pts[i])) {
21999 allInside = false;
22000 break;
22001 }
22002 }
22003
22004 if (allInside) {
22005 box.push(edge);
22006 }
22007 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22008 box.push(edge);
22009 }
22010 }
22011 }
22012
22013 return box;
22014};
22015
22016var BRp$2 = {};
22017
22018BRp$2.calculateArrowAngles = function (edge) {
22019 var rs = edge._private.rscratch;
22020 var isHaystack = rs.edgeType === 'haystack';
22021 var isBezier = rs.edgeType === 'bezier';
22022 var isMultibezier = rs.edgeType === 'multibezier';
22023 var isSegments = rs.edgeType === 'segments';
22024 var isCompound = rs.edgeType === 'compound';
22025 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
22026
22027 var dispX, dispY;
22028 var startX, startY, endX, endY, midX, midY;
22029
22030 if (isHaystack) {
22031 startX = rs.haystackPts[0];
22032 startY = rs.haystackPts[1];
22033 endX = rs.haystackPts[2];
22034 endY = rs.haystackPts[3];
22035 } else {
22036 startX = rs.arrowStartX;
22037 startY = rs.arrowStartY;
22038 endX = rs.arrowEndX;
22039 endY = rs.arrowEndY;
22040 }
22041
22042 midX = rs.midX;
22043 midY = rs.midY; // source
22044 //
22045
22046 if (isSegments) {
22047 dispX = startX - rs.segpts[0];
22048 dispY = startY - rs.segpts[1];
22049 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22050 var pts = rs.allpts;
22051 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22052 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22053 dispX = startX - bX;
22054 dispY = startY - bY;
22055 } else {
22056 dispX = startX - midX;
22057 dispY = startY - midY;
22058 }
22059
22060 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
22061 //
22062
22063 var midX = rs.midX;
22064 var midY = rs.midY;
22065
22066 if (isHaystack) {
22067 midX = (startX + endX) / 2;
22068 midY = (startY + endY) / 2;
22069 }
22070
22071 dispX = endX - startX;
22072 dispY = endY - startY;
22073
22074 if (isSegments) {
22075 var pts = rs.allpts;
22076
22077 if (pts.length / 2 % 2 === 0) {
22078 var i2 = pts.length / 2;
22079 var i1 = i2 - 2;
22080 dispX = pts[i2] - pts[i1];
22081 dispY = pts[i2 + 1] - pts[i1 + 1];
22082 } else {
22083 var i2 = pts.length / 2 - 1;
22084 var i1 = i2 - 2;
22085 var i3 = i2 + 2;
22086 dispX = pts[i2] - pts[i1];
22087 dispY = pts[i2 + 1] - pts[i1 + 1];
22088 }
22089 } else if (isMultibezier || isCompound || isSelf) {
22090 var pts = rs.allpts;
22091 var cpts = rs.ctrlpts;
22092 var bp0x, bp0y;
22093 var bp1x, bp1y;
22094
22095 if (cpts.length / 2 % 2 === 0) {
22096 var p0 = pts.length / 2 - 1; // startpt
22097
22098 var ic = p0 + 2;
22099 var p1 = ic + 2;
22100 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22101 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22102 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22103 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22104 } else {
22105 var ic = pts.length / 2 - 1; // ctrpt
22106
22107 var p0 = ic - 2; // startpt
22108
22109 var p1 = ic + 2; // endpt
22110
22111 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22112 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22113 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22114 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22115 }
22116
22117 dispX = bp1x - bp0x;
22118 dispY = bp1y - bp0y;
22119 }
22120
22121 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22122 rs.midDispX = dispX;
22123 rs.midDispY = dispY; // mid source
22124 //
22125
22126 dispX *= -1;
22127 dispY *= -1;
22128
22129 if (isSegments) {
22130 var pts = rs.allpts;
22131
22132 if (pts.length / 2 % 2 === 0) ; else {
22133 var i2 = pts.length / 2 - 1;
22134 var i3 = i2 + 2;
22135 dispX = -(pts[i3] - pts[i2]);
22136 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22137 }
22138 }
22139
22140 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22141 //
22142
22143 if (isSegments) {
22144 dispX = endX - rs.segpts[rs.segpts.length - 2];
22145 dispY = endY - rs.segpts[rs.segpts.length - 1];
22146 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22147 var pts = rs.allpts;
22148 var l = pts.length;
22149 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22150 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22151 dispX = endX - bX;
22152 dispY = endY - bY;
22153 } else {
22154 dispX = endX - midX;
22155 dispY = endY - midY;
22156 }
22157
22158 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22159};
22160
22161BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22162 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22163 var cachedVal = cache[edgeWidth + ', ' + scale];
22164
22165 if (cachedVal) {
22166 return cachedVal;
22167 }
22168
22169 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22170 cache[edgeWidth + ', ' + scale] = cachedVal;
22171 return cachedVal;
22172};
22173
22174var BRp$3 = {};
22175
22176BRp$3.findHaystackPoints = function (edges) {
22177 for (var i = 0; i < edges.length; i++) {
22178 var edge = edges[i];
22179 var _p = edge._private;
22180 var rs = _p.rscratch;
22181
22182 if (!rs.haystack) {
22183 var angle = Math.random() * 2 * Math.PI;
22184 rs.source = {
22185 x: Math.cos(angle),
22186 y: Math.sin(angle)
22187 };
22188 angle = Math.random() * 2 * Math.PI;
22189 rs.target = {
22190 x: Math.cos(angle),
22191 y: Math.sin(angle)
22192 };
22193 }
22194
22195 var src = _p.source;
22196 var tgt = _p.target;
22197 var srcPos = src.position();
22198 var tgtPos = tgt.position();
22199 var srcW = src.width();
22200 var tgtW = tgt.width();
22201 var srcH = src.height();
22202 var tgtH = tgt.height();
22203 var radius = edge.pstyle('haystack-radius').value;
22204 var halfRadius = radius / 2; // b/c have to half width/height
22205
22206 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];
22207 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22208 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22209
22210 rs.edgeType = 'haystack';
22211 rs.haystack = true;
22212 this.storeEdgeProjections(edge);
22213 this.calculateArrowAngles(edge);
22214 this.recalculateEdgeLabelProjections(edge);
22215 this.calculateLabelAngles(edge);
22216 }
22217};
22218
22219BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22220 // Segments (multiple straight lines)
22221 var rs = edge._private.rscratch;
22222 var posPts = pairInfo.posPts,
22223 intersectionPts = pairInfo.intersectionPts,
22224 vectorNormInverse = pairInfo.vectorNormInverse;
22225 var edgeDistances = edge.pstyle('edge-distances').value;
22226 var segmentWs = edge.pstyle('segment-weights');
22227 var segmentDs = edge.pstyle('segment-distances');
22228 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22229 rs.edgeType = 'segments';
22230 rs.segpts = [];
22231
22232 for (var s = 0; s < segmentsN; s++) {
22233 var w = segmentWs.pfValue[s];
22234 var d = segmentDs.pfValue[s];
22235 var w1 = 1 - w;
22236 var w2 = w;
22237 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22238 var adjustedMidpt = {
22239 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22240 y: midptPts.y1 * w1 + midptPts.y2 * w2
22241 };
22242 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22243 }
22244};
22245
22246BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22247 // Self-edge
22248 var rs = edge._private.rscratch;
22249 var dirCounts = pairInfo.dirCounts,
22250 srcPos = pairInfo.srcPos;
22251 var ctrlptDists = edge.pstyle('control-point-distances');
22252 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22253 var loopDir = edge.pstyle('loop-direction').pfValue;
22254 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22255 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22256 rs.edgeType = 'self';
22257 var j = i;
22258 var loopDist = stepSize;
22259
22260 if (edgeIsUnbundled) {
22261 j = 0;
22262 loopDist = ctrlptDist;
22263 }
22264
22265 var loopAngle = loopDir - Math.PI / 2;
22266 var outAngle = loopAngle - loopSwp / 2;
22267 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22268
22269 var dc = String(loopDir + '_' + loopSwp);
22270 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22271 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)];
22272};
22273
22274BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22275 // Compound edge
22276 var rs = edge._private.rscratch;
22277 rs.edgeType = 'compound';
22278 var srcPos = pairInfo.srcPos,
22279 tgtPos = pairInfo.tgtPos,
22280 srcW = pairInfo.srcW,
22281 srcH = pairInfo.srcH,
22282 tgtW = pairInfo.tgtW,
22283 tgtH = pairInfo.tgtH;
22284 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22285 var ctrlptDists = edge.pstyle('control-point-distances');
22286 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22287 var j = i;
22288 var loopDist = stepSize;
22289
22290 if (edgeIsUnbundled) {
22291 j = 0;
22292 loopDist = ctrlptDist;
22293 }
22294
22295 var loopW = 50;
22296 var loopaPos = {
22297 x: srcPos.x - srcW / 2,
22298 y: srcPos.y - srcH / 2
22299 };
22300 var loopbPos = {
22301 x: tgtPos.x - tgtW / 2,
22302 y: tgtPos.y - tgtH / 2
22303 };
22304 var loopPos = {
22305 x: Math.min(loopaPos.x, loopbPos.x),
22306 y: Math.min(loopaPos.y, loopbPos.y)
22307 }; // avoids cases with impossible beziers
22308
22309 var minCompoundStretch = 0.5;
22310 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22311 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22312 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];
22313};
22314
22315BRp$3.findStraightEdgePoints = function (edge) {
22316 // Straight edge within bundle
22317 edge._private.rscratch.edgeType = 'straight';
22318};
22319
22320BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22321 var rs = edge._private.rscratch;
22322 var vectorNormInverse = pairInfo.vectorNormInverse,
22323 posPts = pairInfo.posPts,
22324 intersectionPts = pairInfo.intersectionPts;
22325 var edgeDistances = edge.pstyle('edge-distances').value;
22326 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22327 var ctrlptDists = edge.pstyle('control-point-distances');
22328 var ctrlptWs = edge.pstyle('control-point-weights');
22329 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22330 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22331 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22332
22333 var multi = edgeIsUnbundled;
22334 rs.edgeType = multi ? 'multibezier' : 'bezier';
22335 rs.ctrlpts = [];
22336
22337 for (var b = 0; b < bezierN; b++) {
22338 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22339 var manctrlptDist = void 0;
22340 var sign = signum(normctrlptDist);
22341
22342 if (multi) {
22343 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22344
22345 ctrlptWeight = ctrlptWs.value[b];
22346 }
22347
22348 if (edgeIsUnbundled) {
22349 // multi or single unbundled
22350 manctrlptDist = ctrlptDist;
22351 } else {
22352 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22353 }
22354
22355 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22356 var w1 = 1 - ctrlptWeight;
22357 var w2 = ctrlptWeight;
22358 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22359 var adjustedMidpt = {
22360 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22361 y: midptPts.y1 * w1 + midptPts.y2 * w2
22362 };
22363 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22364 }
22365};
22366
22367BRp$3.findTaxiPoints = function (edge, pairInfo) {
22368 // Taxicab geometry with two turns maximum
22369 var rs = edge._private.rscratch;
22370 rs.edgeType = 'segments';
22371 var VERTICAL = 'vertical';
22372 var HORIZONTAL = 'horizontal';
22373 var LEFTWARD = 'leftward';
22374 var RIGHTWARD = 'rightward';
22375 var DOWNWARD = 'downward';
22376 var UPWARD = 'upward';
22377 var AUTO = 'auto';
22378 var posPts = pairInfo.posPts,
22379 srcW = pairInfo.srcW,
22380 srcH = pairInfo.srcH,
22381 tgtW = pairInfo.tgtW,
22382 tgtH = pairInfo.tgtH;
22383 var edgeDistances = edge.pstyle('edge-distances').value;
22384 var dIncludesNodeBody = edgeDistances !== 'node-position';
22385 var taxiDir = edge.pstyle('taxi-direction').value;
22386 var rawTaxiDir = taxiDir; // unprocessed value
22387
22388 var taxiTurn = edge.pstyle('taxi-turn');
22389 var turnIsPercent = taxiTurn.units === '%';
22390 var taxiTurnPfVal = taxiTurn.pfValue;
22391 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22392
22393 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22394 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22395 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22396 var pdx = posPts.x2 - posPts.x1;
22397 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22398
22399 var subDWH = function subDWH(dxy, dwh) {
22400 if (dxy > 0) {
22401 return Math.max(dxy - dwh, 0);
22402 } else {
22403 return Math.min(dxy + dwh, 0);
22404 }
22405 };
22406
22407 var dx = subDWH(pdx, dw);
22408 var dy = subDWH(pdy, dh);
22409 var isExplicitDir = false;
22410
22411 if (rawTaxiDir === AUTO) {
22412 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22413 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22414 taxiDir = VERTICAL;
22415 isExplicitDir = true;
22416 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22417 taxiDir = HORIZONTAL;
22418 isExplicitDir = true;
22419 }
22420
22421 var isVert = taxiDir === VERTICAL;
22422 var l = isVert ? dy : dx;
22423 var pl = isVert ? pdy : pdx;
22424 var sgnL = signum(pl);
22425 var forcedDir = false;
22426
22427 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22428 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22429 sgnL *= -1;
22430 l = sgnL * Math.abs(l);
22431 forcedDir = true;
22432 }
22433
22434 var d;
22435
22436 if (turnIsPercent) {
22437 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22438 d = p * l;
22439 } else {
22440 var k = taxiTurnPfVal < 0 ? l : 0;
22441 d = k + taxiTurnPfVal * sgnL;
22442 }
22443
22444 var getIsTooClose = function getIsTooClose(d) {
22445 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22446 };
22447
22448 var isTooCloseSrc = getIsTooClose(d);
22449 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22450 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22451
22452 if (isTooClose && !forcedDir) {
22453 // non-ideal routing
22454 if (isVert) {
22455 // vertical fallbacks
22456 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22457 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22458
22459 if (lShapeInsideSrc) {
22460 // horizontal Z-shape (direction not respected)
22461 var x = (posPts.x1 + posPts.x2) / 2;
22462 var y1 = posPts.y1,
22463 y2 = posPts.y2;
22464 rs.segpts = [x, y1, x, y2];
22465 } else if (lShapeInsideTgt) {
22466 // vertical Z-shape (distance not respected)
22467 var y = (posPts.y1 + posPts.y2) / 2;
22468 var x1 = posPts.x1,
22469 x2 = posPts.x2;
22470 rs.segpts = [x1, y, x2, y];
22471 } else {
22472 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22473 rs.segpts = [posPts.x1, posPts.y2];
22474 }
22475 } else {
22476 // horizontal fallbacks
22477 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22478
22479 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22480
22481 if (_lShapeInsideSrc) {
22482 // vertical Z-shape (direction not respected)
22483 var _y = (posPts.y1 + posPts.y2) / 2;
22484
22485 var _x = posPts.x1,
22486 _x2 = posPts.x2;
22487 rs.segpts = [_x, _y, _x2, _y];
22488 } else if (_lShapeInsideTgt) {
22489 // horizontal Z-shape (turn distance not respected)
22490 var _x3 = (posPts.x1 + posPts.x2) / 2;
22491
22492 var _y2 = posPts.y1,
22493 _y3 = posPts.y2;
22494 rs.segpts = [_x3, _y2, _x3, _y3];
22495 } else {
22496 // L-shape (turn distance not respected, but works well for tree siblings)
22497 rs.segpts = [posPts.x2, posPts.y1];
22498 }
22499 }
22500 } else {
22501 // ideal routing
22502 if (isVert) {
22503 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22504
22505 var _x4 = posPts.x1,
22506 _x5 = posPts.x2;
22507 rs.segpts = [_x4, _y4, _x5, _y4];
22508 } else {
22509 // horizontal
22510 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22511
22512 var _y5 = posPts.y1,
22513 _y6 = posPts.y2;
22514 rs.segpts = [_x6, _y5, _x6, _y6];
22515 }
22516 }
22517};
22518
22519BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22520 var rs = edge._private.rscratch; // can only correct beziers for now...
22521
22522 if (rs.edgeType === 'bezier') {
22523 var srcPos = pairInfo.srcPos,
22524 tgtPos = pairInfo.tgtPos,
22525 srcW = pairInfo.srcW,
22526 srcH = pairInfo.srcH,
22527 tgtW = pairInfo.tgtW,
22528 tgtH = pairInfo.tgtH,
22529 srcShape = pairInfo.srcShape,
22530 tgtShape = pairInfo.tgtShape;
22531 var badStart = !number(rs.startX) || !number(rs.startY);
22532 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22533 var badEnd = !number(rs.endX) || !number(rs.endY);
22534 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22535 var minCpADistFactor = 3;
22536 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22537 var minCpADist = minCpADistFactor * arrowW;
22538 var startACpDist = dist({
22539 x: rs.ctrlpts[0],
22540 y: rs.ctrlpts[1]
22541 }, {
22542 x: rs.startX,
22543 y: rs.startY
22544 });
22545 var closeStartACp = startACpDist < minCpADist;
22546 var endACpDist = dist({
22547 x: rs.ctrlpts[0],
22548 y: rs.ctrlpts[1]
22549 }, {
22550 x: rs.endX,
22551 y: rs.endY
22552 });
22553 var closeEndACp = endACpDist < minCpADist;
22554 var overlapping = false;
22555
22556 if (badStart || badAStart || closeStartACp) {
22557 overlapping = true; // project control point along line from src centre to outside the src shape
22558 // (otherwise intersection will yield nothing)
22559
22560 var cpD = {
22561 // delta
22562 x: rs.ctrlpts[0] - srcPos.x,
22563 y: rs.ctrlpts[1] - srcPos.y
22564 };
22565 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22566
22567 var cpM = {
22568 // normalised delta
22569 x: cpD.x / cpL,
22570 y: cpD.y / cpL
22571 };
22572 var radius = Math.max(srcW, srcH);
22573 var cpProj = {
22574 // *2 radius guarantees outside shape
22575 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22576 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22577 };
22578 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22579
22580 if (closeStartACp) {
22581 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22582 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22583 } else {
22584 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22585 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22586 }
22587 }
22588
22589 if (badEnd || badAEnd || closeEndACp) {
22590 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22591 // (otherwise intersection will yield nothing)
22592
22593 var _cpD = {
22594 // delta
22595 x: rs.ctrlpts[0] - tgtPos.x,
22596 y: rs.ctrlpts[1] - tgtPos.y
22597 };
22598
22599 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22600
22601
22602 var _cpM = {
22603 // normalised delta
22604 x: _cpD.x / _cpL,
22605 y: _cpD.y / _cpL
22606 };
22607
22608 var _radius = Math.max(srcW, srcH);
22609
22610 var _cpProj = {
22611 // *2 radius guarantees outside shape
22612 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22613 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22614 };
22615 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22616
22617 if (closeEndACp) {
22618 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22619 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22620 } else {
22621 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22622 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22623 }
22624 }
22625
22626 if (overlapping) {
22627 // recalc endpts
22628 this.findEndpoints(edge);
22629 }
22630 }
22631};
22632
22633BRp$3.storeAllpts = function (edge) {
22634 var rs = edge._private.rscratch;
22635
22636 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22637 rs.allpts = [];
22638 rs.allpts.push(rs.startX, rs.startY);
22639
22640 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22641 // ctrl pt itself
22642 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22643
22644 if (b + 3 < rs.ctrlpts.length) {
22645 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22646 }
22647 }
22648
22649 rs.allpts.push(rs.endX, rs.endY);
22650 var m, mt;
22651
22652 if (rs.ctrlpts.length / 2 % 2 === 0) {
22653 m = rs.allpts.length / 2 - 1;
22654 rs.midX = rs.allpts[m];
22655 rs.midY = rs.allpts[m + 1];
22656 } else {
22657 m = rs.allpts.length / 2 - 3;
22658 mt = 0.5;
22659 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22660 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22661 }
22662 } else if (rs.edgeType === 'straight') {
22663 // need to calc these after endpts
22664 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22665
22666 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22667 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22668 } else if (rs.edgeType === 'segments') {
22669 rs.allpts = [];
22670 rs.allpts.push(rs.startX, rs.startY);
22671 rs.allpts.push.apply(rs.allpts, rs.segpts);
22672 rs.allpts.push(rs.endX, rs.endY);
22673
22674 if (rs.segpts.length % 4 === 0) {
22675 var i2 = rs.segpts.length / 2;
22676 var i1 = i2 - 2;
22677 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22678 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22679 } else {
22680 var _i = rs.segpts.length / 2 - 1;
22681
22682 rs.midX = rs.segpts[_i];
22683 rs.midY = rs.segpts[_i + 1];
22684 }
22685 }
22686};
22687
22688BRp$3.checkForInvalidEdgeWarning = function (edge) {
22689 var rs = edge[0]._private.rscratch;
22690
22691 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22692 rs.loggedErr = false;
22693 } else {
22694 if (!rs.loggedErr) {
22695 rs.loggedErr = true;
22696 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.');
22697 }
22698 }
22699};
22700
22701BRp$3.findEdgeControlPoints = function (edges) {
22702 var _this = this;
22703
22704 if (!edges || edges.length === 0) {
22705 return;
22706 }
22707
22708 var r = this;
22709 var cy = r.cy;
22710 var hasCompounds = cy.hasCompoundNodes();
22711 var hashTable = {
22712 map: new Map$1(),
22713 get: function get(pairId) {
22714 var map2 = this.map.get(pairId[0]);
22715
22716 if (map2 != null) {
22717 return map2.get(pairId[1]);
22718 } else {
22719 return null;
22720 }
22721 },
22722 set: function set(pairId, val) {
22723 var map2 = this.map.get(pairId[0]);
22724
22725 if (map2 == null) {
22726 map2 = new Map$1();
22727 this.map.set(pairId[0], map2);
22728 }
22729
22730 map2.set(pairId[1], val);
22731 }
22732 };
22733 var pairIds = [];
22734 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22735
22736 for (var i = 0; i < edges.length; i++) {
22737 var edge = edges[i];
22738 var _p = edge._private;
22739 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22740 // they shouldn't take up space
22741
22742 if (edge.removed() || !edge.takesUpSpace()) {
22743 continue;
22744 }
22745
22746 if (curveStyle === 'haystack') {
22747 haystackEdges.push(edge);
22748 continue;
22749 }
22750
22751 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'straight-triangle' || curveStyle === 'taxi';
22752 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22753 var src = _p.source;
22754 var tgt = _p.target;
22755 var srcIndex = src.poolIndex();
22756 var tgtIndex = tgt.poolIndex();
22757 var pairId = [srcIndex, tgtIndex].sort();
22758 var tableEntry = hashTable.get(pairId);
22759
22760 if (tableEntry == null) {
22761 tableEntry = {
22762 eles: []
22763 };
22764 hashTable.set(pairId, tableEntry);
22765 pairIds.push(pairId);
22766 }
22767
22768 tableEntry.eles.push(edge);
22769
22770 if (edgeIsUnbundled) {
22771 tableEntry.hasUnbundled = true;
22772 }
22773
22774 if (edgeIsBezier) {
22775 tableEntry.hasBezier = true;
22776 }
22777 } // for each pair (src, tgt), create the ctrl pts
22778 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22779
22780
22781 var _loop = function _loop(p) {
22782 var pairId = pairIds[p];
22783 var pairInfo = hashTable.get(pairId);
22784 var swappedpairInfo = void 0;
22785
22786 if (!pairInfo.hasUnbundled) {
22787 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22788 return e.isBundledBezier();
22789 });
22790 clearArray(pairInfo.eles);
22791 pllEdges.forEach(function (edge) {
22792 return pairInfo.eles.push(edge);
22793 }); // for each pair id, the edges should be sorted by index
22794
22795 pairInfo.eles.sort(function (edge1, edge2) {
22796 return edge1.poolIndex() - edge2.poolIndex();
22797 });
22798 }
22799
22800 var firstEdge = pairInfo.eles[0];
22801 var src = firstEdge.source();
22802 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22803
22804 if (src.poolIndex() > tgt.poolIndex()) {
22805 var temp = src;
22806 src = tgt;
22807 tgt = temp;
22808 }
22809
22810 var srcPos = pairInfo.srcPos = src.position();
22811 var tgtPos = pairInfo.tgtPos = tgt.position();
22812 var srcW = pairInfo.srcW = src.outerWidth();
22813 var srcH = pairInfo.srcH = src.outerHeight();
22814 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22815 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22816
22817 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22818
22819 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22820
22821 pairInfo.dirCounts = {
22822 'north': 0,
22823 'west': 0,
22824 'south': 0,
22825 'east': 0,
22826 'northwest': 0,
22827 'southwest': 0,
22828 'northeast': 0,
22829 'southeast': 0
22830 };
22831
22832 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22833 var _edge = pairInfo.eles[_i2];
22834 var rs = _edge[0]._private.rscratch;
22835
22836 var _curveStyle = _edge.pstyle('curve-style').value;
22837
22838 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22839
22840
22841 var edgeIsSwapped = !src.same(_edge.source());
22842
22843 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22844 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22845
22846 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22847 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22848
22849 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22850 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22851 var intersectionPts = pairInfo.intersectionPts = {
22852 x1: srcOutside[0],
22853 x2: tgtOutside[0],
22854 y1: srcOutside[1],
22855 y2: tgtOutside[1]
22856 };
22857 var posPts = pairInfo.posPts = {
22858 x1: srcPos.x,
22859 x2: tgtPos.x,
22860 y1: srcPos.y,
22861 y2: tgtPos.y
22862 };
22863 var dy = tgtOutside[1] - srcOutside[1];
22864 var dx = tgtOutside[0] - srcOutside[0];
22865 var l = Math.sqrt(dx * dx + dy * dy);
22866 var vector = pairInfo.vector = {
22867 x: dx,
22868 y: dy
22869 };
22870 var vectorNorm = pairInfo.vectorNorm = {
22871 x: vector.x / l,
22872 y: vector.y / l
22873 };
22874 var vectorNormInverse = {
22875 x: -vectorNorm.y,
22876 y: vectorNorm.x
22877 }; // if node shapes overlap, then no ctrl pts to draw
22878
22879 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);
22880 pairInfo.vectorNormInverse = vectorNormInverse;
22881 swappedpairInfo = {
22882 nodesOverlap: pairInfo.nodesOverlap,
22883 dirCounts: pairInfo.dirCounts,
22884 calculatedIntersection: true,
22885 hasBezier: pairInfo.hasBezier,
22886 hasUnbundled: pairInfo.hasUnbundled,
22887 eles: pairInfo.eles,
22888 srcPos: tgtPos,
22889 tgtPos: srcPos,
22890 srcW: tgtW,
22891 srcH: tgtH,
22892 tgtW: srcW,
22893 tgtH: srcH,
22894 srcIntn: tgtIntn,
22895 tgtIntn: srcIntn,
22896 srcShape: tgtShape,
22897 tgtShape: srcShape,
22898 posPts: {
22899 x1: posPts.x2,
22900 y1: posPts.y2,
22901 x2: posPts.x1,
22902 y2: posPts.y1
22903 },
22904 intersectionPts: {
22905 x1: intersectionPts.x2,
22906 y1: intersectionPts.y2,
22907 x2: intersectionPts.x1,
22908 y2: intersectionPts.y1
22909 },
22910 vector: {
22911 x: -vector.x,
22912 y: -vector.y
22913 },
22914 vectorNorm: {
22915 x: -vectorNorm.x,
22916 y: -vectorNorm.y
22917 },
22918 vectorNormInverse: {
22919 x: -vectorNormInverse.x,
22920 y: -vectorNormInverse.y
22921 }
22922 };
22923 }
22924
22925 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22926 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22927 rs.srcIntn = passedPairInfo.srcIntn;
22928 rs.tgtIntn = passedPairInfo.tgtIntn;
22929
22930 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22931 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22932 } else if (src === tgt) {
22933 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22934 } else if (_curveStyle === 'segments') {
22935 _this.findSegmentsPoints(_edge, passedPairInfo);
22936 } else if (_curveStyle === 'taxi') {
22937 _this.findTaxiPoints(_edge, passedPairInfo);
22938 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22939 _this.findStraightEdgePoints(_edge);
22940 } else {
22941 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22942 }
22943
22944 _this.findEndpoints(_edge);
22945
22946 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22947
22948 _this.checkForInvalidEdgeWarning(_edge);
22949
22950 _this.storeAllpts(_edge);
22951
22952 _this.storeEdgeProjections(_edge);
22953
22954 _this.calculateArrowAngles(_edge);
22955
22956 _this.recalculateEdgeLabelProjections(_edge);
22957
22958 _this.calculateLabelAngles(_edge);
22959 } // for pair edges
22960
22961 };
22962
22963 for (var p = 0; p < pairIds.length; p++) {
22964 _loop(p);
22965 } // for pair ids
22966 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22967
22968
22969 this.findHaystackPoints(haystackEdges);
22970};
22971
22972function getPts(pts) {
22973 var retPts = [];
22974
22975 if (pts == null) {
22976 return;
22977 }
22978
22979 for (var i = 0; i < pts.length; i += 2) {
22980 var x = pts[i];
22981 var y = pts[i + 1];
22982 retPts.push({
22983 x: x,
22984 y: y
22985 });
22986 }
22987
22988 return retPts;
22989}
22990
22991BRp$3.getSegmentPoints = function (edge) {
22992 var rs = edge[0]._private.rscratch;
22993 var type = rs.edgeType;
22994
22995 if (type === 'segments') {
22996 this.recalculateRenderedStyle(edge);
22997 return getPts(rs.segpts);
22998 }
22999};
23000
23001BRp$3.getControlPoints = function (edge) {
23002 var rs = edge[0]._private.rscratch;
23003 var type = rs.edgeType;
23004
23005 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23006 this.recalculateRenderedStyle(edge);
23007 return getPts(rs.ctrlpts);
23008 }
23009};
23010
23011BRp$3.getEdgeMidpoint = function (edge) {
23012 var rs = edge[0]._private.rscratch;
23013 this.recalculateRenderedStyle(edge);
23014 return {
23015 x: rs.midX,
23016 y: rs.midY
23017 };
23018};
23019
23020var BRp$4 = {};
23021
23022BRp$4.manualEndptToPx = function (node, prop) {
23023 var r = this;
23024 var npos = node.position();
23025 var w = node.outerWidth();
23026 var h = node.outerHeight();
23027
23028 if (prop.value.length === 2) {
23029 var p = [prop.pfValue[0], prop.pfValue[1]];
23030
23031 if (prop.units[0] === '%') {
23032 p[0] = p[0] * w;
23033 }
23034
23035 if (prop.units[1] === '%') {
23036 p[1] = p[1] * h;
23037 }
23038
23039 p[0] += npos.x;
23040 p[1] += npos.y;
23041 return p;
23042 } else {
23043 var angle = prop.pfValue[0];
23044 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23045
23046 var l = 2 * Math.max(w, h);
23047 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23048 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
23049 }
23050};
23051
23052BRp$4.findEndpoints = function (edge) {
23053 var r = this;
23054 var intersect;
23055 var source = edge.source()[0];
23056 var target = edge.target()[0];
23057 var srcPos = source.position();
23058 var tgtPos = target.position();
23059 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23060 var srcArShape = edge.pstyle('source-arrow-shape').value;
23061 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23062 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23063 var curveStyle = edge.pstyle('curve-style').value;
23064 var rs = edge._private.rscratch;
23065 var et = rs.edgeType;
23066 var taxi = curveStyle === 'taxi';
23067 var self = et === 'self' || et === 'compound';
23068 var bezier = et === 'bezier' || et === 'multibezier' || self;
23069 var multi = et !== 'bezier';
23070 var lines = et === 'straight' || et === 'segments';
23071 var segments = et === 'segments';
23072 var hasEndpts = bezier || multi || lines;
23073 var overrideEndpts = self || taxi;
23074 var srcManEndpt = edge.pstyle('source-endpoint');
23075 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23076 var tgtManEndpt = edge.pstyle('target-endpoint');
23077 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23078 rs.srcManEndpt = srcManEndpt;
23079 rs.tgtManEndpt = tgtManEndpt;
23080 var p1; // last known point of edge on target side
23081
23082 var p2; // last known point of edge on source side
23083
23084 var p1_i; // point to intersect with target shape
23085
23086 var p2_i; // point to intersect with source shape
23087
23088 if (bezier) {
23089 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23090 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23091 p1 = cpEnd;
23092 p2 = cpStart;
23093 } else if (lines) {
23094 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23095 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23096 p1 = tgtArrowFromPt;
23097 p2 = srcArrowFromPt;
23098 }
23099
23100 if (tgtManEndptVal === 'inside-to-node') {
23101 intersect = [tgtPos.x, tgtPos.y];
23102 } else if (tgtManEndpt.units) {
23103 intersect = this.manualEndptToPx(target, tgtManEndpt);
23104 } else if (tgtManEndptVal === 'outside-to-line') {
23105 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23106 } else {
23107 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23108 p1_i = p1;
23109 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23110 p1_i = [srcPos.x, srcPos.y];
23111 }
23112
23113 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
23114
23115 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23116 var trs = target._private.rscratch;
23117 var lw = trs.labelWidth;
23118 var lh = trs.labelHeight;
23119 var lx = trs.labelX;
23120 var ly = trs.labelY;
23121 var lw2 = lw / 2;
23122 var lh2 = lh / 2;
23123 var va = target.pstyle('text-valign').value;
23124
23125 if (va === 'top') {
23126 ly -= lh2;
23127 } else if (va === 'bottom') {
23128 ly += lh2;
23129 }
23130
23131 var ha = target.pstyle('text-halign').value;
23132
23133 if (ha === 'left') {
23134 lx -= lw2;
23135 } else if (ha === 'right') {
23136 lx += lw2;
23137 }
23138
23139 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);
23140
23141 if (labelIntersect.length > 0) {
23142 var refPt = srcPos;
23143 var intSqdist = sqdist(refPt, array2point(intersect));
23144 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23145 var minSqDist = intSqdist;
23146
23147 if (labIntSqdist < intSqdist) {
23148 intersect = labelIntersect;
23149 minSqDist = labIntSqdist;
23150 }
23151
23152 if (labelIntersect.length > 2) {
23153 var labInt2SqDist = sqdist(refPt, {
23154 x: labelIntersect[2],
23155 y: labelIntersect[3]
23156 });
23157
23158 if (labInt2SqDist < minSqDist) {
23159 intersect = [labelIntersect[2], labelIntersect[3]];
23160 }
23161 }
23162 }
23163 }
23164 }
23165
23166 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23167 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23168 rs.endX = edgeEnd[0];
23169 rs.endY = edgeEnd[1];
23170 rs.arrowEndX = arrowEnd[0];
23171 rs.arrowEndY = arrowEnd[1];
23172
23173 if (srcManEndptVal === 'inside-to-node') {
23174 intersect = [srcPos.x, srcPos.y];
23175 } else if (srcManEndpt.units) {
23176 intersect = this.manualEndptToPx(source, srcManEndpt);
23177 } else if (srcManEndptVal === 'outside-to-line') {
23178 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23179 } else {
23180 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23181 p2_i = p2;
23182 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23183 p2_i = [tgtPos.x, tgtPos.y];
23184 }
23185
23186 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23187
23188 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23189 var srs = source._private.rscratch;
23190 var _lw = srs.labelWidth;
23191 var _lh = srs.labelHeight;
23192 var _lx = srs.labelX;
23193 var _ly = srs.labelY;
23194
23195 var _lw2 = _lw / 2;
23196
23197 var _lh2 = _lh / 2;
23198
23199 var _va = source.pstyle('text-valign').value;
23200
23201 if (_va === 'top') {
23202 _ly -= _lh2;
23203 } else if (_va === 'bottom') {
23204 _ly += _lh2;
23205 }
23206
23207 var _ha = source.pstyle('text-halign').value;
23208
23209 if (_ha === 'left') {
23210 _lx -= _lw2;
23211 } else if (_ha === 'right') {
23212 _lx += _lw2;
23213 }
23214
23215 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);
23216
23217 if (_labelIntersect.length > 0) {
23218 var _refPt = tgtPos;
23219
23220 var _intSqdist = sqdist(_refPt, array2point(intersect));
23221
23222 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23223
23224 var _minSqDist = _intSqdist;
23225
23226 if (_labIntSqdist < _intSqdist) {
23227 intersect = [_labelIntersect[0], _labelIntersect[1]];
23228 _minSqDist = _labIntSqdist;
23229 }
23230
23231 if (_labelIntersect.length > 2) {
23232 var _labInt2SqDist = sqdist(_refPt, {
23233 x: _labelIntersect[2],
23234 y: _labelIntersect[3]
23235 });
23236
23237 if (_labInt2SqDist < _minSqDist) {
23238 intersect = [_labelIntersect[2], _labelIntersect[3]];
23239 }
23240 }
23241 }
23242 }
23243 }
23244
23245 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23246 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23247 rs.startX = edgeStart[0];
23248 rs.startY = edgeStart[1];
23249 rs.arrowStartX = arrowStart[0];
23250 rs.arrowStartY = arrowStart[1];
23251
23252 if (hasEndpts) {
23253 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23254 rs.badLine = true;
23255 } else {
23256 rs.badLine = false;
23257 }
23258 }
23259};
23260
23261BRp$4.getSourceEndpoint = function (edge) {
23262 var rs = edge[0]._private.rscratch;
23263 this.recalculateRenderedStyle(edge);
23264
23265 switch (rs.edgeType) {
23266 case 'haystack':
23267 return {
23268 x: rs.haystackPts[0],
23269 y: rs.haystackPts[1]
23270 };
23271
23272 default:
23273 return {
23274 x: rs.arrowStartX,
23275 y: rs.arrowStartY
23276 };
23277 }
23278};
23279
23280BRp$4.getTargetEndpoint = function (edge) {
23281 var rs = edge[0]._private.rscratch;
23282 this.recalculateRenderedStyle(edge);
23283
23284 switch (rs.edgeType) {
23285 case 'haystack':
23286 return {
23287 x: rs.haystackPts[2],
23288 y: rs.haystackPts[3]
23289 };
23290
23291 default:
23292 return {
23293 x: rs.arrowEndX,
23294 y: rs.arrowEndY
23295 };
23296 }
23297};
23298
23299var BRp$5 = {};
23300
23301function pushBezierPts(r, edge, pts) {
23302 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23303 return qbezierAt(p1, p2, p3, t);
23304 };
23305
23306 var _p = edge._private;
23307 var bpts = _p.rstyle.bezierPts;
23308
23309 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23310 var p = r.bezierProjPcts[i];
23311 bpts.push({
23312 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23313 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23314 });
23315 }
23316}
23317
23318BRp$5.storeEdgeProjections = function (edge) {
23319 var _p = edge._private;
23320 var rs = _p.rscratch;
23321 var et = rs.edgeType; // clear the cached points state
23322
23323 _p.rstyle.bezierPts = null;
23324 _p.rstyle.linePts = null;
23325 _p.rstyle.haystackPts = null;
23326
23327 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23328 _p.rstyle.bezierPts = [];
23329
23330 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23331 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23332 }
23333 } else if (et === 'segments') {
23334 var lpts = _p.rstyle.linePts = [];
23335
23336 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23337 lpts.push({
23338 x: rs.allpts[i],
23339 y: rs.allpts[i + 1]
23340 });
23341 }
23342 } else if (et === 'haystack') {
23343 var hpts = rs.haystackPts;
23344 _p.rstyle.haystackPts = [{
23345 x: hpts[0],
23346 y: hpts[1]
23347 }, {
23348 x: hpts[2],
23349 y: hpts[3]
23350 }];
23351 }
23352
23353 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23354};
23355
23356BRp$5.recalculateEdgeProjections = function (edges) {
23357 this.findEdgeControlPoints(edges);
23358};
23359
23360/* global document */
23361
23362var BRp$6 = {};
23363
23364BRp$6.recalculateNodeLabelProjection = function (node) {
23365 var content = node.pstyle('label').strValue;
23366
23367 if (emptyString(content)) {
23368 return;
23369 }
23370
23371 var textX, textY;
23372 var _p = node._private;
23373 var nodeWidth = node.width();
23374 var nodeHeight = node.height();
23375 var padding = node.padding();
23376 var nodePos = node.position();
23377 var textHalign = node.pstyle('text-halign').strValue;
23378 var textValign = node.pstyle('text-valign').strValue;
23379 var rs = _p.rscratch;
23380 var rstyle = _p.rstyle;
23381
23382 switch (textHalign) {
23383 case 'left':
23384 textX = nodePos.x - nodeWidth / 2 - padding;
23385 break;
23386
23387 case 'right':
23388 textX = nodePos.x + nodeWidth / 2 + padding;
23389 break;
23390
23391 default:
23392 // e.g. center
23393 textX = nodePos.x;
23394 }
23395
23396 switch (textValign) {
23397 case 'top':
23398 textY = nodePos.y - nodeHeight / 2 - padding;
23399 break;
23400
23401 case 'bottom':
23402 textY = nodePos.y + nodeHeight / 2 + padding;
23403 break;
23404
23405 default:
23406 // e.g. middle
23407 textY = nodePos.y;
23408 }
23409
23410 rs.labelX = textX;
23411 rs.labelY = textY;
23412 rstyle.labelX = textX;
23413 rstyle.labelY = textY;
23414 this.calculateLabelAngles(node);
23415 this.applyLabelDimensions(node);
23416};
23417
23418var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23419 var angle = Math.atan(dy / dx);
23420
23421 if (dx === 0 && angle < 0) {
23422 angle = angle * -1;
23423 }
23424
23425 return angle;
23426};
23427
23428var lineAngle = function lineAngle(p0, p1) {
23429 var dx = p1.x - p0.x;
23430 var dy = p1.y - p0.y;
23431 return lineAngleFromDelta(dx, dy);
23432};
23433
23434var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23435 var t0 = bound(0, t - 0.001, 1);
23436 var t1 = bound(0, t + 0.001, 1);
23437 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23438 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23439 return lineAngle(lp0, lp1);
23440};
23441
23442BRp$6.recalculateEdgeLabelProjections = function (edge) {
23443 var p;
23444 var _p = edge._private;
23445 var rs = _p.rscratch;
23446 var r = this;
23447 var content = {
23448 mid: edge.pstyle('label').strValue,
23449 source: edge.pstyle('source-label').strValue,
23450 target: edge.pstyle('target-label').strValue
23451 };
23452
23453 if (content.mid || content.source || content.target) ; else {
23454 return; // no labels => no calcs
23455 } // add center point to style so bounding box calculations can use it
23456 //
23457
23458
23459 p = {
23460 x: rs.midX,
23461 y: rs.midY
23462 };
23463
23464 var setRs = function setRs(propName, prefix, value) {
23465 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23466 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23467 };
23468
23469 setRs('labelX', null, p.x);
23470 setRs('labelY', null, p.y);
23471 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23472 setRs('labelAutoAngle', null, midAngle);
23473
23474 var createControlPointInfo = function createControlPointInfo() {
23475 if (createControlPointInfo.cache) {
23476 return createControlPointInfo.cache;
23477 } // use cache so only 1x per edge
23478
23479
23480 var ctrlpts = []; // store each ctrlpt info init
23481
23482 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23483 var p0 = {
23484 x: rs.allpts[i],
23485 y: rs.allpts[i + 1]
23486 };
23487 var p1 = {
23488 x: rs.allpts[i + 2],
23489 y: rs.allpts[i + 3]
23490 }; // ctrlpt
23491
23492 var p2 = {
23493 x: rs.allpts[i + 4],
23494 y: rs.allpts[i + 5]
23495 };
23496 ctrlpts.push({
23497 p0: p0,
23498 p1: p1,
23499 p2: p2,
23500 startDist: 0,
23501 length: 0,
23502 segments: []
23503 });
23504 }
23505
23506 var bpts = _p.rstyle.bezierPts;
23507 var nProjs = r.bezierProjPcts.length;
23508
23509 function addSegment(cp, p0, p1, t0, t1) {
23510 var length = dist(p0, p1);
23511 var prevSegment = cp.segments[cp.segments.length - 1];
23512 var segment = {
23513 p0: p0,
23514 p1: p1,
23515 t0: t0,
23516 t1: t1,
23517 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23518 length: length
23519 };
23520 cp.segments.push(segment);
23521 cp.length += length;
23522 } // update each ctrlpt with segment info
23523
23524
23525 for (var _i = 0; _i < ctrlpts.length; _i++) {
23526 var cp = ctrlpts[_i];
23527 var prevCp = ctrlpts[_i - 1];
23528
23529 if (prevCp) {
23530 cp.startDist = prevCp.startDist + prevCp.length;
23531 }
23532
23533 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23534
23535 for (var j = 0; j < nProjs - 1; j++) {
23536 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23537 }
23538
23539 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23540 }
23541
23542 return createControlPointInfo.cache = ctrlpts;
23543 };
23544
23545 var calculateEndProjection = function calculateEndProjection(prefix) {
23546 var angle;
23547 var isSrc = prefix === 'source';
23548
23549 if (!content[prefix]) {
23550 return;
23551 }
23552
23553 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23554
23555 switch (rs.edgeType) {
23556 case 'self':
23557 case 'compound':
23558 case 'bezier':
23559 case 'multibezier':
23560 {
23561 var cps = createControlPointInfo();
23562 var selected;
23563 var startDist = 0;
23564 var totalDist = 0; // find the segment we're on
23565
23566 for (var i = 0; i < cps.length; i++) {
23567 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23568
23569 for (var j = 0; j < _cp.segments.length; j++) {
23570 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23571 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23572 startDist = totalDist;
23573 totalDist += _seg.length;
23574
23575 if (totalDist >= offset || lastSeg) {
23576 selected = {
23577 cp: _cp,
23578 segment: _seg
23579 };
23580 break;
23581 }
23582 }
23583
23584 if (selected) {
23585 break;
23586 }
23587 }
23588
23589 var cp = selected.cp;
23590 var seg = selected.segment;
23591 var tSegment = (offset - startDist) / seg.length;
23592 var segDt = seg.t1 - seg.t0;
23593 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23594 t = bound(0, t, 1);
23595 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23596 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23597 break;
23598 }
23599
23600 case 'straight':
23601 case 'segments':
23602 case 'haystack':
23603 {
23604 var d = 0,
23605 di,
23606 d0;
23607 var p0, p1;
23608 var l = rs.allpts.length;
23609
23610 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23611 if (isSrc) {
23612 p0 = {
23613 x: rs.allpts[_i2],
23614 y: rs.allpts[_i2 + 1]
23615 };
23616 p1 = {
23617 x: rs.allpts[_i2 + 2],
23618 y: rs.allpts[_i2 + 3]
23619 };
23620 } else {
23621 p0 = {
23622 x: rs.allpts[l - 2 - _i2],
23623 y: rs.allpts[l - 1 - _i2]
23624 };
23625 p1 = {
23626 x: rs.allpts[l - 4 - _i2],
23627 y: rs.allpts[l - 3 - _i2]
23628 };
23629 }
23630
23631 di = dist(p0, p1);
23632 d0 = d;
23633 d += di;
23634
23635 if (d >= offset) {
23636 break;
23637 }
23638 }
23639
23640 var pD = offset - d0;
23641
23642 var _t = pD / di;
23643
23644 _t = bound(0, _t, 1);
23645 p = lineAt(p0, p1, _t);
23646 angle = lineAngle(p0, p1);
23647 break;
23648 }
23649 }
23650
23651 setRs('labelX', prefix, p.x);
23652 setRs('labelY', prefix, p.y);
23653 setRs('labelAutoAngle', prefix, angle);
23654 };
23655
23656 calculateEndProjection('source');
23657 calculateEndProjection('target');
23658 this.applyLabelDimensions(edge);
23659};
23660
23661BRp$6.applyLabelDimensions = function (ele) {
23662 this.applyPrefixedLabelDimensions(ele);
23663
23664 if (ele.isEdge()) {
23665 this.applyPrefixedLabelDimensions(ele, 'source');
23666 this.applyPrefixedLabelDimensions(ele, 'target');
23667 }
23668};
23669
23670BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23671 var _p = ele._private;
23672 var text = this.getLabelText(ele, prefix);
23673 var labelDims = this.calculateLabelDimensions(ele, text);
23674 var lineHeight = ele.pstyle('line-height').pfValue;
23675 var textWrap = ele.pstyle('text-wrap').strValue;
23676 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23677 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23678 var normPerLineHeight = labelDims.height / numLines;
23679 var labelLineHeight = normPerLineHeight * lineHeight;
23680 var width = labelDims.width;
23681 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23682 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23683 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23684 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23685 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23686 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23687};
23688
23689BRp$6.getLabelText = function (ele, prefix) {
23690 var _p = ele._private;
23691 var pfd = prefix ? prefix + '-' : '';
23692 var text = ele.pstyle(pfd + 'label').strValue;
23693 var textTransform = ele.pstyle('text-transform').value;
23694
23695 var rscratch = function rscratch(propName, value) {
23696 if (value) {
23697 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23698 return value;
23699 } else {
23700 return getPrefixedProperty(_p.rscratch, propName, prefix);
23701 }
23702 }; // for empty text, skip all processing
23703
23704
23705 if (!text) {
23706 return '';
23707 }
23708
23709 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23710 text = text.toUpperCase();
23711 } else if (textTransform == 'lowercase') {
23712 text = text.toLowerCase();
23713 }
23714
23715 var wrapStyle = ele.pstyle('text-wrap').value;
23716
23717 if (wrapStyle === 'wrap') {
23718 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23719
23720 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23721 return rscratch('labelWrapCachedText');
23722 }
23723
23724 var zwsp = "\u200B";
23725 var lines = text.split('\n');
23726 var maxW = ele.pstyle('text-max-width').pfValue;
23727 var overflow = ele.pstyle('text-overflow-wrap').value;
23728 var overflowAny = overflow === 'anywhere';
23729 var wrappedLines = [];
23730 var wordsRegex = /[\s\u200b]+/;
23731 var wordSeparator = overflowAny ? '' : ' ';
23732
23733 for (var l = 0; l < lines.length; l++) {
23734 var line = lines[l];
23735 var lineDims = this.calculateLabelDimensions(ele, line);
23736 var lineW = lineDims.width;
23737
23738 if (overflowAny) {
23739 var processedLine = line.split('').join(zwsp);
23740 line = processedLine;
23741 }
23742
23743 if (lineW > maxW) {
23744 // line is too long
23745 var words = line.split(wordsRegex);
23746 var subline = '';
23747
23748 for (var w = 0; w < words.length; w++) {
23749 var word = words[w];
23750 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23751 var testDims = this.calculateLabelDimensions(ele, testLine);
23752 var testW = testDims.width;
23753
23754 if (testW <= maxW) {
23755 // word fits on current line
23756 subline += word + wordSeparator;
23757 } else {
23758 // word starts new line
23759 if (subline) {
23760 wrappedLines.push(subline);
23761 }
23762
23763 subline = word + wordSeparator;
23764 }
23765 } // if there's remaining text, put it in a wrapped line
23766
23767
23768 if (!subline.match(/^[\s\u200b]+$/)) {
23769 wrappedLines.push(subline);
23770 }
23771 } else {
23772 // line is already short enough
23773 wrappedLines.push(line);
23774 }
23775 } // for
23776
23777
23778 rscratch('labelWrapCachedLines', wrappedLines);
23779 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23780 rscratch('labelWrapKey', labelKey);
23781 } else if (wrapStyle === 'ellipsis') {
23782 var _maxW = ele.pstyle('text-max-width').pfValue;
23783 var ellipsized = '';
23784 var ellipsis = "\u2026";
23785 var incLastCh = false;
23786
23787 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23788 // the label already fits
23789 return text;
23790 }
23791
23792 for (var i = 0; i < text.length; i++) {
23793 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23794
23795 if (widthWithNextCh > _maxW) {
23796 break;
23797 }
23798
23799 ellipsized += text[i];
23800
23801 if (i === text.length - 1) {
23802 incLastCh = true;
23803 }
23804 }
23805
23806 if (!incLastCh) {
23807 ellipsized += ellipsis;
23808 }
23809
23810 return ellipsized;
23811 } // if ellipsize
23812
23813
23814 return text;
23815};
23816
23817BRp$6.getLabelJustification = function (ele) {
23818 var justification = ele.pstyle('text-justification').strValue;
23819 var textHalign = ele.pstyle('text-halign').strValue;
23820
23821 if (justification === 'auto') {
23822 if (ele.isNode()) {
23823 switch (textHalign) {
23824 case 'left':
23825 return 'right';
23826
23827 case 'right':
23828 return 'left';
23829
23830 default:
23831 return 'center';
23832 }
23833 } else {
23834 return 'center';
23835 }
23836 } else {
23837 return justification;
23838 }
23839};
23840
23841BRp$6.calculateLabelDimensions = function (ele, text) {
23842 var r = this;
23843 var cacheKey = hashString(text, ele._private.labelDimsKey);
23844 var cache = r.labelDimCache || (r.labelDimCache = []);
23845 var existingVal = cache[cacheKey];
23846
23847 if (existingVal != null) {
23848 return existingVal;
23849 }
23850
23851 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23852
23853 var fStyle = ele.pstyle('font-style').strValue;
23854 var size = ele.pstyle('font-size').pfValue;
23855 var family = ele.pstyle('font-family').strValue;
23856 var weight = ele.pstyle('font-weight').strValue;
23857 var canvas = this.labelCalcCanvas;
23858 var c2d = this.labelCalcCanvasContext;
23859
23860 if (!canvas) {
23861 canvas = this.labelCalcCanvas = document.createElement('canvas');
23862 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23863 var ds = canvas.style;
23864 ds.position = 'absolute';
23865 ds.left = '-9999px';
23866 ds.top = '-9999px';
23867 ds.zIndex = '-1';
23868 ds.visibility = 'hidden';
23869 ds.pointerEvents = 'none';
23870 }
23871
23872 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23873 var width = 0;
23874 var height = 0;
23875 var lines = text.split('\n');
23876
23877 for (var i = 0; i < lines.length; i++) {
23878 var line = lines[i];
23879 var metrics = c2d.measureText(line);
23880 var w = Math.ceil(metrics.width);
23881 var h = size;
23882 width = Math.max(w, width);
23883 height += h;
23884 }
23885
23886 width += padding;
23887 height += padding;
23888 return cache[cacheKey] = {
23889 width: width,
23890 height: height
23891 };
23892};
23893
23894BRp$6.calculateLabelAngle = function (ele, prefix) {
23895 var _p = ele._private;
23896 var rs = _p.rscratch;
23897 var isEdge = ele.isEdge();
23898 var prefixDash = prefix ? prefix + '-' : '';
23899 var rot = ele.pstyle(prefixDash + 'text-rotation');
23900 var rotStr = rot.strValue;
23901
23902 if (rotStr === 'none') {
23903 return 0;
23904 } else if (isEdge && rotStr === 'autorotate') {
23905 return rs.labelAutoAngle;
23906 } else if (rotStr === 'autorotate') {
23907 return 0;
23908 } else {
23909 return rot.pfValue;
23910 }
23911};
23912
23913BRp$6.calculateLabelAngles = function (ele) {
23914 var r = this;
23915 var isEdge = ele.isEdge();
23916 var _p = ele._private;
23917 var rs = _p.rscratch;
23918 rs.labelAngle = r.calculateLabelAngle(ele);
23919
23920 if (isEdge) {
23921 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23922 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23923 }
23924};
23925
23926var BRp$7 = {};
23927var TOO_SMALL_CUT_RECT = 28;
23928var warnedCutRect = false;
23929
23930BRp$7.getNodeShape = function (node) {
23931 var r = this;
23932 var shape = node.pstyle('shape').value;
23933
23934 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23935 if (!warnedCutRect) {
23936 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23937 warnedCutRect = true;
23938 }
23939
23940 return 'rectangle';
23941 }
23942
23943 if (node.isParent()) {
23944 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23945 return shape;
23946 } else {
23947 return 'rectangle';
23948 }
23949 }
23950
23951 if (shape === 'polygon') {
23952 var points = node.pstyle('shape-polygon-points').value;
23953 return r.nodeShapes.makePolygon(points).name;
23954 }
23955
23956 return shape;
23957};
23958
23959var BRp$8 = {};
23960
23961BRp$8.registerCalculationListeners = function () {
23962 var cy = this.cy;
23963 var elesToUpdate = cy.collection();
23964 var r = this;
23965
23966 var enqueue = function enqueue(eles) {
23967 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23968 elesToUpdate.merge(eles);
23969
23970 if (dirtyStyleCaches) {
23971 for (var i = 0; i < eles.length; i++) {
23972 var ele = eles[i];
23973 var _p = ele._private;
23974 var rstyle = _p.rstyle;
23975 rstyle.clean = false;
23976 rstyle.cleanConnected = false;
23977 }
23978 }
23979 };
23980
23981 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23982 var ele = e.target;
23983 enqueue(ele);
23984 }).on('style.* background.*', function onDirtyStyle(e) {
23985 var ele = e.target;
23986 enqueue(ele, false);
23987 });
23988
23989 var updateEleCalcs = function updateEleCalcs(willDraw) {
23990 if (willDraw) {
23991 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
23992 // before calculating rendered style (and pstyle might not be called yet)
23993
23994 elesToUpdate.cleanStyle();
23995
23996 for (var i = 0; i < elesToUpdate.length; i++) {
23997 var ele = elesToUpdate[i];
23998 var rstyle = ele._private.rstyle;
23999
24000 if (ele.isNode() && !rstyle.cleanConnected) {
24001 enqueue(ele.connectedEdges());
24002 rstyle.cleanConnected = true;
24003 }
24004 }
24005
24006 if (fns) {
24007 for (var _i = 0; _i < fns.length; _i++) {
24008 var fn = fns[_i];
24009 fn(willDraw, elesToUpdate);
24010 }
24011 }
24012
24013 r.recalculateRenderedStyle(elesToUpdate);
24014 elesToUpdate = cy.collection();
24015 }
24016 };
24017
24018 r.flushRenderedStyleQueue = function () {
24019 updateEleCalcs(true);
24020 };
24021
24022 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24023};
24024
24025BRp$8.onUpdateEleCalcs = function (fn) {
24026 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24027 fns.push(fn);
24028};
24029
24030BRp$8.recalculateRenderedStyle = function (eles, useCache) {
24031 var isCleanConnected = function isCleanConnected(ele) {
24032 return ele._private.rstyle.cleanConnected;
24033 };
24034
24035 var edges = [];
24036 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24037
24038 if (this.destroyed) {
24039 return;
24040 } // use cache by default for perf
24041
24042
24043 if (useCache === undefined) {
24044 useCache = true;
24045 }
24046
24047 for (var i = 0; i < eles.length; i++) {
24048 var ele = eles[i];
24049 var _p = ele._private;
24050 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
24051 // (and a request for recalc may come in between frames)
24052
24053 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24054 rstyle.clean = false;
24055 } // only update if dirty and in graph
24056
24057
24058 if (useCache && rstyle.clean || ele.removed()) {
24059 continue;
24060 } // only update if not display: none
24061
24062
24063 if (ele.pstyle('display').value === 'none') {
24064 continue;
24065 }
24066
24067 if (_p.group === 'nodes') {
24068 nodes.push(ele);
24069 } else {
24070 // edges
24071 edges.push(ele);
24072 }
24073
24074 rstyle.clean = true;
24075 } // update node data from projections
24076
24077
24078 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24079 var _ele = nodes[_i2];
24080 var _p2 = _ele._private;
24081 var _rstyle = _p2.rstyle;
24082
24083 var pos = _ele.position();
24084
24085 this.recalculateNodeLabelProjection(_ele);
24086 _rstyle.nodeX = pos.x;
24087 _rstyle.nodeY = pos.y;
24088 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24089 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24090 }
24091
24092 this.recalculateEdgeProjections(edges); // update edge data from projections
24093
24094 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24095 var _ele2 = edges[_i3];
24096 var _p3 = _ele2._private;
24097 var _rstyle2 = _p3.rstyle;
24098 var rs = _p3.rscratch; // update rstyle positions
24099
24100 _rstyle2.srcX = rs.arrowStartX;
24101 _rstyle2.srcY = rs.arrowStartY;
24102 _rstyle2.tgtX = rs.arrowEndX;
24103 _rstyle2.tgtY = rs.arrowEndY;
24104 _rstyle2.midX = rs.midX;
24105 _rstyle2.midY = rs.midY;
24106 _rstyle2.labelAngle = rs.labelAngle;
24107 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24108 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24109 }
24110};
24111
24112var BRp$9 = {};
24113
24114BRp$9.updateCachedGrabbedEles = function () {
24115 var eles = this.cachedZSortedEles;
24116
24117 if (!eles) {
24118 // just let this be recalculated on the next z sort tick
24119 return;
24120 }
24121
24122 eles.drag = [];
24123 eles.nondrag = [];
24124 var grabTargets = [];
24125
24126 for (var i = 0; i < eles.length; i++) {
24127 var ele = eles[i];
24128 var rs = ele._private.rscratch;
24129
24130 if (ele.grabbed() && !ele.isParent()) {
24131 grabTargets.push(ele);
24132 } else if (rs.inDragLayer) {
24133 eles.drag.push(ele);
24134 } else {
24135 eles.nondrag.push(ele);
24136 }
24137 } // put the grab target nodes last so it's on top of its neighbourhood
24138
24139
24140 for (var i = 0; i < grabTargets.length; i++) {
24141 var ele = grabTargets[i];
24142 eles.drag.push(ele);
24143 }
24144};
24145
24146BRp$9.invalidateCachedZSortedEles = function () {
24147 this.cachedZSortedEles = null;
24148};
24149
24150BRp$9.getCachedZSortedEles = function (forceRecalc) {
24151 if (forceRecalc || !this.cachedZSortedEles) {
24152 var eles = this.cy.mutableElements().toArray();
24153 eles.sort(zIndexSort);
24154 eles.interactive = eles.filter(function (ele) {
24155 return ele.interactive();
24156 });
24157 this.cachedZSortedEles = eles;
24158 this.updateCachedGrabbedEles();
24159 } else {
24160 eles = this.cachedZSortedEles;
24161 }
24162
24163 return eles;
24164};
24165
24166var BRp$a = {};
24167[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24168 extend(BRp$a, props);
24169});
24170
24171var BRp$b = {};
24172
24173BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24174 var r = this;
24175 var imageCache = r.imageCache = r.imageCache || {};
24176 var cache = imageCache[url];
24177
24178 if (cache) {
24179 if (!cache.image.complete) {
24180 cache.image.addEventListener('load', onLoad);
24181 }
24182
24183 return cache.image;
24184 } else {
24185 cache = imageCache[url] = imageCache[url] || {};
24186 var image = cache.image = new Image(); // eslint-disable-line no-undef
24187
24188 image.addEventListener('load', onLoad);
24189 image.addEventListener('error', function () {
24190 image.error = true;
24191 }); // #1582 safari doesn't load data uris with crossOrigin properly
24192 // https://bugs.webkit.org/show_bug.cgi?id=123978
24193
24194 var dataUriPrefix = 'data:';
24195 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24196
24197 if (!isDataUri) {
24198 image.crossOrigin = crossOrigin; // prevent tainted canvas
24199 }
24200
24201 image.src = url;
24202 return image;
24203 }
24204};
24205
24206var BRp$c = {};
24207/* global document, window, ResizeObserver, MutationObserver */
24208
24209BRp$c.registerBinding = function (target, event, handler, useCapture) {
24210 // eslint-disable-line no-unused-vars
24211 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24212
24213 var b = this.binder(target);
24214 return b.on.apply(b, args);
24215};
24216
24217BRp$c.binder = function (tgt) {
24218 var r = this;
24219 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24220
24221 if (r.supportsPassiveEvents == null) {
24222 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24223 var supportsPassive = false;
24224
24225 try {
24226 var opts = Object.defineProperty({}, 'passive', {
24227 get: function get() {
24228 supportsPassive = true;
24229 return true;
24230 }
24231 });
24232 window.addEventListener('test', null, opts);
24233 } catch (err) {// not supported
24234 }
24235
24236 r.supportsPassiveEvents = supportsPassive;
24237 }
24238
24239 var on = function on(event, handler, useCapture) {
24240 var args = Array.prototype.slice.call(arguments);
24241
24242 if (tgtIsDom && r.supportsPassiveEvents) {
24243 // replace useCapture w/ opts obj
24244 args[2] = {
24245 capture: useCapture != null ? useCapture : false,
24246 passive: false,
24247 once: false
24248 };
24249 }
24250
24251 r.bindings.push({
24252 target: tgt,
24253 args: args
24254 });
24255 (tgt.addEventListener || tgt.on).apply(tgt, args);
24256 return this;
24257 };
24258
24259 return {
24260 on: on,
24261 addEventListener: on,
24262 addListener: on,
24263 bind: on
24264 };
24265};
24266
24267BRp$c.nodeIsDraggable = function (node) {
24268 return node && node.isNode() && !node.locked() && node.grabbable();
24269};
24270
24271BRp$c.nodeIsGrabbable = function (node) {
24272 return this.nodeIsDraggable(node) && node.interactive();
24273};
24274
24275BRp$c.load = function () {
24276 var r = this;
24277
24278 var isSelected = function isSelected(ele) {
24279 return ele.selected();
24280 };
24281
24282 var triggerEvents = function triggerEvents(target, names, e, position) {
24283 if (target == null) {
24284 target = r.cy;
24285 }
24286
24287 for (var i = 0; i < names.length; i++) {
24288 var name = names[i];
24289 target.emit({
24290 originalEvent: e,
24291 type: name,
24292 position: position
24293 });
24294 }
24295 };
24296
24297 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24298 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24299 };
24300
24301 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24302 var allowPassthrough = true;
24303
24304 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24305 // a grabbable compound node below the ele => no passthrough panning
24306 for (var i = 0; downs && i < downs.length; i++) {
24307 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
24308
24309 if (down.isNode() && down.isParent() && !down.pannable()) {
24310 allowPassthrough = false;
24311 break;
24312 }
24313 }
24314 } else {
24315 allowPassthrough = true;
24316 }
24317
24318 return allowPassthrough;
24319 };
24320
24321 var setGrabbed = function setGrabbed(ele) {
24322 ele[0]._private.grabbed = true;
24323 };
24324
24325 var setFreed = function setFreed(ele) {
24326 ele[0]._private.grabbed = false;
24327 };
24328
24329 var setInDragLayer = function setInDragLayer(ele) {
24330 ele[0]._private.rscratch.inDragLayer = true;
24331 };
24332
24333 var setOutDragLayer = function setOutDragLayer(ele) {
24334 ele[0]._private.rscratch.inDragLayer = false;
24335 };
24336
24337 var setGrabTarget = function setGrabTarget(ele) {
24338 ele[0]._private.rscratch.isGrabTarget = true;
24339 };
24340
24341 var removeGrabTarget = function removeGrabTarget(ele) {
24342 ele[0]._private.rscratch.isGrabTarget = false;
24343 };
24344
24345 var addToDragList = function addToDragList(ele, opts) {
24346 var list = opts.addToList;
24347 var listHasEle = list.has(ele);
24348
24349 if (!listHasEle && ele.grabbable() && !ele.locked()) {
24350 list.merge(ele);
24351 setGrabbed(ele);
24352 }
24353 }; // helper function to determine which child nodes and inner edges
24354 // of a compound node to be dragged as well as the grabbed and selected nodes
24355
24356
24357 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24358 if (!node.cy().hasCompoundNodes()) {
24359 return;
24360 }
24361
24362 if (opts.inDragLayer == null && opts.addToList == null) {
24363 return;
24364 } // nothing to do
24365
24366
24367 var innerNodes = node.descendants();
24368
24369 if (opts.inDragLayer) {
24370 innerNodes.forEach(setInDragLayer);
24371 innerNodes.connectedEdges().forEach(setInDragLayer);
24372 }
24373
24374 if (opts.addToList) {
24375 addToDragList(innerNodes, opts);
24376 }
24377 }; // adds the given nodes and its neighbourhood to the drag layer
24378
24379
24380 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24381 opts = opts || {};
24382 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24383
24384 if (opts.inDragLayer) {
24385 nodes.forEach(setInDragLayer);
24386 nodes.neighborhood().stdFilter(function (ele) {
24387 return !hasCompoundNodes || ele.isEdge();
24388 }).forEach(setInDragLayer);
24389 }
24390
24391 if (opts.addToList) {
24392 nodes.forEach(function (ele) {
24393 addToDragList(ele, opts);
24394 });
24395 }
24396
24397 addDescendantsToDrag(nodes, opts); // always add to drag
24398 // also add nodes and edges related to the topmost ancestor
24399
24400 updateAncestorsInDragLayer(nodes, {
24401 inDragLayer: opts.inDragLayer
24402 });
24403 r.updateCachedGrabbedEles();
24404 };
24405
24406 var addNodeToDrag = addNodesToDrag;
24407
24408 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24409 if (!grabbedEles) {
24410 return;
24411 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24412
24413
24414 r.getCachedZSortedEles().forEach(function (ele) {
24415 setFreed(ele);
24416 setOutDragLayer(ele);
24417 removeGrabTarget(ele);
24418 });
24419 r.updateCachedGrabbedEles();
24420 }; // helper function to determine which ancestor nodes and edges should go
24421 // to the drag layer (or should be removed from drag layer).
24422
24423
24424 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24425 if (opts.inDragLayer == null && opts.addToList == null) {
24426 return;
24427 } // nothing to do
24428
24429
24430 if (!node.cy().hasCompoundNodes()) {
24431 return;
24432 } // find top-level parent
24433
24434
24435 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24436
24437 if (parent.same(node)) {
24438 return;
24439 }
24440
24441 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24442 var edges = nodes.connectedEdges();
24443
24444 if (opts.inDragLayer) {
24445 edges.forEach(setInDragLayer);
24446 nodes.forEach(setInDragLayer);
24447 }
24448
24449 if (opts.addToList) {
24450 nodes.forEach(function (ele) {
24451 addToDragList(ele, opts);
24452 });
24453 }
24454 };
24455
24456 var blurActiveDomElement = function blurActiveDomElement() {
24457 if (document.activeElement != null && document.activeElement.blur != null) {
24458 document.activeElement.blur();
24459 }
24460 };
24461
24462 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24463 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24464
24465 if (haveMutationsApi) {
24466 r.removeObserver = new MutationObserver(function (mutns) {
24467 // eslint-disable-line no-undef
24468 for (var i = 0; i < mutns.length; i++) {
24469 var mutn = mutns[i];
24470 var rNodes = mutn.removedNodes;
24471
24472 if (rNodes) {
24473 for (var j = 0; j < rNodes.length; j++) {
24474 var rNode = rNodes[j];
24475
24476 if (rNode === r.container) {
24477 r.destroy();
24478 break;
24479 }
24480 }
24481 }
24482 }
24483 });
24484
24485 if (r.container.parentNode) {
24486 r.removeObserver.observe(r.container.parentNode, {
24487 childList: true
24488 });
24489 }
24490 } else {
24491 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24492 // eslint-disable-line no-unused-vars
24493 r.destroy();
24494 });
24495 }
24496
24497 var onResize = util(function () {
24498 r.cy.resize();
24499 }, 100);
24500
24501 if (haveMutationsApi) {
24502 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24503
24504 r.styleObserver.observe(r.container, {
24505 attributes: true
24506 });
24507 } // auto resize
24508
24509
24510 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24511
24512 if (haveResizeObserverApi) {
24513 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24514
24515 r.resizeObserver.observe(r.container);
24516 }
24517
24518 var forEachUp = function forEachUp(domEle, fn) {
24519 while (domEle != null) {
24520 fn(domEle);
24521 domEle = domEle.parentNode;
24522 }
24523 };
24524
24525 var invalidateCoords = function invalidateCoords() {
24526 r.invalidateContainerClientCoordsCache();
24527 };
24528
24529 forEachUp(r.container, function (domEle) {
24530 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24531 r.registerBinding(domEle, 'animationend', invalidateCoords);
24532 r.registerBinding(domEle, 'scroll', invalidateCoords);
24533 }); // stop right click menu from appearing on cy
24534
24535 r.registerBinding(r.container, 'contextmenu', function (e) {
24536 e.preventDefault();
24537 });
24538
24539 var inBoxSelection = function inBoxSelection() {
24540 return r.selection[4] !== 0;
24541 };
24542
24543 var eventInContainer = function eventInContainer(e) {
24544 // save cycles if mouse events aren't to be captured
24545 var containerPageCoords = r.findContainerClientCoords();
24546 var x = containerPageCoords[0];
24547 var y = containerPageCoords[1];
24548 var width = containerPageCoords[2];
24549 var height = containerPageCoords[3];
24550 var positions = e.touches ? e.touches : [e];
24551 var atLeastOnePosInside = false;
24552
24553 for (var i = 0; i < positions.length; i++) {
24554 var p = positions[i];
24555
24556 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24557 atLeastOnePosInside = true;
24558 break;
24559 }
24560 }
24561
24562 if (!atLeastOnePosInside) {
24563 return false;
24564 }
24565
24566 var container = r.container;
24567 var target = e.target;
24568 var tParent = target.parentNode;
24569 var containerIsTarget = false;
24570
24571 while (tParent) {
24572 if (tParent === container) {
24573 containerIsTarget = true;
24574 break;
24575 }
24576
24577 tParent = tParent.parentNode;
24578 }
24579
24580 if (!containerIsTarget) {
24581 return false;
24582 } // if target is outisde cy container, then this event is not for us
24583
24584
24585 return true;
24586 }; // Primary key
24587
24588
24589 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24590 if (!eventInContainer(e)) {
24591 return;
24592 }
24593
24594 e.preventDefault();
24595 blurActiveDomElement();
24596 r.hoverData.capture = true;
24597 r.hoverData.which = e.which;
24598 var cy = r.cy;
24599 var gpos = [e.clientX, e.clientY];
24600 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24601 var select = r.selection;
24602 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24603 var near = nears[0];
24604 var draggedElements = r.dragData.possibleDragElements;
24605 r.hoverData.mdownPos = pos;
24606 r.hoverData.mdownGPos = gpos;
24607
24608 var checkForTaphold = function checkForTaphold() {
24609 r.hoverData.tapholdCancelled = false;
24610 clearTimeout(r.hoverData.tapholdTimeout);
24611 r.hoverData.tapholdTimeout = setTimeout(function () {
24612 if (r.hoverData.tapholdCancelled) {
24613 return;
24614 } else {
24615 var ele = r.hoverData.down;
24616
24617 if (ele) {
24618 ele.emit({
24619 originalEvent: e,
24620 type: 'taphold',
24621 position: {
24622 x: pos[0],
24623 y: pos[1]
24624 }
24625 });
24626 } else {
24627 cy.emit({
24628 originalEvent: e,
24629 type: 'taphold',
24630 position: {
24631 x: pos[0],
24632 y: pos[1]
24633 }
24634 });
24635 }
24636 }
24637 }, r.tapholdDuration);
24638 }; // Right click button
24639
24640
24641 if (e.which == 3) {
24642 r.hoverData.cxtStarted = true;
24643 var cxtEvt = {
24644 originalEvent: e,
24645 type: 'cxttapstart',
24646 position: {
24647 x: pos[0],
24648 y: pos[1]
24649 }
24650 };
24651
24652 if (near) {
24653 near.activate();
24654 near.emit(cxtEvt);
24655 r.hoverData.down = near;
24656 } else {
24657 cy.emit(cxtEvt);
24658 }
24659
24660 r.hoverData.downTime = new Date().getTime();
24661 r.hoverData.cxtDragged = false; // Primary button
24662 } else if (e.which == 1) {
24663 if (near) {
24664 near.activate();
24665 } // Element dragging
24666
24667
24668 {
24669 // If something is under the cursor and it is draggable, prepare to grab it
24670 if (near != null) {
24671 if (r.nodeIsGrabbable(near)) {
24672 var makeEvent = function makeEvent(type) {
24673 return {
24674 originalEvent: e,
24675 type: type,
24676 position: {
24677 x: pos[0],
24678 y: pos[1]
24679 }
24680 };
24681 };
24682
24683 var triggerGrab = function triggerGrab(ele) {
24684 ele.emit(makeEvent('grab'));
24685 };
24686
24687 setGrabTarget(near);
24688
24689 if (!near.selected()) {
24690 draggedElements = r.dragData.possibleDragElements = cy.collection();
24691 addNodeToDrag(near, {
24692 addToList: draggedElements
24693 });
24694 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24695 } else {
24696 draggedElements = r.dragData.possibleDragElements = cy.collection();
24697 var selectedNodes = cy.$(function (ele) {
24698 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24699 });
24700 addNodesToDrag(selectedNodes, {
24701 addToList: draggedElements
24702 });
24703 near.emit(makeEvent('grabon'));
24704 selectedNodes.forEach(triggerGrab);
24705 }
24706
24707 r.redrawHint('eles', true);
24708 r.redrawHint('drag', true);
24709 }
24710 }
24711
24712 r.hoverData.down = near;
24713 r.hoverData.downs = nears;
24714 r.hoverData.downTime = new Date().getTime();
24715 }
24716 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24717 x: pos[0],
24718 y: pos[1]
24719 });
24720
24721 if (near == null) {
24722 select[4] = 1;
24723 r.data.bgActivePosistion = {
24724 x: pos[0],
24725 y: pos[1]
24726 };
24727 r.redrawHint('select', true);
24728 r.redraw();
24729 } else if (near.pannable()) {
24730 select[4] = 1; // for future pan
24731 }
24732
24733 checkForTaphold();
24734 } // Initialize selection box coordinates
24735
24736
24737 select[0] = select[2] = pos[0];
24738 select[1] = select[3] = pos[1];
24739 }, false);
24740 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24741 // eslint-disable-line no-undef
24742 var capture = r.hoverData.capture;
24743
24744 if (!capture && !eventInContainer(e)) {
24745 return;
24746 }
24747
24748 var preventDefault = false;
24749 var cy = r.cy;
24750 var zoom = cy.zoom();
24751 var gpos = [e.clientX, e.clientY];
24752 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24753 var mdownPos = r.hoverData.mdownPos;
24754 var mdownGPos = r.hoverData.mdownGPos;
24755 var select = r.selection;
24756 var near = null;
24757
24758 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24759 near = r.findNearestElement(pos[0], pos[1], true, false);
24760 }
24761
24762 var last = r.hoverData.last;
24763 var down = r.hoverData.down;
24764 var disp = [pos[0] - select[2], pos[1] - select[3]];
24765 var draggedElements = r.dragData.possibleDragElements;
24766 var isOverThresholdDrag;
24767
24768 if (mdownGPos) {
24769 var dx = gpos[0] - mdownGPos[0];
24770 var dx2 = dx * dx;
24771 var dy = gpos[1] - mdownGPos[1];
24772 var dy2 = dy * dy;
24773 var dist2 = dx2 + dy2;
24774 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24775 }
24776
24777 var multSelKeyDown = isMultSelKeyDown(e);
24778
24779 if (isOverThresholdDrag) {
24780 r.hoverData.tapholdCancelled = true;
24781 }
24782
24783 var updateDragDelta = function updateDragDelta() {
24784 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24785
24786 if (dragDelta.length === 0) {
24787 dragDelta.push(disp[0]);
24788 dragDelta.push(disp[1]);
24789 } else {
24790 dragDelta[0] += disp[0];
24791 dragDelta[1] += disp[1];
24792 }
24793 };
24794
24795 preventDefault = true;
24796 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24797 x: pos[0],
24798 y: pos[1]
24799 });
24800
24801 var goIntoBoxMode = function goIntoBoxMode() {
24802 r.data.bgActivePosistion = undefined;
24803
24804 if (!r.hoverData.selecting) {
24805 cy.emit({
24806 originalEvent: e,
24807 type: 'boxstart',
24808 position: {
24809 x: pos[0],
24810 y: pos[1]
24811 }
24812 });
24813 }
24814
24815 select[4] = 1;
24816 r.hoverData.selecting = true;
24817 r.redrawHint('select', true);
24818 r.redraw();
24819 }; // trigger context drag if rmouse down
24820
24821
24822 if (r.hoverData.which === 3) {
24823 // but only if over threshold
24824 if (isOverThresholdDrag) {
24825 var cxtEvt = {
24826 originalEvent: e,
24827 type: 'cxtdrag',
24828 position: {
24829 x: pos[0],
24830 y: pos[1]
24831 }
24832 };
24833
24834 if (down) {
24835 down.emit(cxtEvt);
24836 } else {
24837 cy.emit(cxtEvt);
24838 }
24839
24840 r.hoverData.cxtDragged = true;
24841
24842 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24843 if (r.hoverData.cxtOver) {
24844 r.hoverData.cxtOver.emit({
24845 originalEvent: e,
24846 type: 'cxtdragout',
24847 position: {
24848 x: pos[0],
24849 y: pos[1]
24850 }
24851 });
24852 }
24853
24854 r.hoverData.cxtOver = near;
24855
24856 if (near) {
24857 near.emit({
24858 originalEvent: e,
24859 type: 'cxtdragover',
24860 position: {
24861 x: pos[0],
24862 y: pos[1]
24863 }
24864 });
24865 }
24866 }
24867 } // Check if we are drag panning the entire graph
24868
24869 } else if (r.hoverData.dragging) {
24870 preventDefault = true;
24871
24872 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24873 var deltaP;
24874
24875 if (r.hoverData.justStartedPan) {
24876 var mdPos = r.hoverData.mdownPos;
24877 deltaP = {
24878 x: (pos[0] - mdPos[0]) * zoom,
24879 y: (pos[1] - mdPos[1]) * zoom
24880 };
24881 r.hoverData.justStartedPan = false;
24882 } else {
24883 deltaP = {
24884 x: disp[0] * zoom,
24885 y: disp[1] * zoom
24886 };
24887 }
24888
24889 cy.panBy(deltaP);
24890 cy.emit('dragpan');
24891 r.hoverData.dragged = true;
24892 } // Needs reproject due to pan changing viewport
24893
24894
24895 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24896 } else if (select[4] == 1 && (down == null || down.pannable())) {
24897 if (isOverThresholdDrag) {
24898 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24899 goIntoBoxMode();
24900 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24901 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24902
24903 if (allowPassthrough) {
24904 r.hoverData.dragging = true;
24905 r.hoverData.justStartedPan = true;
24906 select[4] = 0;
24907 r.data.bgActivePosistion = array2point(mdownPos);
24908 r.redrawHint('select', true);
24909 r.redraw();
24910 }
24911 }
24912
24913 if (down && down.pannable() && down.active()) {
24914 down.unactivate();
24915 }
24916 }
24917 } else {
24918 if (down && down.pannable() && down.active()) {
24919 down.unactivate();
24920 }
24921
24922 if ((!down || !down.grabbed()) && near != last) {
24923 if (last) {
24924 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24925 x: pos[0],
24926 y: pos[1]
24927 });
24928 }
24929
24930 if (near) {
24931 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24932 x: pos[0],
24933 y: pos[1]
24934 });
24935 }
24936
24937 r.hoverData.last = near;
24938 }
24939
24940 if (down) {
24941 if (isOverThresholdDrag) {
24942 // then we can take action
24943 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24944 // then selection overrides
24945 if (down && down.grabbed()) {
24946 freeDraggedElements(draggedElements);
24947 down.emit('freeon');
24948 draggedElements.emit('free');
24949
24950 if (r.dragData.didDrag) {
24951 down.emit('dragfreeon');
24952 draggedElements.emit('dragfree');
24953 }
24954 }
24955
24956 goIntoBoxMode();
24957 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24958 // drag node
24959 var justStartedDrag = !r.dragData.didDrag;
24960
24961 if (justStartedDrag) {
24962 r.redrawHint('eles', true);
24963 }
24964
24965 r.dragData.didDrag = true; // indicate that we actually did drag the node
24966 // now, add the elements to the drag layer if not done already
24967
24968 if (!r.hoverData.draggingEles) {
24969 addNodesToDrag(draggedElements, {
24970 inDragLayer: true
24971 });
24972 }
24973
24974 var totalShift = {
24975 x: 0,
24976 y: 0
24977 };
24978
24979 if (number(disp[0]) && number(disp[1])) {
24980 totalShift.x += disp[0];
24981 totalShift.y += disp[1];
24982
24983 if (justStartedDrag) {
24984 var dragDelta = r.hoverData.dragDelta;
24985
24986 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24987 totalShift.x += dragDelta[0];
24988 totalShift.y += dragDelta[1];
24989 }
24990 }
24991 }
24992
24993 r.hoverData.draggingEles = true;
24994 draggedElements.silentShift(totalShift).emit('position drag');
24995 r.redrawHint('drag', true);
24996 r.redraw();
24997 }
24998 } else {
24999 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25000 updateDragDelta();
25001 }
25002 } // prevent the dragging from triggering text selection on the page
25003
25004
25005 preventDefault = true;
25006 }
25007
25008 select[2] = pos[0];
25009 select[3] = pos[1];
25010
25011 if (preventDefault) {
25012 if (e.stopPropagation) e.stopPropagation();
25013 if (e.preventDefault) e.preventDefault();
25014 return false;
25015 }
25016 }, false);
25017 var clickTimeout, didDoubleClick, prevClickTimeStamp;
25018 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
25019 // eslint-disable-line no-undef
25020 var capture = r.hoverData.capture;
25021
25022 if (!capture) {
25023 return;
25024 }
25025
25026 r.hoverData.capture = false;
25027 var cy = r.cy;
25028 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25029 var select = r.selection;
25030 var near = r.findNearestElement(pos[0], pos[1], true, false);
25031 var draggedElements = r.dragData.possibleDragElements;
25032 var down = r.hoverData.down;
25033 var multSelKeyDown = isMultSelKeyDown(e);
25034
25035 if (r.data.bgActivePosistion) {
25036 r.redrawHint('select', true);
25037 r.redraw();
25038 }
25039
25040 r.hoverData.tapholdCancelled = true;
25041 r.data.bgActivePosistion = undefined; // not active bg now
25042
25043 if (down) {
25044 down.unactivate();
25045 }
25046
25047 if (r.hoverData.which === 3) {
25048 var cxtEvt = {
25049 originalEvent: e,
25050 type: 'cxttapend',
25051 position: {
25052 x: pos[0],
25053 y: pos[1]
25054 }
25055 };
25056
25057 if (down) {
25058 down.emit(cxtEvt);
25059 } else {
25060 cy.emit(cxtEvt);
25061 }
25062
25063 if (!r.hoverData.cxtDragged) {
25064 var cxtTap = {
25065 originalEvent: e,
25066 type: 'cxttap',
25067 position: {
25068 x: pos[0],
25069 y: pos[1]
25070 }
25071 };
25072
25073 if (down) {
25074 down.emit(cxtTap);
25075 } else {
25076 cy.emit(cxtTap);
25077 }
25078 }
25079
25080 r.hoverData.cxtDragged = false;
25081 r.hoverData.which = null;
25082 } else if (r.hoverData.which === 1) {
25083 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25084 x: pos[0],
25085 y: pos[1]
25086 });
25087
25088 if (!r.dragData.didDrag && // didn't move a node around
25089 !r.hoverData.dragged && // didn't pan
25090 !r.hoverData.selecting && // not box selection
25091 !r.hoverData.isOverThresholdDrag // didn't move too much
25092 ) {
25093 triggerEvents(down, ["click", "tap", "vclick"], e, {
25094 x: pos[0],
25095 y: pos[1]
25096 });
25097 didDoubleClick = false;
25098
25099 if (e.timeStamp - prevClickTimeStamp <= cy.multiClickDebounceTime()) {
25100 clickTimeout && clearTimeout(clickTimeout);
25101 didDoubleClick = true;
25102 prevClickTimeStamp = null;
25103 triggerEvents(down, ["dblclick", "dbltap", "vdblclick"], e, {
25104 x: pos[0],
25105 y: pos[1]
25106 });
25107 } else {
25108 clickTimeout = setTimeout(function () {
25109 if (didDoubleClick) return;
25110 triggerEvents(down, ["oneclick", "onetap", "voneclick"], e, {
25111 x: pos[0],
25112 y: pos[1]
25113 });
25114 }, cy.multiClickDebounceTime());
25115 prevClickTimeStamp = e.timeStamp;
25116 }
25117 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25118
25119
25120 if (down == null && // not mousedown on node
25121 !r.dragData.didDrag // didn't move the node around
25122 && !r.hoverData.selecting // not box selection
25123 && !r.hoverData.dragged // didn't pan
25124 && !isMultSelKeyDown(e)) {
25125 cy.$(isSelected).unselect(['tapunselect']);
25126
25127 if (draggedElements.length > 0) {
25128 r.redrawHint('eles', true);
25129 }
25130
25131 r.dragData.possibleDragElements = draggedElements = cy.collection();
25132 } // Single selection
25133
25134
25135 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25136 if (near != null && near._private.selectable) {
25137 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25138 if (near.selected()) {
25139 near.unselect(['tapunselect']);
25140 } else {
25141 near.select(['tapselect']);
25142 }
25143 } else {
25144 if (!multSelKeyDown) {
25145 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25146 near.select(['tapselect']);
25147 }
25148 }
25149
25150 r.redrawHint('eles', true);
25151 }
25152 }
25153
25154 if (r.hoverData.selecting) {
25155 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25156 r.redrawHint('select', true);
25157
25158 if (box.length > 0) {
25159 r.redrawHint('eles', true);
25160 }
25161
25162 cy.emit({
25163 type: 'boxend',
25164 originalEvent: e,
25165 position: {
25166 x: pos[0],
25167 y: pos[1]
25168 }
25169 });
25170
25171 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25172 return ele.selectable() && !ele.selected();
25173 };
25174
25175 if (cy.selectionType() === 'additive') {
25176 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25177 } else {
25178 if (!multSelKeyDown) {
25179 cy.$(isSelected).unmerge(box).unselect();
25180 }
25181
25182 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25183 } // always need redraw in case eles unselectable
25184
25185
25186 r.redraw();
25187 } // Cancel drag pan
25188
25189
25190 if (r.hoverData.dragging) {
25191 r.hoverData.dragging = false;
25192 r.redrawHint('select', true);
25193 r.redrawHint('eles', true);
25194 r.redraw();
25195 }
25196
25197 if (!select[4]) {
25198 r.redrawHint('drag', true);
25199 r.redrawHint('eles', true);
25200 var downWasGrabbed = down && down.grabbed();
25201 freeDraggedElements(draggedElements);
25202
25203 if (downWasGrabbed) {
25204 down.emit('freeon');
25205 draggedElements.emit('free');
25206
25207 if (r.dragData.didDrag) {
25208 down.emit('dragfreeon');
25209 draggedElements.emit('dragfree');
25210 }
25211 }
25212 }
25213 } // else not right mouse
25214
25215
25216 select[4] = 0;
25217 r.hoverData.down = null;
25218 r.hoverData.cxtStarted = false;
25219 r.hoverData.draggingEles = false;
25220 r.hoverData.selecting = false;
25221 r.hoverData.isOverThresholdDrag = false;
25222 r.dragData.didDrag = false;
25223 r.hoverData.dragged = false;
25224 r.hoverData.dragDelta = [];
25225 r.hoverData.mdownPos = null;
25226 r.hoverData.mdownGPos = null;
25227 }, false);
25228
25229 var wheelHandler = function wheelHandler(e) {
25230 if (r.scrollingPage) {
25231 return;
25232 } // while scrolling, ignore wheel-to-zoom
25233
25234
25235 var cy = r.cy;
25236 var zoom = cy.zoom();
25237 var pan = cy.pan();
25238 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25239 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25240
25241 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25242 // if pan dragging or cxt dragging, wheel movements make no zoom
25243 e.preventDefault();
25244 return;
25245 }
25246
25247 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25248 e.preventDefault();
25249 r.data.wheelZooming = true;
25250 clearTimeout(r.data.wheelTimeout);
25251 r.data.wheelTimeout = setTimeout(function () {
25252 r.data.wheelZooming = false;
25253 r.redrawHint('eles', true);
25254 r.redraw();
25255 }, 150);
25256 var diff;
25257
25258 if (e.deltaY != null) {
25259 diff = e.deltaY / -250;
25260 } else if (e.wheelDeltaY != null) {
25261 diff = e.wheelDeltaY / 1000;
25262 } else {
25263 diff = e.wheelDelta / 1000;
25264 }
25265
25266 diff = diff * r.wheelSensitivity;
25267 var needsWheelFix = e.deltaMode === 1;
25268
25269 if (needsWheelFix) {
25270 // fixes slow wheel events on ff/linux and ff/windows
25271 diff *= 33;
25272 }
25273
25274 var newZoom = cy.zoom() * Math.pow(10, diff);
25275
25276 if (e.type === 'gesturechange') {
25277 newZoom = r.gestureStartZoom * e.scale;
25278 }
25279
25280 cy.zoom({
25281 level: newZoom,
25282 renderedPosition: {
25283 x: rpos[0],
25284 y: rpos[1]
25285 }
25286 });
25287 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25288 }
25289 }; // Functions to help with whether mouse wheel should trigger zooming
25290 // --
25291
25292
25293 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25294 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25295 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25296 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25297
25298 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25299 // eslint-disable-line no-unused-vars
25300 r.scrollingPage = true;
25301 clearTimeout(r.scrollingPageTimeout);
25302 r.scrollingPageTimeout = setTimeout(function () {
25303 r.scrollingPage = false;
25304 }, 250);
25305 }, true); // desktop safari pinch to zoom start
25306
25307 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25308 r.gestureStartZoom = r.cy.zoom();
25309
25310 if (!r.hasTouchStarted) {
25311 // don't affect touch devices like iphone
25312 e.preventDefault();
25313 }
25314 }, true);
25315 r.registerBinding(r.container, 'gesturechange', function (e) {
25316 if (!r.hasTouchStarted) {
25317 // don't affect touch devices like iphone
25318 wheelHandler(e);
25319 }
25320 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25321 // Handle mouseout on Cytoscape container
25322
25323 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25324 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25325 r.cy.emit({
25326 originalEvent: e,
25327 type: 'mouseout',
25328 position: {
25329 x: pos[0],
25330 y: pos[1]
25331 }
25332 });
25333 }, false);
25334 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25335 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25336 r.cy.emit({
25337 originalEvent: e,
25338 type: 'mouseover',
25339 position: {
25340 x: pos[0],
25341 y: pos[1]
25342 }
25343 });
25344 }, false);
25345 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25346
25347 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25348
25349 var center1, modelCenter1; // center point on start pinch to zoom
25350
25351 var offsetLeft, offsetTop;
25352 var containerWidth, containerHeight;
25353 var twoFingersStartInside;
25354
25355 var distance = function distance(x1, y1, x2, y2) {
25356 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25357 };
25358
25359 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25360 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25361 };
25362
25363 var touchstartHandler;
25364 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25365 r.hasTouchStarted = true;
25366
25367 if (!eventInContainer(e)) {
25368 return;
25369 }
25370
25371 blurActiveDomElement();
25372 r.touchData.capture = true;
25373 r.data.bgActivePosistion = undefined;
25374 var cy = r.cy;
25375 var now = r.touchData.now;
25376 var earlier = r.touchData.earlier;
25377
25378 if (e.touches[0]) {
25379 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25380 now[0] = pos[0];
25381 now[1] = pos[1];
25382 }
25383
25384 if (e.touches[1]) {
25385 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25386 now[2] = pos[0];
25387 now[3] = pos[1];
25388 }
25389
25390 if (e.touches[2]) {
25391 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25392 now[4] = pos[0];
25393 now[5] = pos[1];
25394 } // record starting points for pinch-to-zoom
25395
25396
25397 if (e.touches[1]) {
25398 r.touchData.singleTouchMoved = true;
25399 freeDraggedElements(r.dragData.touchDragEles);
25400 var offsets = r.findContainerClientCoords();
25401 offsetLeft = offsets[0];
25402 offsetTop = offsets[1];
25403 containerWidth = offsets[2];
25404 containerHeight = offsets[3];
25405 f1x1 = e.touches[0].clientX - offsetLeft;
25406 f1y1 = e.touches[0].clientY - offsetTop;
25407 f2x1 = e.touches[1].clientX - offsetLeft;
25408 f2y1 = e.touches[1].clientY - offsetTop;
25409 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25410 var pan = cy.pan();
25411 var zoom = cy.zoom();
25412 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25413 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25414 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25415 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25416
25417 var cxtDistThreshold = 200;
25418 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25419
25420 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25421 var near1 = r.findNearestElement(now[0], now[1], true, true);
25422 var near2 = r.findNearestElement(now[2], now[3], true, true);
25423
25424 if (near1 && near1.isNode()) {
25425 near1.activate().emit({
25426 originalEvent: e,
25427 type: 'cxttapstart',
25428 position: {
25429 x: now[0],
25430 y: now[1]
25431 }
25432 });
25433 r.touchData.start = near1;
25434 } else if (near2 && near2.isNode()) {
25435 near2.activate().emit({
25436 originalEvent: e,
25437 type: 'cxttapstart',
25438 position: {
25439 x: now[0],
25440 y: now[1]
25441 }
25442 });
25443 r.touchData.start = near2;
25444 } else {
25445 cy.emit({
25446 originalEvent: e,
25447 type: 'cxttapstart',
25448 position: {
25449 x: now[0],
25450 y: now[1]
25451 }
25452 });
25453 }
25454
25455 if (r.touchData.start) {
25456 r.touchData.start._private.grabbed = false;
25457 }
25458
25459 r.touchData.cxt = true;
25460 r.touchData.cxtDragged = false;
25461 r.data.bgActivePosistion = undefined;
25462 r.redraw();
25463 return;
25464 }
25465 }
25466
25467 if (e.touches[2]) {
25468 // ignore
25469 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25470 if (cy.boxSelectionEnabled()) {
25471 e.preventDefault();
25472 }
25473 } else if (e.touches[1]) ; else if (e.touches[0]) {
25474 var nears = r.findNearestElements(now[0], now[1], true, true);
25475 var near = nears[0];
25476
25477 if (near != null) {
25478 near.activate();
25479 r.touchData.start = near;
25480 r.touchData.starts = nears;
25481
25482 if (r.nodeIsGrabbable(near)) {
25483 var draggedEles = r.dragData.touchDragEles = cy.collection();
25484 var selectedNodes = null;
25485 r.redrawHint('eles', true);
25486 r.redrawHint('drag', true);
25487
25488 if (near.selected()) {
25489 // reset drag elements, since near will be added again
25490 selectedNodes = cy.$(function (ele) {
25491 return ele.selected() && r.nodeIsGrabbable(ele);
25492 });
25493 addNodesToDrag(selectedNodes, {
25494 addToList: draggedEles
25495 });
25496 } else {
25497 addNodeToDrag(near, {
25498 addToList: draggedEles
25499 });
25500 }
25501
25502 setGrabTarget(near);
25503
25504 var makeEvent = function makeEvent(type) {
25505 return {
25506 originalEvent: e,
25507 type: type,
25508 position: {
25509 x: now[0],
25510 y: now[1]
25511 }
25512 };
25513 };
25514
25515 near.emit(makeEvent('grabon'));
25516
25517 if (selectedNodes) {
25518 selectedNodes.forEach(function (n) {
25519 n.emit(makeEvent('grab'));
25520 });
25521 } else {
25522 near.emit(makeEvent('grab'));
25523 }
25524 }
25525 }
25526
25527 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25528 x: now[0],
25529 y: now[1]
25530 });
25531
25532 if (near == null) {
25533 r.data.bgActivePosistion = {
25534 x: pos[0],
25535 y: pos[1]
25536 };
25537 r.redrawHint('select', true);
25538 r.redraw();
25539 } // Tap, taphold
25540 // -----
25541
25542
25543 r.touchData.singleTouchMoved = false;
25544 r.touchData.singleTouchStartTime = +new Date();
25545 clearTimeout(r.touchData.tapholdTimeout);
25546 r.touchData.tapholdTimeout = setTimeout(function () {
25547 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25548 && !r.touchData.selecting // box selection shouldn't allow taphold through
25549 ) {
25550 triggerEvents(r.touchData.start, ['taphold'], e, {
25551 x: now[0],
25552 y: now[1]
25553 });
25554 }
25555 }, r.tapholdDuration);
25556 }
25557
25558 if (e.touches.length >= 1) {
25559 var sPos = r.touchData.startPosition = [];
25560
25561 for (var i = 0; i < now.length; i++) {
25562 sPos[i] = earlier[i] = now[i];
25563 }
25564
25565 var touch0 = e.touches[0];
25566 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25567 }
25568 }, false);
25569 var touchmoveHandler;
25570 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25571 // eslint-disable-line no-undef
25572 var capture = r.touchData.capture;
25573
25574 if (!capture && !eventInContainer(e)) {
25575 return;
25576 }
25577
25578 var select = r.selection;
25579 var cy = r.cy;
25580 var now = r.touchData.now;
25581 var earlier = r.touchData.earlier;
25582 var zoom = cy.zoom();
25583
25584 if (e.touches[0]) {
25585 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25586 now[0] = pos[0];
25587 now[1] = pos[1];
25588 }
25589
25590 if (e.touches[1]) {
25591 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25592 now[2] = pos[0];
25593 now[3] = pos[1];
25594 }
25595
25596 if (e.touches[2]) {
25597 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25598 now[4] = pos[0];
25599 now[5] = pos[1];
25600 }
25601
25602 var startGPos = r.touchData.startGPosition;
25603 var isOverThresholdDrag;
25604
25605 if (capture && e.touches[0] && startGPos) {
25606 var disp = [];
25607
25608 for (var j = 0; j < now.length; j++) {
25609 disp[j] = now[j] - earlier[j];
25610 }
25611
25612 var dx = e.touches[0].clientX - startGPos[0];
25613 var dx2 = dx * dx;
25614 var dy = e.touches[0].clientY - startGPos[1];
25615 var dy2 = dy * dy;
25616 var dist2 = dx2 + dy2;
25617 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25618 } // context swipe cancelling
25619
25620
25621 if (capture && r.touchData.cxt) {
25622 e.preventDefault();
25623 var f1x2 = e.touches[0].clientX - offsetLeft,
25624 f1y2 = e.touches[0].clientY - offsetTop;
25625 var f2x2 = e.touches[1].clientX - offsetLeft,
25626 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25627
25628 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25629 var factorSq = distance2Sq / distance1Sq;
25630 var distThreshold = 150;
25631 var distThresholdSq = distThreshold * distThreshold;
25632 var factorThreshold = 1.5;
25633 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25634
25635 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25636 r.touchData.cxt = false;
25637 r.data.bgActivePosistion = undefined;
25638 r.redrawHint('select', true);
25639 var cxtEvt = {
25640 originalEvent: e,
25641 type: 'cxttapend',
25642 position: {
25643 x: now[0],
25644 y: now[1]
25645 }
25646 };
25647
25648 if (r.touchData.start) {
25649 r.touchData.start.unactivate().emit(cxtEvt);
25650 r.touchData.start = null;
25651 } else {
25652 cy.emit(cxtEvt);
25653 }
25654 }
25655 } // context swipe
25656
25657
25658 if (capture && r.touchData.cxt) {
25659 var cxtEvt = {
25660 originalEvent: e,
25661 type: 'cxtdrag',
25662 position: {
25663 x: now[0],
25664 y: now[1]
25665 }
25666 };
25667 r.data.bgActivePosistion = undefined;
25668 r.redrawHint('select', true);
25669
25670 if (r.touchData.start) {
25671 r.touchData.start.emit(cxtEvt);
25672 } else {
25673 cy.emit(cxtEvt);
25674 }
25675
25676 if (r.touchData.start) {
25677 r.touchData.start._private.grabbed = false;
25678 }
25679
25680 r.touchData.cxtDragged = true;
25681 var near = r.findNearestElement(now[0], now[1], true, true);
25682
25683 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25684 if (r.touchData.cxtOver) {
25685 r.touchData.cxtOver.emit({
25686 originalEvent: e,
25687 type: 'cxtdragout',
25688 position: {
25689 x: now[0],
25690 y: now[1]
25691 }
25692 });
25693 }
25694
25695 r.touchData.cxtOver = near;
25696
25697 if (near) {
25698 near.emit({
25699 originalEvent: e,
25700 type: 'cxtdragover',
25701 position: {
25702 x: now[0],
25703 y: now[1]
25704 }
25705 });
25706 }
25707 } // box selection
25708
25709 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25710 e.preventDefault();
25711 r.data.bgActivePosistion = undefined;
25712 this.lastThreeTouch = +new Date();
25713
25714 if (!r.touchData.selecting) {
25715 cy.emit({
25716 originalEvent: e,
25717 type: 'boxstart',
25718 position: {
25719 x: now[0],
25720 y: now[1]
25721 }
25722 });
25723 }
25724
25725 r.touchData.selecting = true;
25726 r.touchData.didSelect = true;
25727 select[4] = 1;
25728
25729 if (!select || select.length === 0 || select[0] === undefined) {
25730 select[0] = (now[0] + now[2] + now[4]) / 3;
25731 select[1] = (now[1] + now[3] + now[5]) / 3;
25732 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25733 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25734 } else {
25735 select[2] = (now[0] + now[2] + now[4]) / 3;
25736 select[3] = (now[1] + now[3] + now[5]) / 3;
25737 }
25738
25739 r.redrawHint('select', true);
25740 r.redraw(); // pinch to zoom
25741 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25742 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25743 // two fingers => pinch to zoom
25744 e.preventDefault();
25745 r.data.bgActivePosistion = undefined;
25746 r.redrawHint('select', true);
25747 var draggedEles = r.dragData.touchDragEles;
25748
25749 if (draggedEles) {
25750 r.redrawHint('drag', true);
25751
25752 for (var i = 0; i < draggedEles.length; i++) {
25753 var de_p = draggedEles[i]._private;
25754 de_p.grabbed = false;
25755 de_p.rscratch.inDragLayer = false;
25756 }
25757 }
25758
25759 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25760
25761 var f1x2 = e.touches[0].clientX - offsetLeft,
25762 f1y2 = e.touches[0].clientY - offsetTop;
25763 var f2x2 = e.touches[1].clientX - offsetLeft,
25764 f2y2 = e.touches[1].clientY - offsetTop;
25765 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25766 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25767
25768 var factor = distance2 / distance1;
25769
25770 if (twoFingersStartInside) {
25771 // delta finger1
25772 var df1x = f1x2 - f1x1;
25773 var df1y = f1y2 - f1y1; // delta finger 2
25774
25775 var df2x = f2x2 - f2x1;
25776 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25777 // i.e. so pinching cancels out and moving together pans
25778
25779 var tx = (df1x + df2x) / 2;
25780 var ty = (df1y + df2y) / 2; // now calculate the zoom
25781
25782 var zoom1 = cy.zoom();
25783 var zoom2 = zoom1 * factor;
25784 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25785
25786 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25787 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25788 var pan2 = {
25789 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25790 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25791 }; // remove dragged eles
25792
25793 if (_start && _start.active()) {
25794 var draggedEles = r.dragData.touchDragEles;
25795 freeDraggedElements(draggedEles);
25796 r.redrawHint('drag', true);
25797 r.redrawHint('eles', true);
25798
25799 _start.unactivate().emit('freeon');
25800
25801 draggedEles.emit('free');
25802
25803 if (r.dragData.didDrag) {
25804 _start.emit('dragfreeon');
25805
25806 draggedEles.emit('dragfree');
25807 }
25808 }
25809
25810 cy.viewport({
25811 zoom: zoom2,
25812 pan: pan2,
25813 cancelOnFailedZoom: true
25814 });
25815 cy.emit('pinchzoom');
25816 distance1 = distance2;
25817 f1x1 = f1x2;
25818 f1y1 = f1y2;
25819 f2x1 = f2x2;
25820 f2y1 = f2y2;
25821 r.pinching = true;
25822 } // Re-project
25823
25824
25825 if (e.touches[0]) {
25826 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25827 now[0] = pos[0];
25828 now[1] = pos[1];
25829 }
25830
25831 if (e.touches[1]) {
25832 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25833 now[2] = pos[0];
25834 now[3] = pos[1];
25835 }
25836
25837 if (e.touches[2]) {
25838 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25839 now[4] = pos[0];
25840 now[5] = pos[1];
25841 }
25842 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25843 ) {
25844 var start = r.touchData.start;
25845 var last = r.touchData.last;
25846 var near;
25847
25848 if (!r.hoverData.draggingEles && !r.swipePanning) {
25849 near = r.findNearestElement(now[0], now[1], true, true);
25850 }
25851
25852 if (capture && start != null) {
25853 e.preventDefault();
25854 } // dragging nodes
25855
25856
25857 if (capture && start != null && r.nodeIsDraggable(start)) {
25858 if (isOverThresholdDrag) {
25859 // then dragging can happen
25860 var draggedEles = r.dragData.touchDragEles;
25861 var justStartedDrag = !r.dragData.didDrag;
25862
25863 if (justStartedDrag) {
25864 addNodesToDrag(draggedEles, {
25865 inDragLayer: true
25866 });
25867 }
25868
25869 r.dragData.didDrag = true;
25870 var totalShift = {
25871 x: 0,
25872 y: 0
25873 };
25874
25875 if (number(disp[0]) && number(disp[1])) {
25876 totalShift.x += disp[0];
25877 totalShift.y += disp[1];
25878
25879 if (justStartedDrag) {
25880 r.redrawHint('eles', true);
25881 var dragDelta = r.touchData.dragDelta;
25882
25883 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25884 totalShift.x += dragDelta[0];
25885 totalShift.y += dragDelta[1];
25886 }
25887 }
25888 }
25889
25890 r.hoverData.draggingEles = true;
25891 draggedEles.silentShift(totalShift).emit('position drag');
25892 r.redrawHint('drag', true);
25893
25894 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25895 r.redrawHint('eles', true);
25896 }
25897
25898 r.redraw();
25899 } else {
25900 // otherise keep track of drag delta for later
25901 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25902
25903 if (dragDelta.length === 0) {
25904 dragDelta.push(disp[0]);
25905 dragDelta.push(disp[1]);
25906 } else {
25907 dragDelta[0] += disp[0];
25908 dragDelta[1] += disp[1];
25909 }
25910 }
25911 } // touchmove
25912
25913
25914 {
25915 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25916 x: now[0],
25917 y: now[1]
25918 });
25919
25920 if ((!start || !start.grabbed()) && near != last) {
25921 if (last) {
25922 last.emit({
25923 originalEvent: e,
25924 type: 'tapdragout',
25925 position: {
25926 x: now[0],
25927 y: now[1]
25928 }
25929 });
25930 }
25931
25932 if (near) {
25933 near.emit({
25934 originalEvent: e,
25935 type: 'tapdragover',
25936 position: {
25937 x: now[0],
25938 y: now[1]
25939 }
25940 });
25941 }
25942 }
25943
25944 r.touchData.last = near;
25945 } // check to cancel taphold
25946
25947 if (capture) {
25948 for (var i = 0; i < now.length; i++) {
25949 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25950 r.touchData.singleTouchMoved = true;
25951 }
25952 }
25953 } // panning
25954
25955
25956 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25957 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25958
25959 if (allowPassthrough) {
25960 e.preventDefault();
25961
25962 if (!r.data.bgActivePosistion) {
25963 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25964 }
25965
25966 if (r.swipePanning) {
25967 cy.panBy({
25968 x: disp[0] * zoom,
25969 y: disp[1] * zoom
25970 });
25971 cy.emit('dragpan');
25972 } else if (isOverThresholdDrag) {
25973 r.swipePanning = true;
25974 cy.panBy({
25975 x: dx * zoom,
25976 y: dy * zoom
25977 });
25978 cy.emit('dragpan');
25979
25980 if (start) {
25981 start.unactivate();
25982 r.redrawHint('select', true);
25983 r.touchData.start = null;
25984 }
25985 }
25986 } // Re-project
25987
25988
25989 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25990 now[0] = pos[0];
25991 now[1] = pos[1];
25992 }
25993 }
25994
25995 for (var j = 0; j < now.length; j++) {
25996 earlier[j] = now[j];
25997 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25998
25999
26000 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
26001 r.data.bgActivePosistion = undefined;
26002 r.redrawHint('select', true);
26003 r.redraw();
26004 }
26005 }, false);
26006 var touchcancelHandler;
26007 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
26008 // eslint-disable-line no-unused-vars
26009 var start = r.touchData.start;
26010 r.touchData.capture = false;
26011
26012 if (start) {
26013 start.unactivate();
26014 }
26015 });
26016 var touchendHandler, didDoubleTouch, touchTimeout, prevTouchTimeStamp;
26017 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
26018 // eslint-disable-line no-unused-vars
26019 var start = r.touchData.start;
26020 var capture = r.touchData.capture;
26021
26022 if (capture) {
26023 if (e.touches.length === 0) {
26024 r.touchData.capture = false;
26025 }
26026
26027 e.preventDefault();
26028 } else {
26029 return;
26030 }
26031
26032 var select = r.selection;
26033 r.swipePanning = false;
26034 r.hoverData.draggingEles = false;
26035 var cy = r.cy;
26036 var zoom = cy.zoom();
26037 var now = r.touchData.now;
26038 var earlier = r.touchData.earlier;
26039
26040 if (e.touches[0]) {
26041 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26042 now[0] = pos[0];
26043 now[1] = pos[1];
26044 }
26045
26046 if (e.touches[1]) {
26047 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26048 now[2] = pos[0];
26049 now[3] = pos[1];
26050 }
26051
26052 if (e.touches[2]) {
26053 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26054 now[4] = pos[0];
26055 now[5] = pos[1];
26056 }
26057
26058 if (start) {
26059 start.unactivate();
26060 }
26061
26062 var ctxTapend;
26063
26064 if (r.touchData.cxt) {
26065 ctxTapend = {
26066 originalEvent: e,
26067 type: 'cxttapend',
26068 position: {
26069 x: now[0],
26070 y: now[1]
26071 }
26072 };
26073
26074 if (start) {
26075 start.emit(ctxTapend);
26076 } else {
26077 cy.emit(ctxTapend);
26078 }
26079
26080 if (!r.touchData.cxtDragged) {
26081 var ctxTap = {
26082 originalEvent: e,
26083 type: 'cxttap',
26084 position: {
26085 x: now[0],
26086 y: now[1]
26087 }
26088 };
26089
26090 if (start) {
26091 start.emit(ctxTap);
26092 } else {
26093 cy.emit(ctxTap);
26094 }
26095 }
26096
26097 if (r.touchData.start) {
26098 r.touchData.start._private.grabbed = false;
26099 }
26100
26101 r.touchData.cxt = false;
26102 r.touchData.start = null;
26103 r.redraw();
26104 return;
26105 } // no more box selection if we don't have three fingers
26106
26107
26108 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26109 r.touchData.selecting = false;
26110 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26111 select[0] = undefined;
26112 select[1] = undefined;
26113 select[2] = undefined;
26114 select[3] = undefined;
26115 select[4] = 0;
26116 r.redrawHint('select', true);
26117 cy.emit({
26118 type: 'boxend',
26119 originalEvent: e,
26120 position: {
26121 x: now[0],
26122 y: now[1]
26123 }
26124 });
26125
26126 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26127 return ele.selectable() && !ele.selected();
26128 };
26129
26130 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26131
26132 if (box.nonempty()) {
26133 r.redrawHint('eles', true);
26134 }
26135
26136 r.redraw();
26137 }
26138
26139 if (start != null) {
26140 start.unactivate();
26141 }
26142
26143 if (e.touches[2]) {
26144 r.data.bgActivePosistion = undefined;
26145 r.redrawHint('select', true);
26146 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26147 r.data.bgActivePosistion = undefined;
26148 r.redrawHint('select', true);
26149 var draggedEles = r.dragData.touchDragEles;
26150
26151 if (start != null) {
26152 var startWasGrabbed = start._private.grabbed;
26153 freeDraggedElements(draggedEles);
26154 r.redrawHint('drag', true);
26155 r.redrawHint('eles', true);
26156
26157 if (startWasGrabbed) {
26158 start.emit('freeon');
26159 draggedEles.emit('free');
26160
26161 if (r.dragData.didDrag) {
26162 start.emit('dragfreeon');
26163 draggedEles.emit('dragfree');
26164 }
26165 }
26166
26167 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26168 x: now[0],
26169 y: now[1]
26170 });
26171 start.unactivate();
26172 r.touchData.start = null;
26173 } else {
26174 var near = r.findNearestElement(now[0], now[1], true, true);
26175 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26176 x: now[0],
26177 y: now[1]
26178 });
26179 }
26180
26181 var dx = r.touchData.startPosition[0] - now[0];
26182 var dx2 = dx * dx;
26183 var dy = r.touchData.startPosition[1] - now[1];
26184 var dy2 = dy * dy;
26185 var dist2 = dx2 + dy2;
26186 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26187
26188 if (!r.touchData.singleTouchMoved) {
26189 if (!start) {
26190 cy.$(':selected').unselect(['tapunselect']);
26191 }
26192
26193 triggerEvents(start, ['tap', 'vclick'], e, {
26194 x: now[0],
26195 y: now[1]
26196 });
26197 didDoubleTouch = false;
26198
26199 if (e.timeStamp - prevTouchTimeStamp <= cy.multiClickDebounceTime()) {
26200 touchTimeout && clearTimeout(touchTimeout);
26201 didDoubleTouch = true;
26202 prevTouchTimeStamp = null;
26203 triggerEvents(start, ['dbltap', 'vdblclick'], e, {
26204 x: now[0],
26205 y: now[1]
26206 });
26207 } else {
26208 touchTimeout = setTimeout(function () {
26209 if (didDoubleTouch) return;
26210 triggerEvents(start, ['onetap', 'voneclick'], e, {
26211 x: now[0],
26212 y: now[1]
26213 });
26214 }, cy.multiClickDebounceTime());
26215 prevTouchTimeStamp = e.timeStamp;
26216 }
26217 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26218
26219
26220 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26221 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26222 ) {
26223 if (cy.selectionType() === 'single') {
26224 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26225 start.select(['tapselect']);
26226 } else {
26227 if (start.selected()) {
26228 start.unselect(['tapunselect']);
26229 } else {
26230 start.select(['tapselect']);
26231 }
26232 }
26233
26234 r.redrawHint('eles', true);
26235 }
26236
26237 r.touchData.singleTouchMoved = true;
26238 }
26239
26240 for (var j = 0; j < now.length; j++) {
26241 earlier[j] = now[j];
26242 }
26243
26244 r.dragData.didDrag = false; // reset for next touchstart
26245
26246 if (e.touches.length === 0) {
26247 r.touchData.dragDelta = [];
26248 r.touchData.startPosition = null;
26249 r.touchData.startGPosition = null;
26250 r.touchData.didSelect = false;
26251 }
26252
26253 if (e.touches.length < 2) {
26254 if (e.touches.length === 1) {
26255 // the old start global pos'n may not be the same finger that remains
26256 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26257 }
26258
26259 r.pinching = false;
26260 r.redrawHint('eles', true);
26261 r.redraw();
26262 } //r.redraw();
26263
26264 }, false); // fallback compatibility layer for ms pointer events
26265
26266 if (typeof TouchEvent === 'undefined') {
26267 var pointers = [];
26268
26269 var makeTouch = function makeTouch(e) {
26270 return {
26271 clientX: e.clientX,
26272 clientY: e.clientY,
26273 force: 1,
26274 identifier: e.pointerId,
26275 pageX: e.pageX,
26276 pageY: e.pageY,
26277 radiusX: e.width / 2,
26278 radiusY: e.height / 2,
26279 screenX: e.screenX,
26280 screenY: e.screenY,
26281 target: e.target
26282 };
26283 };
26284
26285 var makePointer = function makePointer(e) {
26286 return {
26287 event: e,
26288 touch: makeTouch(e)
26289 };
26290 };
26291
26292 var addPointer = function addPointer(e) {
26293 pointers.push(makePointer(e));
26294 };
26295
26296 var removePointer = function removePointer(e) {
26297 for (var i = 0; i < pointers.length; i++) {
26298 var p = pointers[i];
26299
26300 if (p.event.pointerId === e.pointerId) {
26301 pointers.splice(i, 1);
26302 return;
26303 }
26304 }
26305 };
26306
26307 var updatePointer = function updatePointer(e) {
26308 var p = pointers.filter(function (p) {
26309 return p.event.pointerId === e.pointerId;
26310 })[0];
26311 p.event = e;
26312 p.touch = makeTouch(e);
26313 };
26314
26315 var addTouchesToEvent = function addTouchesToEvent(e) {
26316 e.touches = pointers.map(function (p) {
26317 return p.touch;
26318 });
26319 };
26320
26321 var pointerIsMouse = function pointerIsMouse(e) {
26322 return e.pointerType === 'mouse' || e.pointerType === 4;
26323 };
26324
26325 r.registerBinding(r.container, 'pointerdown', function (e) {
26326 if (pointerIsMouse(e)) {
26327 return;
26328 } // mouse already handled
26329
26330
26331 e.preventDefault();
26332 addPointer(e);
26333 addTouchesToEvent(e);
26334 touchstartHandler(e);
26335 });
26336 r.registerBinding(r.container, 'pointerup', function (e) {
26337 if (pointerIsMouse(e)) {
26338 return;
26339 } // mouse already handled
26340
26341
26342 removePointer(e);
26343 addTouchesToEvent(e);
26344 touchendHandler(e);
26345 });
26346 r.registerBinding(r.container, 'pointercancel', function (e) {
26347 if (pointerIsMouse(e)) {
26348 return;
26349 } // mouse already handled
26350
26351
26352 removePointer(e);
26353 addTouchesToEvent(e);
26354 touchcancelHandler(e);
26355 });
26356 r.registerBinding(r.container, 'pointermove', function (e) {
26357 if (pointerIsMouse(e)) {
26358 return;
26359 } // mouse already handled
26360
26361
26362 e.preventDefault();
26363 updatePointer(e);
26364 addTouchesToEvent(e);
26365 touchmoveHandler(e);
26366 });
26367 }
26368};
26369
26370var BRp$d = {};
26371
26372BRp$d.generatePolygon = function (name, points) {
26373 return this.nodeShapes[name] = {
26374 renderer: this,
26375 name: name,
26376 points: points,
26377 draw: function draw(context, centerX, centerY, width, height) {
26378 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26379 },
26380 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26381 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26382 },
26383 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26384 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26385 }
26386 };
26387};
26388
26389BRp$d.generateEllipse = function () {
26390 return this.nodeShapes['ellipse'] = {
26391 renderer: this,
26392 name: 'ellipse',
26393 draw: function draw(context, centerX, centerY, width, height) {
26394 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26395 },
26396 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26397 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26398 },
26399 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26400 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26401 }
26402 };
26403};
26404
26405BRp$d.generateRoundPolygon = function (name, points) {
26406 // Pre-compute control points
26407 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26408 // the unit vectors.
26409 // For simplicity the layout will be:
26410 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26411 var allPoints = new Array(points.length * 2);
26412
26413 for (var i = 0; i < points.length / 2; i++) {
26414 var sourceIndex = i * 2;
26415 var destIndex = void 0;
26416
26417 if (i < points.length / 2 - 1) {
26418 destIndex = (i + 1) * 2;
26419 } else {
26420 destIndex = 0;
26421 }
26422
26423 allPoints[i * 4] = points[sourceIndex];
26424 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26425 var xDest = points[destIndex] - points[sourceIndex];
26426 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26427 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26428 allPoints[i * 4 + 2] = xDest / norm;
26429 allPoints[i * 4 + 3] = yDest / norm;
26430 }
26431
26432 return this.nodeShapes[name] = {
26433 renderer: this,
26434 name: name,
26435 points: allPoints,
26436 draw: function draw(context, centerX, centerY, width, height) {
26437 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26438 },
26439 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26440 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26441 },
26442 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26443 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26444 }
26445 };
26446};
26447
26448BRp$d.generateRoundRectangle = function () {
26449 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26450 renderer: this,
26451 name: 'round-rectangle',
26452 points: generateUnitNgonPointsFitToSquare(4, 0),
26453 draw: function draw(context, centerX, centerY, width, height) {
26454 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26455 },
26456 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26457 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26458 },
26459 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26460 var cornerRadius = getRoundRectangleRadius(width, height);
26461 var diam = cornerRadius * 2; // Check hBox
26462
26463 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26464 return true;
26465 } // Check vBox
26466
26467
26468 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26469 return true;
26470 } // Check top left quarter circle
26471
26472
26473 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26474 return true;
26475 } // Check top right quarter circle
26476
26477
26478 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26479 return true;
26480 } // Check bottom right quarter circle
26481
26482
26483 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26484 return true;
26485 } // Check bottom left quarter circle
26486
26487
26488 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26489 return true;
26490 }
26491
26492 return false;
26493 }
26494 };
26495};
26496
26497BRp$d.generateCutRectangle = function () {
26498 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26499 renderer: this,
26500 name: 'cut-rectangle',
26501 cornerLength: getCutRectangleCornerLength(),
26502 points: generateUnitNgonPointsFitToSquare(4, 0),
26503 draw: function draw(context, centerX, centerY, width, height) {
26504 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26505 },
26506 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26507 var cl = this.cornerLength;
26508 var hh = height / 2;
26509 var hw = width / 2;
26510 var xBegin = centerX - hw;
26511 var xEnd = centerX + hw;
26512 var yBegin = centerY - hh;
26513 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26514
26515 return {
26516 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26517 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26518 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26519 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26520 };
26521 },
26522 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26523 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26524 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26525 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26526 },
26527 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26528 // Check hBox
26529 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26530 return true;
26531 } // Check vBox
26532
26533
26534 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26535 return true;
26536 }
26537
26538 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26539 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26540 }
26541 };
26542};
26543
26544BRp$d.generateBarrel = function () {
26545 return this.nodeShapes['barrel'] = {
26546 renderer: this,
26547 name: 'barrel',
26548 points: generateUnitNgonPointsFitToSquare(4, 0),
26549 draw: function draw(context, centerX, centerY, width, height) {
26550 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26551 },
26552 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26553 // use two fixed t values for the bezier curve approximation
26554 var t0 = 0.15;
26555 var t1 = 0.5;
26556 var t2 = 0.85;
26557 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26558
26559 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26560 // approximate curve pts based on the two t values
26561 var m0 = qbezierPtAt({
26562 x: pts[0],
26563 y: pts[1]
26564 }, {
26565 x: pts[2],
26566 y: pts[3]
26567 }, {
26568 x: pts[4],
26569 y: pts[5]
26570 }, t0);
26571 var m1 = qbezierPtAt({
26572 x: pts[0],
26573 y: pts[1]
26574 }, {
26575 x: pts[2],
26576 y: pts[3]
26577 }, {
26578 x: pts[4],
26579 y: pts[5]
26580 }, t1);
26581 var m2 = qbezierPtAt({
26582 x: pts[0],
26583 y: pts[1]
26584 }, {
26585 x: pts[2],
26586 y: pts[3]
26587 }, {
26588 x: pts[4],
26589 y: pts[5]
26590 }, t2);
26591 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26592 };
26593
26594 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26595 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26596 },
26597 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26598 var hh = height / 2;
26599 var hw = width / 2;
26600 var xBegin = centerX - hw;
26601 var xEnd = centerX + hw;
26602 var yBegin = centerY - hh;
26603 var yEnd = centerY + hh;
26604 var curveConstants = getBarrelCurveConstants(width, height);
26605 var hOffset = curveConstants.heightOffset;
26606 var wOffset = curveConstants.widthOffset;
26607 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26608
26609 var pts = {
26610 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26611 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26612 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26613 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26614 };
26615 pts.topLeft.isTop = true;
26616 pts.topRight.isTop = true;
26617 pts.bottomLeft.isBottom = true;
26618 pts.bottomRight.isBottom = true;
26619 return pts;
26620 },
26621 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26622 var curveConstants = getBarrelCurveConstants(width, height);
26623 var hOffset = curveConstants.heightOffset;
26624 var wOffset = curveConstants.widthOffset; // Check hBox
26625
26626 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26627 return true;
26628 } // Check vBox
26629
26630
26631 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26632 return true;
26633 }
26634
26635 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26636
26637 var getCurveT = function getCurveT(x, y, curvePts) {
26638 var x0 = curvePts[4];
26639 var x1 = curvePts[2];
26640 var x2 = curvePts[0];
26641 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26642
26643 var y2 = curvePts[1];
26644 var xMin = Math.min(x0, x2);
26645 var xMax = Math.max(x0, x2);
26646 var yMin = Math.min(y0, y2);
26647 var yMax = Math.max(y0, y2);
26648
26649 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26650 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26651 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26652 var validRoots = roots.filter(function (r) {
26653 return 0 <= r && r <= 1;
26654 });
26655
26656 if (validRoots.length > 0) {
26657 return validRoots[0];
26658 }
26659 }
26660
26661 return null;
26662 };
26663
26664 var curveRegions = Object.keys(barrelCurvePts);
26665
26666 for (var i = 0; i < curveRegions.length; i++) {
26667 var corner = curveRegions[i];
26668 var cornerPts = barrelCurvePts[corner];
26669 var t = getCurveT(x, y, cornerPts);
26670
26671 if (t == null) {
26672 continue;
26673 }
26674
26675 var y0 = cornerPts[5];
26676 var y1 = cornerPts[3];
26677 var y2 = cornerPts[1];
26678 var bezY = qbezierAt(y0, y1, y2, t);
26679
26680 if (cornerPts.isTop && bezY <= y) {
26681 return true;
26682 }
26683
26684 if (cornerPts.isBottom && y <= bezY) {
26685 return true;
26686 }
26687 }
26688
26689 return false;
26690 }
26691 };
26692};
26693
26694BRp$d.generateBottomRoundrectangle = function () {
26695 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26696 renderer: this,
26697 name: 'bottom-round-rectangle',
26698 points: generateUnitNgonPointsFitToSquare(4, 0),
26699 draw: function draw(context, centerX, centerY, width, height) {
26700 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26701 },
26702 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26703 var topStartX = nodeX - (width / 2 + padding);
26704 var topStartY = nodeY - (height / 2 + padding);
26705 var topEndY = topStartY;
26706 var topEndX = nodeX + (width / 2 + padding);
26707 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26708
26709 if (topIntersections.length > 0) {
26710 return topIntersections;
26711 }
26712
26713 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26714 },
26715 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26716 var cornerRadius = getRoundRectangleRadius(width, height);
26717 var diam = 2 * cornerRadius; // Check hBox
26718
26719 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26720 return true;
26721 } // Check vBox
26722
26723
26724 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26725 return true;
26726 } // check non-rounded top side
26727
26728
26729 var outerWidth = width / 2 + 2 * padding;
26730 var outerHeight = height / 2 + 2 * padding;
26731 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26732
26733 if (pointInsidePolygonPoints(x, y, points)) {
26734 return true;
26735 } // Check bottom right quarter circle
26736
26737
26738 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26739 return true;
26740 } // Check bottom left quarter circle
26741
26742
26743 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26744 return true;
26745 }
26746
26747 return false;
26748 }
26749 };
26750};
26751
26752BRp$d.registerNodeShapes = function () {
26753 var nodeShapes = this.nodeShapes = {};
26754 var renderer = this;
26755 this.generateEllipse();
26756 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26757 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26758 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26759 nodeShapes['square'] = nodeShapes['rectangle'];
26760 this.generateRoundRectangle();
26761 this.generateCutRectangle();
26762 this.generateBarrel();
26763 this.generateBottomRoundrectangle();
26764 {
26765 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26766 this.generatePolygon('diamond', diamondPoints);
26767 this.generateRoundPolygon('round-diamond', diamondPoints);
26768 }
26769 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26770 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26771 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26772 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26773 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26774 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26775 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26776 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26777 var star5Points = new Array(20);
26778 {
26779 var outerPoints = generateUnitNgonPoints(5, 0);
26780 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26781
26782 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26783 innerRadius *= 1.57;
26784
26785 for (var i = 0; i < innerPoints.length / 2; i++) {
26786 innerPoints[i * 2] *= innerRadius;
26787 innerPoints[i * 2 + 1] *= innerRadius;
26788 }
26789
26790 for (var i = 0; i < 20 / 4; i++) {
26791 star5Points[i * 4] = outerPoints[i * 2];
26792 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26793 star5Points[i * 4 + 2] = innerPoints[i * 2];
26794 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26795 }
26796 }
26797 star5Points = fitPolygonToSquare(star5Points);
26798 this.generatePolygon('star', star5Points);
26799 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26800 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26801 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]);
26802 {
26803 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26804 this.generatePolygon('tag', tagPoints);
26805 this.generateRoundPolygon('round-tag', tagPoints);
26806 }
26807
26808 nodeShapes.makePolygon = function (points) {
26809 // use caching on user-specified polygons so they are as fast as native shapes
26810 var key = points.join('$');
26811 var name = 'polygon-' + key;
26812 var shape;
26813
26814 if (shape = this[name]) {
26815 // got cached shape
26816 return shape;
26817 } // create and cache new shape
26818
26819
26820 return renderer.generatePolygon(name, points);
26821 };
26822};
26823
26824var BRp$e = {};
26825
26826BRp$e.timeToRender = function () {
26827 return this.redrawTotalTime / this.redrawCount;
26828};
26829
26830BRp$e.redraw = function (options) {
26831 options = options || staticEmptyObject();
26832 var r = this;
26833
26834 if (r.averageRedrawTime === undefined) {
26835 r.averageRedrawTime = 0;
26836 }
26837
26838 if (r.lastRedrawTime === undefined) {
26839 r.lastRedrawTime = 0;
26840 }
26841
26842 if (r.lastDrawTime === undefined) {
26843 r.lastDrawTime = 0;
26844 }
26845
26846 r.requestedFrame = true;
26847 r.renderOptions = options;
26848};
26849
26850BRp$e.beforeRender = function (fn, priority) {
26851 // the renderer can't add tick callbacks when destroyed
26852 if (this.destroyed) {
26853 return;
26854 }
26855
26856 if (priority == null) {
26857 error('Priority is not optional for beforeRender');
26858 }
26859
26860 var cbs = this.beforeRenderCallbacks;
26861 cbs.push({
26862 fn: fn,
26863 priority: priority
26864 }); // higher priority callbacks executed first
26865
26866 cbs.sort(function (a, b) {
26867 return b.priority - a.priority;
26868 });
26869};
26870
26871var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26872 var cbs = r.beforeRenderCallbacks;
26873
26874 for (var i = 0; i < cbs.length; i++) {
26875 cbs[i].fn(willDraw, startTime);
26876 }
26877};
26878
26879BRp$e.startRenderLoop = function () {
26880 var r = this;
26881 var cy = r.cy;
26882
26883 if (r.renderLoopStarted) {
26884 return;
26885 } else {
26886 r.renderLoopStarted = true;
26887 }
26888
26889 var renderFn = function renderFn(requestTime) {
26890 if (r.destroyed) {
26891 return;
26892 }
26893
26894 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26895 beforeRenderCallbacks(r, true, requestTime);
26896 var startTime = performanceNow();
26897 r.render(r.renderOptions);
26898 var endTime = r.lastDrawTime = performanceNow();
26899
26900 if (r.averageRedrawTime === undefined) {
26901 r.averageRedrawTime = endTime - startTime;
26902 }
26903
26904 if (r.redrawCount === undefined) {
26905 r.redrawCount = 0;
26906 }
26907
26908 r.redrawCount++;
26909
26910 if (r.redrawTotalTime === undefined) {
26911 r.redrawTotalTime = 0;
26912 }
26913
26914 var duration = endTime - startTime;
26915 r.redrawTotalTime += duration;
26916 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26917
26918 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26919 r.requestedFrame = false;
26920 } else {
26921 beforeRenderCallbacks(r, false, requestTime);
26922 }
26923
26924 r.skipFrame = false;
26925 requestAnimationFrame(renderFn);
26926 };
26927
26928 requestAnimationFrame(renderFn);
26929};
26930
26931var BaseRenderer = function BaseRenderer(options) {
26932 this.init(options);
26933};
26934
26935var BR = BaseRenderer;
26936var BRp$f = BR.prototype;
26937BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26938
26939BRp$f.init = function (options) {
26940 var r = this;
26941 r.options = options;
26942 r.cy = options.cy;
26943 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26944
26945 if (window$1) {
26946 var document = window$1.document;
26947 var head = document.head;
26948 var stylesheetId = '__________cytoscape_stylesheet';
26949 var className = '__________cytoscape_container';
26950 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26951
26952 if (ctr.className.indexOf(className) < 0) {
26953 ctr.className = (ctr.className || '') + ' ' + className;
26954 }
26955
26956 if (!stylesheetAlreadyExists) {
26957 var stylesheet = document.createElement('style');
26958 stylesheet.id = stylesheetId;
26959 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26960 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26961 }
26962
26963 var computedStyle = window$1.getComputedStyle(ctr);
26964 var position = computedStyle.getPropertyValue('position');
26965
26966 if (position === 'static') {
26967 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26968 }
26969 }
26970
26971 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26972
26973 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26974
26975 r.hoverData = {
26976 down: null,
26977 last: null,
26978 downTime: null,
26979 triggerMode: null,
26980 dragging: false,
26981 initialPan: [null, null],
26982 capture: false
26983 };
26984 r.dragData = {
26985 possibleDragElements: []
26986 };
26987 r.touchData = {
26988 start: null,
26989 capture: false,
26990 // These 3 fields related to tap, taphold events
26991 startPosition: [null, null, null, null, null, null],
26992 singleTouchStartTime: null,
26993 singleTouchMoved: true,
26994 now: [null, null, null, null, null, null],
26995 earlier: [null, null, null, null, null, null]
26996 };
26997 r.redraws = 0;
26998 r.showFps = options.showFps;
26999 r.debug = options.debug;
27000 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
27001 r.textureOnViewport = options.textureOnViewport;
27002 r.wheelSensitivity = options.wheelSensitivity;
27003 r.motionBlurEnabled = options.motionBlur; // on by default
27004
27005 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
27006 r.motionBlur = options.motionBlur; // for initial kick off
27007
27008 r.motionBlurOpacity = options.motionBlurOpacity;
27009 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
27010 r.motionBlurPxRatio = 1;
27011 r.mbPxRBlurry = 1; //0.8;
27012
27013 r.minMbLowQualFrames = 4;
27014 r.fullQualityMb = false;
27015 r.clearedForMotionBlur = [];
27016 r.desktopTapThreshold = options.desktopTapThreshold;
27017 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
27018 r.touchTapThreshold = options.touchTapThreshold;
27019 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
27020 r.tapholdDuration = 500;
27021 r.bindings = [];
27022 r.beforeRenderCallbacks = [];
27023 r.beforeRenderPriorities = {
27024 // higher priority execs before lower one
27025 animations: 400,
27026 eleCalcs: 300,
27027 eleTxrDeq: 200,
27028 lyrTxrDeq: 150,
27029 lyrTxrSkip: 100
27030 };
27031 r.registerNodeShapes();
27032 r.registerArrowShapes();
27033 r.registerCalculationListeners();
27034};
27035
27036BRp$f.notify = function (eventName, eles) {
27037 var r = this;
27038 var cy = r.cy; // the renderer can't be notified after it's destroyed
27039
27040 if (this.destroyed) {
27041 return;
27042 }
27043
27044 if (eventName === 'init') {
27045 r.load();
27046 return;
27047 }
27048
27049 if (eventName === 'destroy') {
27050 r.destroy();
27051 return;
27052 }
27053
27054 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
27055 r.invalidateCachedZSortedEles();
27056 }
27057
27058 if (eventName === 'viewport') {
27059 r.redrawHint('select', true);
27060 }
27061
27062 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
27063 r.invalidateContainerClientCoordsCache();
27064 r.matchCanvasSize(r.container);
27065 }
27066
27067 r.redrawHint('eles', true);
27068 r.redrawHint('drag', true);
27069 this.startRenderLoop();
27070 this.redraw();
27071};
27072
27073BRp$f.destroy = function () {
27074 var r = this;
27075 r.destroyed = true;
27076 r.cy.stopAnimationLoop();
27077
27078 for (var i = 0; i < r.bindings.length; i++) {
27079 var binding = r.bindings[i];
27080 var b = binding;
27081 var tgt = b.target;
27082 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
27083 }
27084
27085 r.bindings = [];
27086 r.beforeRenderCallbacks = [];
27087 r.onUpdateEleCalcsFns = [];
27088
27089 if (r.removeObserver) {
27090 r.removeObserver.disconnect();
27091 }
27092
27093 if (r.styleObserver) {
27094 r.styleObserver.disconnect();
27095 }
27096
27097 if (r.resizeObserver) {
27098 r.resizeObserver.disconnect();
27099 }
27100
27101 if (r.labelCalcDiv) {
27102 try {
27103 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
27104 } catch (e) {// ie10 issue #1014
27105 }
27106 }
27107};
27108
27109BRp$f.isHeadless = function () {
27110 return false;
27111};
27112
27113[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
27114 extend(BRp$f, props);
27115});
27116
27117var fullFpsTime = 1000 / 60; // assume 60 frames per second
27118
27119var defs = {
27120 setupDequeueing: function setupDequeueing(opts) {
27121 return function setupDequeueingImpl() {
27122 var self = this;
27123 var r = this.renderer;
27124
27125 if (self.dequeueingSetup) {
27126 return;
27127 } else {
27128 self.dequeueingSetup = true;
27129 }
27130
27131 var queueRedraw = util(function () {
27132 r.redrawHint('eles', true);
27133 r.redrawHint('drag', true);
27134 r.redraw();
27135 }, opts.deqRedrawThreshold);
27136
27137 var dequeue = function dequeue(willDraw, frameStartTime) {
27138 var startTime = performanceNow();
27139 var avgRenderTime = r.averageRedrawTime;
27140 var renderTime = r.lastRedrawTime;
27141 var deqd = [];
27142 var extent = r.cy.extent();
27143 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
27144 // queue won't automatically be flushed before dequeueing starts
27145
27146 if (!willDraw) {
27147 r.flushRenderedStyleQueue();
27148 }
27149
27150 while (true) {
27151 // eslint-disable-line no-constant-condition
27152 var now = performanceNow();
27153 var duration = now - startTime;
27154 var frameDuration = now - frameStartTime;
27155
27156 if (renderTime < fullFpsTime) {
27157 // if we're rendering faster than the ideal fps, then do dequeueing
27158 // during all of the remaining frame time
27159 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27160
27161 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27162 break;
27163 }
27164 } else {
27165 if (willDraw) {
27166 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27167 break;
27168 }
27169 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27170 break;
27171 }
27172 }
27173
27174 var thisDeqd = opts.deq(self, pixelRatio, extent);
27175
27176 if (thisDeqd.length > 0) {
27177 for (var i = 0; i < thisDeqd.length; i++) {
27178 deqd.push(thisDeqd[i]);
27179 }
27180 } else {
27181 break;
27182 }
27183 } // callbacks on dequeue
27184
27185
27186 if (deqd.length > 0) {
27187 opts.onDeqd(self, deqd);
27188
27189 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27190 queueRedraw();
27191 }
27192 }
27193 };
27194
27195 var priority = opts.priority || noop;
27196 r.beforeRender(dequeue, priority(self));
27197 };
27198 }
27199};
27200
27201// Uses keys so elements may share the same cache.
27202
27203var ElementTextureCacheLookup =
27204/*#__PURE__*/
27205function () {
27206 function ElementTextureCacheLookup(getKey) {
27207 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27208
27209 _classCallCheck(this, ElementTextureCacheLookup);
27210
27211 this.idsByKey = new Map$1();
27212 this.keyForId = new Map$1();
27213 this.cachesByLvl = new Map$1();
27214 this.lvls = [];
27215 this.getKey = getKey;
27216 this.doesEleInvalidateKey = doesEleInvalidateKey;
27217 }
27218
27219 _createClass(ElementTextureCacheLookup, [{
27220 key: "getIdsFor",
27221 value: function getIdsFor(key) {
27222 if (key == null) {
27223 error("Can not get id list for null key");
27224 }
27225
27226 var idsByKey = this.idsByKey;
27227 var ids = this.idsByKey.get(key);
27228
27229 if (!ids) {
27230 ids = new Set$1();
27231 idsByKey.set(key, ids);
27232 }
27233
27234 return ids;
27235 }
27236 }, {
27237 key: "addIdForKey",
27238 value: function addIdForKey(key, id) {
27239 if (key != null) {
27240 this.getIdsFor(key).add(id);
27241 }
27242 }
27243 }, {
27244 key: "deleteIdForKey",
27245 value: function deleteIdForKey(key, id) {
27246 if (key != null) {
27247 this.getIdsFor(key)["delete"](id);
27248 }
27249 }
27250 }, {
27251 key: "getNumberOfIdsForKey",
27252 value: function getNumberOfIdsForKey(key) {
27253 if (key == null) {
27254 return 0;
27255 } else {
27256 return this.getIdsFor(key).size;
27257 }
27258 }
27259 }, {
27260 key: "updateKeyMappingFor",
27261 value: function updateKeyMappingFor(ele) {
27262 var id = ele.id();
27263 var prevKey = this.keyForId.get(id);
27264 var currKey = this.getKey(ele);
27265 this.deleteIdForKey(prevKey, id);
27266 this.addIdForKey(currKey, id);
27267 this.keyForId.set(id, currKey);
27268 }
27269 }, {
27270 key: "deleteKeyMappingFor",
27271 value: function deleteKeyMappingFor(ele) {
27272 var id = ele.id();
27273 var prevKey = this.keyForId.get(id);
27274 this.deleteIdForKey(prevKey, id);
27275 this.keyForId["delete"](id);
27276 }
27277 }, {
27278 key: "keyHasChangedFor",
27279 value: function keyHasChangedFor(ele) {
27280 var id = ele.id();
27281 var prevKey = this.keyForId.get(id);
27282 var newKey = this.getKey(ele);
27283 return prevKey !== newKey;
27284 }
27285 }, {
27286 key: "isInvalid",
27287 value: function isInvalid(ele) {
27288 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27289 }
27290 }, {
27291 key: "getCachesAt",
27292 value: function getCachesAt(lvl) {
27293 var cachesByLvl = this.cachesByLvl,
27294 lvls = this.lvls;
27295 var caches = cachesByLvl.get(lvl);
27296
27297 if (!caches) {
27298 caches = new Map$1();
27299 cachesByLvl.set(lvl, caches);
27300 lvls.push(lvl);
27301 }
27302
27303 return caches;
27304 }
27305 }, {
27306 key: "getCache",
27307 value: function getCache(key, lvl) {
27308 return this.getCachesAt(lvl).get(key);
27309 }
27310 }, {
27311 key: "get",
27312 value: function get(ele, lvl) {
27313 var key = this.getKey(ele);
27314 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27315
27316 if (cache != null) {
27317 this.updateKeyMappingFor(ele);
27318 }
27319
27320 return cache;
27321 }
27322 }, {
27323 key: "getForCachedKey",
27324 value: function getForCachedKey(ele, lvl) {
27325 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27326
27327 var cache = this.getCache(key, lvl);
27328 return cache;
27329 }
27330 }, {
27331 key: "hasCache",
27332 value: function hasCache(key, lvl) {
27333 return this.getCachesAt(lvl).has(key);
27334 }
27335 }, {
27336 key: "has",
27337 value: function has(ele, lvl) {
27338 var key = this.getKey(ele);
27339 return this.hasCache(key, lvl);
27340 }
27341 }, {
27342 key: "setCache",
27343 value: function setCache(key, lvl, cache) {
27344 cache.key = key;
27345 this.getCachesAt(lvl).set(key, cache);
27346 }
27347 }, {
27348 key: "set",
27349 value: function set(ele, lvl, cache) {
27350 var key = this.getKey(ele);
27351 this.setCache(key, lvl, cache);
27352 this.updateKeyMappingFor(ele);
27353 }
27354 }, {
27355 key: "deleteCache",
27356 value: function deleteCache(key, lvl) {
27357 this.getCachesAt(lvl)["delete"](key);
27358 }
27359 }, {
27360 key: "delete",
27361 value: function _delete(ele, lvl) {
27362 var key = this.getKey(ele);
27363 this.deleteCache(key, lvl);
27364 }
27365 }, {
27366 key: "invalidateKey",
27367 value: function invalidateKey(key) {
27368 var _this = this;
27369
27370 this.lvls.forEach(function (lvl) {
27371 return _this.deleteCache(key, lvl);
27372 });
27373 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27374
27375 }, {
27376 key: "invalidate",
27377 value: function invalidate(ele) {
27378 var id = ele.id();
27379 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27380
27381 this.deleteKeyMappingFor(ele);
27382 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27383
27384 if (entireKeyInvalidated) {
27385 // clear mapping for current key
27386 this.invalidateKey(key);
27387 }
27388
27389 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27390 }
27391 }]);
27392
27393 return ElementTextureCacheLookup;
27394}();
27395
27396var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27397
27398var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27399
27400var minLvl = -4; // when scaling smaller than that we don't need to re-render
27401
27402var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27403
27404var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27405
27406var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27407
27408var defTxrWidth = 1024; // default/minimum texture width
27409
27410var maxTxrW = 1024; // the maximum width of a texture
27411
27412var maxTxrH = 1024; // the maximum height of a texture
27413
27414var minUtility = 0.2; // if usage of texture is less than this, it is retired
27415
27416var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27417
27418var maxFullnessChecks = 10; // dequeued after this many checks
27419
27420var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27421
27422var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27423
27424var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27425
27426var deqFastCost = 0.9; // % of frame time to be used when >60fps
27427
27428var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27429
27430var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27431
27432var getTxrReasons = {
27433 dequeue: 'dequeue',
27434 downscale: 'downscale',
27435 highQuality: 'highQuality'
27436};
27437var initDefaults = defaults({
27438 getKey: null,
27439 doesEleInvalidateKey: falsify,
27440 drawElement: null,
27441 getBoundingBox: null,
27442 getRotationPoint: null,
27443 getRotationOffset: null,
27444 isVisible: trueify,
27445 allowEdgeTxrCaching: true,
27446 allowParentTxrCaching: true
27447});
27448
27449var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27450 var self = this;
27451 self.renderer = renderer;
27452 self.onDequeues = [];
27453 var opts = initDefaults(initOptions);
27454 extend(self, opts);
27455 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27456 self.setupDequeueing();
27457};
27458
27459var ETCp = ElementTextureCache.prototype;
27460ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27461
27462ETCp.getTextureQueue = function (txrH) {
27463 var self = this;
27464 self.eleImgCaches = self.eleImgCaches || {};
27465 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27466}; // the list of usused textures which can be recycled (in use in texture queue)
27467
27468
27469ETCp.getRetiredTextureQueue = function (txrH) {
27470 var self = this;
27471 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27472 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27473 return rtxtrQ;
27474}; // queue of element draw requests at different scale levels
27475
27476
27477ETCp.getElementQueue = function () {
27478 var self = this;
27479 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27480 return b.reqs - a.reqs;
27481 });
27482 return q;
27483}; // queue of element draw requests at different scale levels (element id lookup)
27484
27485
27486ETCp.getElementKeyToQueue = function () {
27487 var self = this;
27488 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27489 return k2q;
27490};
27491
27492ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27493 var self = this;
27494 var r = this.renderer;
27495 var zoom = r.cy.zoom();
27496 var lookup = this.lookup;
27497
27498 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27499 return null;
27500 }
27501
27502 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27503 return null;
27504 }
27505
27506 if (lvl == null) {
27507 lvl = Math.ceil(log2(zoom * pxRatio));
27508 }
27509
27510 if (lvl < minLvl) {
27511 lvl = minLvl;
27512 } else if (zoom >= maxZoom || lvl > maxLvl) {
27513 return null;
27514 }
27515
27516 var scale = Math.pow(2, lvl);
27517 var eleScaledH = bb.h * scale;
27518 var eleScaledW = bb.w * scale;
27519 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27520
27521 if (!this.isVisible(ele, scaledLabelShown)) {
27522 return null;
27523 }
27524
27525 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27526
27527 if (eleCache && eleCache.invalidated) {
27528 eleCache.invalidated = false;
27529 eleCache.texture.invalidatedWidth -= eleCache.width;
27530 }
27531
27532 if (eleCache) {
27533 return eleCache;
27534 }
27535
27536 var txrH; // which texture height this ele belongs to
27537
27538 if (eleScaledH <= minTxrH) {
27539 txrH = minTxrH;
27540 } else if (eleScaledH <= txrStepH) {
27541 txrH = txrStepH;
27542 } else {
27543 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27544 }
27545
27546 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27547 return null; // caching large elements is not efficient
27548 }
27549
27550 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27551
27552 var txr = txrQ[txrQ.length - 2];
27553
27554 var addNewTxr = function addNewTxr() {
27555 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27556 }; // try the last one if there is no second last one
27557
27558
27559 if (!txr) {
27560 txr = txrQ[txrQ.length - 1];
27561 } // if the last one doesn't exist, we need a first one
27562
27563
27564 if (!txr) {
27565 txr = addNewTxr();
27566 } // if there's no room in the current texture, we need a new one
27567
27568
27569 if (txr.width - txr.usedWidth < eleScaledW) {
27570 txr = addNewTxr();
27571 }
27572
27573 var scalableFrom = function scalableFrom(otherCache) {
27574 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27575 };
27576
27577 var deqing = reason && reason === getTxrReasons.dequeue;
27578 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27579 var downscaleReq = reason && reason === getTxrReasons.downscale;
27580 var higherCache; // the nearest cache with a higher level
27581
27582 for (var l = lvl + 1; l <= maxLvl; l++) {
27583 var c = lookup.get(ele, l);
27584
27585 if (c) {
27586 higherCache = c;
27587 break;
27588 }
27589 }
27590
27591 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27592
27593 var downscale = function downscale() {
27594 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27595 }; // reset ele area in texture
27596
27597
27598 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27599 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27600
27601 if (scalableFrom(oneUpCache)) {
27602 // then we can relatively cheaply rescale the existing image w/o rerendering
27603 downscale();
27604 } else if (scalableFrom(higherCache)) {
27605 // then use the higher cache for now and queue the next level down
27606 // to cheaply scale towards the smaller level
27607 if (highQualityReq) {
27608 for (var _l = higherCache.level; _l > lvl; _l--) {
27609 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27610 }
27611
27612 downscale();
27613 } else {
27614 self.queueElement(ele, higherCache.level - 1);
27615 return higherCache;
27616 }
27617 } else {
27618 var lowerCache; // the nearest cache with a lower level
27619
27620 if (!deqing && !highQualityReq && !downscaleReq) {
27621 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27622 var _c = lookup.get(ele, _l2);
27623
27624 if (_c) {
27625 lowerCache = _c;
27626 break;
27627 }
27628 }
27629 }
27630
27631 if (scalableFrom(lowerCache)) {
27632 // then use the lower quality cache for now and queue the better one for later
27633 self.queueElement(ele, lvl);
27634 return lowerCache;
27635 }
27636
27637 txr.context.translate(txr.usedWidth, 0);
27638 txr.context.scale(scale, scale);
27639 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27640 txr.context.scale(1 / scale, 1 / scale);
27641 txr.context.translate(-txr.usedWidth, 0);
27642 }
27643
27644 eleCache = {
27645 x: txr.usedWidth,
27646 texture: txr,
27647 level: lvl,
27648 scale: scale,
27649 width: eleScaledW,
27650 height: eleScaledH,
27651 scaledLabelShown: scaledLabelShown
27652 };
27653 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27654 txr.eleCaches.push(eleCache);
27655 lookup.set(ele, lvl, eleCache);
27656 self.checkTextureFullness(txr);
27657 return eleCache;
27658};
27659
27660ETCp.invalidateElements = function (eles) {
27661 for (var i = 0; i < eles.length; i++) {
27662 this.invalidateElement(eles[i]);
27663 }
27664};
27665
27666ETCp.invalidateElement = function (ele) {
27667 var self = this;
27668 var lookup = self.lookup;
27669 var caches = [];
27670 var invalid = lookup.isInvalid(ele);
27671
27672 if (!invalid) {
27673 return; // override the invalidation request if the element key has not changed
27674 }
27675
27676 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27677 var cache = lookup.getForCachedKey(ele, lvl);
27678
27679 if (cache) {
27680 caches.push(cache);
27681 }
27682 }
27683
27684 var noOtherElesUseCache = lookup.invalidate(ele);
27685
27686 if (noOtherElesUseCache) {
27687 for (var i = 0; i < caches.length; i++) {
27688 var _cache = caches[i];
27689 var txr = _cache.texture; // remove space from the texture it belongs to
27690
27691 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27692
27693 _cache.invalidated = true; // retire the texture if its utility is low
27694
27695 self.checkTextureUtility(txr);
27696 }
27697 } // remove from queue since the old req was for the old state
27698
27699
27700 self.removeFromQueue(ele);
27701};
27702
27703ETCp.checkTextureUtility = function (txr) {
27704 // invalidate all entries in the cache if the cache size is small
27705 if (txr.invalidatedWidth >= minUtility * txr.width) {
27706 this.retireTexture(txr);
27707 }
27708};
27709
27710ETCp.checkTextureFullness = function (txr) {
27711 // if texture has been mostly filled and passed over several times, remove
27712 // it from the queue so we don't need to waste time looking at it to put new things
27713 var self = this;
27714 var txrQ = self.getTextureQueue(txr.height);
27715
27716 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27717 removeFromArray(txrQ, txr);
27718 } else {
27719 txr.fullnessChecks++;
27720 }
27721};
27722
27723ETCp.retireTexture = function (txr) {
27724 var self = this;
27725 var txrH = txr.height;
27726 var txrQ = self.getTextureQueue(txrH);
27727 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27728
27729 removeFromArray(txrQ, txr);
27730 txr.retired = true; // remove the refs from the eles to the caches:
27731
27732 var eleCaches = txr.eleCaches;
27733
27734 for (var i = 0; i < eleCaches.length; i++) {
27735 var eleCache = eleCaches[i];
27736 lookup.deleteCache(eleCache.key, eleCache.level);
27737 }
27738
27739 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27740
27741 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27742 rtxtrQ.push(txr);
27743};
27744
27745ETCp.addTexture = function (txrH, minW) {
27746 var self = this;
27747 var txrQ = self.getTextureQueue(txrH);
27748 var txr = {};
27749 txrQ.push(txr);
27750 txr.eleCaches = [];
27751 txr.height = txrH;
27752 txr.width = Math.max(defTxrWidth, minW);
27753 txr.usedWidth = 0;
27754 txr.invalidatedWidth = 0;
27755 txr.fullnessChecks = 0;
27756 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27757 txr.context = txr.canvas.getContext('2d');
27758 return txr;
27759};
27760
27761ETCp.recycleTexture = function (txrH, minW) {
27762 var self = this;
27763 var txrQ = self.getTextureQueue(txrH);
27764 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27765
27766 for (var i = 0; i < rtxtrQ.length; i++) {
27767 var txr = rtxtrQ[i];
27768
27769 if (txr.width >= minW) {
27770 txr.retired = false;
27771 txr.usedWidth = 0;
27772 txr.invalidatedWidth = 0;
27773 txr.fullnessChecks = 0;
27774 clearArray(txr.eleCaches);
27775 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27776 txr.context.clearRect(0, 0, txr.width, txr.height);
27777 removeFromArray(rtxtrQ, txr);
27778 txrQ.push(txr);
27779 return txr;
27780 }
27781 }
27782};
27783
27784ETCp.queueElement = function (ele, lvl) {
27785 var self = this;
27786 var q = self.getElementQueue();
27787 var k2q = self.getElementKeyToQueue();
27788 var key = this.getKey(ele);
27789 var existingReq = k2q[key];
27790
27791 if (existingReq) {
27792 // use the max lvl b/c in between lvls are cheap to make
27793 existingReq.level = Math.max(existingReq.level, lvl);
27794 existingReq.eles.merge(ele);
27795 existingReq.reqs++;
27796 q.updateItem(existingReq);
27797 } else {
27798 var req = {
27799 eles: ele.spawn().merge(ele),
27800 level: lvl,
27801 reqs: 1,
27802 key: key
27803 };
27804 q.push(req);
27805 k2q[key] = req;
27806 }
27807};
27808
27809ETCp.dequeue = function (pxRatio
27810/*, extent*/
27811) {
27812 var self = this;
27813 var q = self.getElementQueue();
27814 var k2q = self.getElementKeyToQueue();
27815 var dequeued = [];
27816 var lookup = self.lookup;
27817
27818 for (var i = 0; i < maxDeqSize; i++) {
27819 if (q.size() > 0) {
27820 var req = q.pop();
27821 var key = req.key;
27822 var ele = req.eles[0]; // all eles have the same key
27823
27824 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27825
27826 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27827
27828 if (cacheExists) {
27829 continue;
27830 }
27831
27832 dequeued.push(req);
27833 var bb = self.getBoundingBox(ele);
27834 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27835 } else {
27836 break;
27837 }
27838 }
27839
27840 return dequeued;
27841};
27842
27843ETCp.removeFromQueue = function (ele) {
27844 var self = this;
27845 var q = self.getElementQueue();
27846 var k2q = self.getElementKeyToQueue();
27847 var key = this.getKey(ele);
27848 var req = k2q[key];
27849
27850 if (req != null) {
27851 if (req.eles.length === 1) {
27852 // remove if last ele in the req
27853 // bring to front of queue
27854 req.reqs = MAX_INT;
27855 q.updateItem(req);
27856 q.pop(); // remove from queue
27857
27858 k2q[key] = null; // remove from lookup map
27859 } else {
27860 // otherwise just remove ele from req
27861 req.eles.unmerge(ele);
27862 }
27863 }
27864};
27865
27866ETCp.onDequeue = function (fn) {
27867 this.onDequeues.push(fn);
27868};
27869
27870ETCp.offDequeue = function (fn) {
27871 removeFromArray(this.onDequeues, fn);
27872};
27873
27874ETCp.setupDequeueing = defs.setupDequeueing({
27875 deqRedrawThreshold: deqRedrawThreshold,
27876 deqCost: deqCost,
27877 deqAvgCost: deqAvgCost,
27878 deqNoDrawCost: deqNoDrawCost,
27879 deqFastCost: deqFastCost,
27880 deq: function deq(self, pxRatio, extent) {
27881 return self.dequeue(pxRatio, extent);
27882 },
27883 onDeqd: function onDeqd(self, deqd) {
27884 for (var i = 0; i < self.onDequeues.length; i++) {
27885 var fn = self.onDequeues[i];
27886 fn(deqd);
27887 }
27888 },
27889 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27890 for (var i = 0; i < deqd.length; i++) {
27891 var eles = deqd[i].eles;
27892
27893 for (var j = 0; j < eles.length; j++) {
27894 var bb = eles[j].boundingBox();
27895
27896 if (boundingBoxesIntersect(bb, extent)) {
27897 return true;
27898 }
27899 }
27900 }
27901
27902 return false;
27903 },
27904 priority: function priority(self) {
27905 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27906 }
27907});
27908
27909var defNumLayers = 1; // default number of layers to use
27910
27911var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27912
27913var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27914
27915var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27916
27917var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27918
27919var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27920
27921var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27922
27923var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27924
27925var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27926
27927var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27928
27929var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27930
27931var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27932
27933var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27934
27935var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27936// var log = function(){ console.log.apply( console, arguments ); };
27937
27938var LayeredTextureCache = function LayeredTextureCache(renderer) {
27939 var self = this;
27940 var r = self.renderer = renderer;
27941 var cy = r.cy;
27942 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27943
27944 self.firstGet = true;
27945 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27946 self.skipping = false;
27947 self.eleTxrDeqs = cy.collection();
27948 self.scheduleElementRefinement = util(function () {
27949 self.refineElementTextures(self.eleTxrDeqs);
27950 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27951 }, refineEleDebounceTime);
27952 r.beforeRender(function (willDraw, now) {
27953 if (now - self.lastInvalidationTime <= invalidThreshold) {
27954 self.skipping = true;
27955 } else {
27956 self.skipping = false;
27957 }
27958 }, r.beforeRenderPriorities.lyrTxrSkip);
27959
27960 var qSort = function qSort(a, b) {
27961 return b.reqs - a.reqs;
27962 };
27963
27964 self.layersQueue = new Heap(qSort);
27965 self.setupDequeueing();
27966};
27967
27968var LTCp = LayeredTextureCache.prototype;
27969var layerIdPool = 0;
27970var MAX_INT$1 = Math.pow(2, 53) - 1;
27971
27972LTCp.makeLayer = function (bb, lvl) {
27973 var scale = Math.pow(2, lvl);
27974 var w = Math.ceil(bb.w * scale);
27975 var h = Math.ceil(bb.h * scale);
27976 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27977 var layer = {
27978 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27979 bb: bb,
27980 level: lvl,
27981 width: w,
27982 height: h,
27983 canvas: canvas,
27984 context: canvas.getContext('2d'),
27985 eles: [],
27986 elesQueue: [],
27987 reqs: 0
27988 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27989
27990 var cxt = layer.context;
27991 var dx = -layer.bb.x1;
27992 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27993
27994 cxt.scale(scale, scale);
27995 cxt.translate(dx, dy);
27996 return layer;
27997};
27998
27999LTCp.getLayers = function (eles, pxRatio, lvl) {
28000 var self = this;
28001 var r = self.renderer;
28002 var cy = r.cy;
28003 var zoom = cy.zoom();
28004 var firstGet = self.firstGet;
28005 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
28006 //log eles.map(function(ele){ return ele.id() }) );
28007
28008 if (lvl == null) {
28009 lvl = Math.ceil(log2(zoom * pxRatio));
28010
28011 if (lvl < minLvl$1) {
28012 lvl = minLvl$1;
28013 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
28014 return null;
28015 }
28016 }
28017
28018 self.validateLayersElesOrdering(lvl, eles);
28019 var layersByLvl = self.layersByLevel;
28020 var scale = Math.pow(2, lvl);
28021 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
28022 var bb;
28023 var lvlComplete = self.levelIsComplete(lvl, eles);
28024 var tmpLayers;
28025
28026 var checkTempLevels = function checkTempLevels() {
28027 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
28028 self.validateLayersElesOrdering(l, eles);
28029
28030 if (self.levelIsComplete(l, eles)) {
28031 tmpLayers = layersByLvl[l];
28032 return true;
28033 }
28034 };
28035
28036 var checkLvls = function checkLvls(dir) {
28037 if (tmpLayers) {
28038 return;
28039 }
28040
28041 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
28042 if (canUseAsTmpLvl(l)) {
28043 break;
28044 }
28045 }
28046 };
28047
28048 checkLvls(+1);
28049 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
28050
28051 for (var i = layers.length - 1; i >= 0; i--) {
28052 var layer = layers[i];
28053
28054 if (layer.invalid) {
28055 removeFromArray(layers, layer);
28056 }
28057 }
28058 };
28059
28060 if (!lvlComplete) {
28061 // if the current level is incomplete, then use the closest, best quality layerset temporarily
28062 // and later queue the current layerset so we can get the proper quality level soon
28063 checkTempLevels();
28064 } else {
28065 // log('level complete, using existing layers\n--');
28066 return layers;
28067 }
28068
28069 var getBb = function getBb() {
28070 if (!bb) {
28071 bb = makeBoundingBox();
28072
28073 for (var i = 0; i < eles.length; i++) {
28074 updateBoundingBox(bb, eles[i].boundingBox());
28075 }
28076 }
28077
28078 return bb;
28079 };
28080
28081 var makeLayer = function makeLayer(opts) {
28082 opts = opts || {};
28083 var after = opts.after;
28084 getBb();
28085 var area = bb.w * scale * (bb.h * scale);
28086
28087 if (area > maxLayerArea) {
28088 return null;
28089 }
28090
28091 var layer = self.makeLayer(bb, lvl);
28092
28093 if (after != null) {
28094 var index = layers.indexOf(after) + 1;
28095 layers.splice(index, 0, layer);
28096 } else if (opts.insert === undefined || opts.insert) {
28097 // no after specified => first layer made so put at start
28098 layers.unshift(layer);
28099 } // if( tmpLayers ){
28100 //self.queueLayer( layer );
28101 // }
28102
28103
28104 return layer;
28105 };
28106
28107 if (self.skipping && !firstGet) {
28108 // log('skip layers');
28109 return null;
28110 } // log('do layers');
28111
28112
28113 var layer = null;
28114 var maxElesPerLayer = eles.length / defNumLayers;
28115 var allowLazyQueueing = !firstGet;
28116
28117 for (var i = 0; i < eles.length; i++) {
28118 var ele = eles[i];
28119 var rs = ele._private.rscratch;
28120 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
28121
28122 var existingLayer = caches[lvl];
28123
28124 if (existingLayer) {
28125 // reuse layer for later eles
28126 // log('reuse layer for', ele.id());
28127 layer = existingLayer;
28128 continue;
28129 }
28130
28131 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
28132 // log('make new layer for ele %s', ele.id());
28133 layer = makeLayer({
28134 insert: true,
28135 after: layer
28136 }); // if now layer can be built then we can't use layers at this level
28137
28138 if (!layer) {
28139 return null;
28140 } // log('new layer with id %s', layer.id);
28141
28142 }
28143
28144 if (tmpLayers || allowLazyQueueing) {
28145 // log('queue ele %s in layer %s', ele.id(), layer.id);
28146 self.queueLayer(layer, ele);
28147 } else {
28148 // log('draw ele %s in layer %s', ele.id(), layer.id);
28149 self.drawEleInLayer(layer, ele, lvl, pxRatio);
28150 }
28151
28152 layer.eles.push(ele);
28153 caches[lvl] = layer;
28154 } // log('--');
28155
28156
28157 if (tmpLayers) {
28158 // then we only queued the current layerset and can't draw it yet
28159 return tmpLayers;
28160 }
28161
28162 if (allowLazyQueueing) {
28163 // log('lazy queue level', lvl);
28164 return null;
28165 }
28166
28167 return layers;
28168}; // a layer may want to use an ele cache of a higher level to avoid blurriness
28169// so the layer level might not equal the ele level
28170
28171
28172LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28173 return lvl;
28174};
28175
28176LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28177 var self = this;
28178 var r = this.renderer;
28179 var context = layer.context;
28180 var bb = ele.boundingBox();
28181
28182 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28183 return;
28184 }
28185
28186 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28187
28188 {
28189 r.setImgSmoothing(context, false);
28190 }
28191
28192 {
28193 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28194 }
28195
28196 {
28197 r.setImgSmoothing(context, true);
28198 }
28199};
28200
28201LTCp.levelIsComplete = function (lvl, eles) {
28202 var self = this;
28203 var layers = self.layersByLevel[lvl];
28204
28205 if (!layers || layers.length === 0) {
28206 return false;
28207 }
28208
28209 var numElesInLayers = 0;
28210
28211 for (var i = 0; i < layers.length; i++) {
28212 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28213
28214 if (layer.reqs > 0) {
28215 return false;
28216 } // if the layer is invalid, the level is not complete
28217
28218
28219 if (layer.invalid) {
28220 return false;
28221 }
28222
28223 numElesInLayers += layer.eles.length;
28224 } // we should have exactly the number of eles passed in to be complete
28225
28226
28227 if (numElesInLayers !== eles.length) {
28228 return false;
28229 }
28230
28231 return true;
28232};
28233
28234LTCp.validateLayersElesOrdering = function (lvl, eles) {
28235 var layers = this.layersByLevel[lvl];
28236
28237 if (!layers) {
28238 return;
28239 } // if in a layer the eles are not in the same order, then the layer is invalid
28240 // (i.e. there is an ele in between the eles in the layer)
28241
28242
28243 for (var i = 0; i < layers.length; i++) {
28244 var layer = layers[i];
28245 var offset = -1; // find the offset
28246
28247 for (var j = 0; j < eles.length; j++) {
28248 if (layer.eles[0] === eles[j]) {
28249 offset = j;
28250 break;
28251 }
28252 }
28253
28254 if (offset < 0) {
28255 // then the layer has nonexistant elements and is invalid
28256 this.invalidateLayer(layer);
28257 continue;
28258 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28259
28260
28261 var o = offset;
28262
28263 for (var j = 0; j < layer.eles.length; j++) {
28264 if (layer.eles[j] !== eles[o + j]) {
28265 // log('invalidate based on ordering', layer.id);
28266 this.invalidateLayer(layer);
28267 break;
28268 }
28269 }
28270 }
28271};
28272
28273LTCp.updateElementsInLayers = function (eles, update) {
28274 var self = this;
28275 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28276 // layer itself along the way
28277
28278 for (var i = 0; i < eles.length; i++) {
28279 var req = isEles ? null : eles[i];
28280 var ele = isEles ? eles[i] : eles[i].ele;
28281 var rs = ele._private.rscratch;
28282 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28283
28284 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28285 var layer = caches[l];
28286
28287 if (!layer) {
28288 continue;
28289 } // if update is a request from the ele cache, then it affects only
28290 // the matching level
28291
28292
28293 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28294 continue;
28295 }
28296
28297 update(layer, ele, req);
28298 }
28299 }
28300};
28301
28302LTCp.haveLayers = function () {
28303 var self = this;
28304 var haveLayers = false;
28305
28306 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28307 var layers = self.layersByLevel[l];
28308
28309 if (layers && layers.length > 0) {
28310 haveLayers = true;
28311 break;
28312 }
28313 }
28314
28315 return haveLayers;
28316};
28317
28318LTCp.invalidateElements = function (eles) {
28319 var self = this;
28320
28321 if (eles.length === 0) {
28322 return;
28323 }
28324
28325 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28326
28327 if (eles.length === 0 || !self.haveLayers()) {
28328 return;
28329 }
28330
28331 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28332 self.invalidateLayer(layer);
28333 });
28334};
28335
28336LTCp.invalidateLayer = function (layer) {
28337 // log('update invalidate layer time');
28338 this.lastInvalidationTime = performanceNow();
28339
28340 if (layer.invalid) {
28341 return;
28342 } // save cycles
28343
28344
28345 var lvl = layer.level;
28346 var eles = layer.eles;
28347 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28348
28349 removeFromArray(layers, layer); // layer.eles = [];
28350
28351 layer.elesQueue = [];
28352 layer.invalid = true;
28353
28354 if (layer.replacement) {
28355 layer.replacement.invalid = true;
28356 }
28357
28358 for (var i = 0; i < eles.length; i++) {
28359 var caches = eles[i]._private.rscratch.imgLayerCaches;
28360
28361 if (caches) {
28362 caches[lvl] = null;
28363 }
28364 }
28365};
28366
28367LTCp.refineElementTextures = function (eles) {
28368 var self = this; // log('refine', eles.length);
28369
28370 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28371 var rLyr = layer.replacement;
28372
28373 if (!rLyr) {
28374 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28375 rLyr.replaces = layer;
28376 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28377 }
28378
28379 if (!rLyr.reqs) {
28380 for (var i = 0; i < rLyr.eles.length; i++) {
28381 self.queueLayer(rLyr, rLyr.eles[i]);
28382 } // log('queue replacement layer refinement', rLyr.id);
28383
28384 }
28385 });
28386};
28387
28388LTCp.enqueueElementRefinement = function (ele) {
28389
28390 this.eleTxrDeqs.merge(ele);
28391 this.scheduleElementRefinement();
28392};
28393
28394LTCp.queueLayer = function (layer, ele) {
28395 var self = this;
28396 var q = self.layersQueue;
28397 var elesQ = layer.elesQueue;
28398 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28399
28400 if (layer.replacement) {
28401 return;
28402 }
28403
28404 if (ele) {
28405 if (hasId[ele.id()]) {
28406 return;
28407 }
28408
28409 elesQ.push(ele);
28410 hasId[ele.id()] = true;
28411 }
28412
28413 if (layer.reqs) {
28414 layer.reqs++;
28415 q.updateItem(layer);
28416 } else {
28417 layer.reqs = 1;
28418 q.push(layer);
28419 }
28420};
28421
28422LTCp.dequeue = function (pxRatio) {
28423 var self = this;
28424 var q = self.layersQueue;
28425 var deqd = [];
28426 var eleDeqs = 0;
28427
28428 while (eleDeqs < maxDeqSize$1) {
28429 if (q.size() === 0) {
28430 break;
28431 }
28432
28433 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28434
28435 if (layer.replacement) {
28436 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28437 q.pop();
28438 continue;
28439 } // if this is a replacement layer that has been superceded, then forget it
28440
28441
28442 if (layer.replaces && layer !== layer.replaces.replacement) {
28443 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28444 q.pop();
28445 continue;
28446 }
28447
28448 if (layer.invalid) {
28449 // log('replacement layer %s is invalid; dequeued', layer.id);
28450 q.pop();
28451 continue;
28452 }
28453
28454 var ele = layer.elesQueue.shift();
28455
28456 if (ele) {
28457 // log('dequeue layer %s', layer.id);
28458 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28459 eleDeqs++;
28460 }
28461
28462 if (deqd.length === 0) {
28463 // we need only one entry in deqd to queue redrawing etc
28464 deqd.push(true);
28465 } // if the layer has all its eles done, then remove from the queue
28466
28467
28468 if (layer.elesQueue.length === 0) {
28469 q.pop();
28470 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28471 // when a replacement layer is dequeued, it replaces the old layer in the level
28472
28473 if (layer.replaces) {
28474 self.applyLayerReplacement(layer);
28475 }
28476
28477 self.requestRedraw();
28478 }
28479 }
28480
28481 return deqd;
28482};
28483
28484LTCp.applyLayerReplacement = function (layer) {
28485 var self = this;
28486 var layersInLevel = self.layersByLevel[layer.level];
28487 var replaced = layer.replaces;
28488 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28489 // refs would be a mistake (i.e. overwriting the true active layer)
28490
28491 if (index < 0 || replaced.invalid) {
28492 // log('replacement layer would have no effect', layer.id);
28493 return;
28494 }
28495
28496 layersInLevel[index] = layer; // replace level ref
28497 // replace refs in eles
28498
28499 for (var i = 0; i < layer.eles.length; i++) {
28500 var _p = layer.eles[i]._private;
28501 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28502
28503 if (cache) {
28504 cache[layer.level] = layer;
28505 }
28506 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28507
28508
28509 self.requestRedraw();
28510};
28511
28512LTCp.requestRedraw = util(function () {
28513 var r = this.renderer;
28514 r.redrawHint('eles', true);
28515 r.redrawHint('drag', true);
28516 r.redraw();
28517}, 100);
28518LTCp.setupDequeueing = defs.setupDequeueing({
28519 deqRedrawThreshold: deqRedrawThreshold$1,
28520 deqCost: deqCost$1,
28521 deqAvgCost: deqAvgCost$1,
28522 deqNoDrawCost: deqNoDrawCost$1,
28523 deqFastCost: deqFastCost$1,
28524 deq: function deq(self, pxRatio) {
28525 return self.dequeue(pxRatio);
28526 },
28527 onDeqd: noop,
28528 shouldRedraw: trueify,
28529 priority: function priority(self) {
28530 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28531 }
28532});
28533
28534var CRp = {};
28535var impl;
28536
28537function polygon(context, points) {
28538 for (var i = 0; i < points.length; i++) {
28539 var pt = points[i];
28540 context.lineTo(pt.x, pt.y);
28541 }
28542}
28543
28544function triangleBackcurve(context, points, controlPoint) {
28545 var firstPt;
28546
28547 for (var i = 0; i < points.length; i++) {
28548 var pt = points[i];
28549
28550 if (i === 0) {
28551 firstPt = pt;
28552 }
28553
28554 context.lineTo(pt.x, pt.y);
28555 }
28556
28557 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28558}
28559
28560function triangleTee(context, trianglePoints, teePoints) {
28561 if (context.beginPath) {
28562 context.beginPath();
28563 }
28564
28565 var triPts = trianglePoints;
28566
28567 for (var i = 0; i < triPts.length; i++) {
28568 var pt = triPts[i];
28569 context.lineTo(pt.x, pt.y);
28570 }
28571
28572 var teePts = teePoints;
28573 var firstTeePt = teePoints[0];
28574 context.moveTo(firstTeePt.x, firstTeePt.y);
28575
28576 for (var i = 1; i < teePts.length; i++) {
28577 var pt = teePts[i];
28578 context.lineTo(pt.x, pt.y);
28579 }
28580
28581 if (context.closePath) {
28582 context.closePath();
28583 }
28584}
28585
28586function circleTriangle(context, trianglePoints, rx, ry, r) {
28587 if (context.beginPath) {
28588 context.beginPath();
28589 }
28590
28591 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28592 var triPts = trianglePoints;
28593 var firstTrPt = triPts[0];
28594 context.moveTo(firstTrPt.x, firstTrPt.y);
28595
28596 for (var i = 0; i < triPts.length; i++) {
28597 var pt = triPts[i];
28598 context.lineTo(pt.x, pt.y);
28599 }
28600
28601 if (context.closePath) {
28602 context.closePath();
28603 }
28604}
28605
28606function circle(context, rx, ry, r) {
28607 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28608}
28609
28610CRp.arrowShapeImpl = function (name) {
28611 return (impl || (impl = {
28612 'polygon': polygon,
28613 'triangle-backcurve': triangleBackcurve,
28614 'triangle-tee': triangleTee,
28615 'circle-triangle': circleTriangle,
28616 'triangle-cross': triangleTee,
28617 'circle': circle
28618 }))[name];
28619};
28620
28621var CRp$1 = {};
28622
28623CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28624 var r = this;
28625
28626 if (ele.isNode()) {
28627 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28628 } else {
28629 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28630 }
28631};
28632
28633CRp$1.drawElementOverlay = function (context, ele) {
28634 var r = this;
28635
28636 if (ele.isNode()) {
28637 r.drawNodeOverlay(context, ele);
28638 } else {
28639 r.drawEdgeOverlay(context, ele);
28640 }
28641};
28642
28643CRp$1.drawElementUnderlay = function (context, ele) {
28644 var r = this;
28645
28646 if (ele.isNode()) {
28647 r.drawNodeUnderlay(context, ele);
28648 } else {
28649 r.drawEdgeUnderlay(context, ele);
28650 }
28651};
28652
28653CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28654 var r = this;
28655 var bb = eleTxrCache.getBoundingBox(ele);
28656
28657 if (bb.w === 0 || bb.h === 0) {
28658 return;
28659 } // ignore zero size case
28660
28661
28662 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28663
28664 if (eleCache != null) {
28665 var opacity = getOpacity(r, ele);
28666
28667 if (opacity === 0) {
28668 return;
28669 }
28670
28671 var theta = getRotation(r, ele);
28672 var x1 = bb.x1,
28673 y1 = bb.y1,
28674 w = bb.w,
28675 h = bb.h;
28676 var x, y, sx, sy, smooth;
28677
28678 if (theta !== 0) {
28679 var rotPt = eleTxrCache.getRotationPoint(ele);
28680 sx = rotPt.x;
28681 sy = rotPt.y;
28682 context.translate(sx, sy);
28683 context.rotate(theta);
28684 smooth = r.getImgSmoothing(context);
28685
28686 if (!smooth) {
28687 r.setImgSmoothing(context, true);
28688 }
28689
28690 var off = eleTxrCache.getRotationOffset(ele);
28691 x = off.x;
28692 y = off.y;
28693 } else {
28694 x = x1;
28695 y = y1;
28696 }
28697
28698 var oldGlobalAlpha;
28699
28700 if (opacity !== 1) {
28701 oldGlobalAlpha = context.globalAlpha;
28702 context.globalAlpha = oldGlobalAlpha * opacity;
28703 }
28704
28705 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28706
28707 if (opacity !== 1) {
28708 context.globalAlpha = oldGlobalAlpha;
28709 }
28710
28711 if (theta !== 0) {
28712 context.rotate(-theta);
28713 context.translate(-sx, -sy);
28714
28715 if (!smooth) {
28716 r.setImgSmoothing(context, false);
28717 }
28718 }
28719 } else {
28720 eleTxrCache.drawElement(context, ele); // direct draw fallback
28721 }
28722};
28723
28724var getZeroRotation = function getZeroRotation() {
28725 return 0;
28726};
28727
28728var getLabelRotation = function getLabelRotation(r, ele) {
28729 return r.getTextAngle(ele, null);
28730};
28731
28732var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28733 return r.getTextAngle(ele, 'source');
28734};
28735
28736var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28737 return r.getTextAngle(ele, 'target');
28738};
28739
28740var getOpacity = function getOpacity(r, ele) {
28741 return ele.effectiveOpacity();
28742};
28743
28744var getTextOpacity = function getTextOpacity(e, ele) {
28745 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28746};
28747
28748CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28749 var r = this;
28750 var _r$data = r.data,
28751 eleTxrCache = _r$data.eleTxrCache,
28752 lblTxrCache = _r$data.lblTxrCache,
28753 slbTxrCache = _r$data.slbTxrCache,
28754 tlbTxrCache = _r$data.tlbTxrCache;
28755 var bb = ele.boundingBox();
28756 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28757
28758 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28759 return;
28760 }
28761
28762 if (!extent || boundingBoxesIntersect(bb, extent)) {
28763 var isEdge = ele.isEdge();
28764
28765 var badLine = ele.element()._private.rscratch.badLine;
28766
28767 r.drawElementUnderlay(context, ele);
28768 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28769
28770 if (!isEdge || !badLine) {
28771 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28772 }
28773
28774 if (isEdge && !badLine) {
28775 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28776 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28777 }
28778
28779 r.drawElementOverlay(context, ele);
28780 }
28781};
28782
28783CRp$1.drawElements = function (context, eles) {
28784 var r = this;
28785
28786 for (var i = 0; i < eles.length; i++) {
28787 var ele = eles[i];
28788 r.drawElement(context, ele);
28789 }
28790};
28791
28792CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28793 var r = this;
28794
28795 for (var i = 0; i < eles.length; i++) {
28796 var ele = eles[i];
28797 r.drawCachedElement(context, ele, pxRatio, extent);
28798 }
28799};
28800
28801CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28802 var r = this;
28803
28804 for (var i = 0; i < eles.length; i++) {
28805 var ele = eles[i];
28806
28807 if (!ele.isNode()) {
28808 continue;
28809 }
28810
28811 r.drawCachedElement(context, ele, pxRatio, extent);
28812 }
28813};
28814
28815CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28816 var r = this;
28817 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28818
28819 if (layers) {
28820 for (var i = 0; i < layers.length; i++) {
28821 var layer = layers[i];
28822 var bb = layer.bb;
28823
28824 if (bb.w === 0 || bb.h === 0) {
28825 continue;
28826 }
28827
28828 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28829 }
28830 } else {
28831 // fall back on plain caching if no layers
28832 r.drawCachedElements(context, eles, pxRatio, extent);
28833 }
28834};
28835
28836/* global Path2D */
28837var CRp$2 = {};
28838
28839CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28840 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28841 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28842 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28843 var r = this;
28844 var rs = edge._private.rscratch;
28845
28846 if (shouldDrawOpacity && !edge.visible()) {
28847 return;
28848 } // if bezier ctrl pts can not be calculated, then die
28849
28850
28851 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28852 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28853 return;
28854 }
28855
28856 var bb;
28857
28858 if (shiftToOriginWithBb) {
28859 bb = shiftToOriginWithBb;
28860 context.translate(-bb.x1, -bb.y1);
28861 }
28862
28863 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28864 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28865 var curveStyle = edge.pstyle('curve-style').value;
28866 var lineStyle = edge.pstyle('line-style').value;
28867 var edgeWidth = edge.pstyle('width').pfValue;
28868 var lineCap = edge.pstyle('line-cap').value;
28869 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
28870
28871 var effectiveArrowOpacity = opacity * lineOpacity;
28872
28873 var drawLine = function drawLine() {
28874 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28875
28876 if (curveStyle === 'straight-triangle') {
28877 r.eleStrokeStyle(context, edge, strokeOpacity);
28878 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28879 } else {
28880 context.lineWidth = edgeWidth;
28881 context.lineCap = lineCap;
28882 r.eleStrokeStyle(context, edge, strokeOpacity);
28883 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28884 context.lineCap = 'butt'; // reset for other drawing functions
28885 }
28886 };
28887
28888 var drawOverlay = function drawOverlay() {
28889 if (!shouldDrawOverlay) {
28890 return;
28891 }
28892
28893 r.drawEdgeOverlay(context, edge);
28894 };
28895
28896 var drawUnderlay = function drawUnderlay() {
28897 if (!shouldDrawOverlay) {
28898 return;
28899 }
28900
28901 r.drawEdgeUnderlay(context, edge);
28902 };
28903
28904 var drawArrows = function drawArrows() {
28905 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28906 r.drawArrowheads(context, edge, arrowOpacity);
28907 };
28908
28909 var drawText = function drawText() {
28910 r.drawElementText(context, edge, null, drawLabel);
28911 };
28912
28913 context.lineJoin = 'round';
28914 var ghost = edge.pstyle('ghost').value === 'yes';
28915
28916 if (ghost) {
28917 var gx = edge.pstyle('ghost-offset-x').pfValue;
28918 var gy = edge.pstyle('ghost-offset-y').pfValue;
28919 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28920 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28921 context.translate(gx, gy);
28922 drawLine(effectiveGhostOpacity);
28923 drawArrows(effectiveGhostOpacity);
28924 context.translate(-gx, -gy);
28925 }
28926
28927 drawUnderlay();
28928 drawLine();
28929 drawArrows();
28930 drawOverlay();
28931 drawText();
28932
28933 if (shiftToOriginWithBb) {
28934 context.translate(bb.x1, bb.y1);
28935 }
28936};
28937
28938var drawEdgeOverlayUnderlay = function drawEdgeOverlayUnderlay(overlayOrUnderlay) {
28939 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
28940 throw new Error('Invalid state');
28941 }
28942
28943 return function (context, edge) {
28944 if (!edge.visible()) {
28945 return;
28946 }
28947
28948 var opacity = edge.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
28949
28950 if (opacity === 0) {
28951 return;
28952 }
28953
28954 var r = this;
28955 var usePaths = r.usePaths();
28956 var rs = edge._private.rscratch;
28957 var padding = edge.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
28958 var width = 2 * padding;
28959 var color = edge.pstyle("".concat(overlayOrUnderlay, "-color")).value;
28960 context.lineWidth = width;
28961
28962 if (rs.edgeType === 'self' && !usePaths) {
28963 context.lineCap = 'butt';
28964 } else {
28965 context.lineCap = 'round';
28966 }
28967
28968 r.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28969 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28970 };
28971};
28972
28973CRp$2.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
28974CRp$2.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
28975
28976CRp$2.drawEdgePath = function (edge, context, pts, type) {
28977 var rs = edge._private.rscratch;
28978 var canvasCxt = context;
28979 var path;
28980 var pathCacheHit = false;
28981 var usePaths = this.usePaths();
28982 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28983 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28984
28985 if (usePaths) {
28986 var pathCacheKey = pts.join('$');
28987 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28988
28989 if (keyMatches) {
28990 path = context = rs.pathCache;
28991 pathCacheHit = true;
28992 } else {
28993 path = context = new Path2D();
28994 rs.pathCacheKey = pathCacheKey;
28995 rs.pathCache = path;
28996 }
28997 }
28998
28999 if (canvasCxt.setLineDash) {
29000 // for very outofdate browsers
29001 switch (type) {
29002 case 'dotted':
29003 canvasCxt.setLineDash([1, 1]);
29004 break;
29005
29006 case 'dashed':
29007 canvasCxt.setLineDash(lineDashPattern);
29008 canvasCxt.lineDashOffset = lineDashOffset;
29009 break;
29010
29011 case 'solid':
29012 canvasCxt.setLineDash([]);
29013 break;
29014 }
29015 }
29016
29017 if (!pathCacheHit && !rs.badLine) {
29018 if (context.beginPath) {
29019 context.beginPath();
29020 }
29021
29022 context.moveTo(pts[0], pts[1]);
29023
29024 switch (rs.edgeType) {
29025 case 'bezier':
29026 case 'self':
29027 case 'compound':
29028 case 'multibezier':
29029 for (var i = 2; i + 3 < pts.length; i += 4) {
29030 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
29031 }
29032
29033 break;
29034
29035 case 'straight':
29036 case 'segments':
29037 case 'haystack':
29038 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
29039 context.lineTo(pts[_i], pts[_i + 1]);
29040 }
29041
29042 break;
29043 }
29044 }
29045
29046 context = canvasCxt;
29047
29048 if (usePaths) {
29049 context.stroke(path);
29050 } else {
29051 context.stroke();
29052 } // reset any line dashes
29053
29054
29055 if (context.setLineDash) {
29056 // for very outofdate browsers
29057 context.setLineDash([]);
29058 }
29059};
29060
29061CRp$2.drawEdgeTrianglePath = function (edge, context, pts) {
29062 // use line stroke style for triangle fill style
29063 context.fillStyle = context.strokeStyle;
29064 var edgeWidth = edge.pstyle('width').pfValue;
29065
29066 for (var i = 0; i + 1 < pts.length; i += 2) {
29067 var vector = [pts[i + 2] - pts[i], pts[i + 3] - pts[i + 1]];
29068 var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
29069 var normal = [vector[1] / length, -vector[0] / length];
29070 var triangleHead = [normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2];
29071 context.beginPath();
29072 context.moveTo(pts[i] - triangleHead[0], pts[i + 1] - triangleHead[1]);
29073 context.lineTo(pts[i] + triangleHead[0], pts[i + 1] + triangleHead[1]);
29074 context.lineTo(pts[i + 2], pts[i + 3]);
29075 context.closePath();
29076 context.fill();
29077 }
29078};
29079
29080CRp$2.drawArrowheads = function (context, edge, opacity) {
29081 var rs = edge._private.rscratch;
29082 var isHaystack = rs.edgeType === 'haystack';
29083
29084 if (!isHaystack) {
29085 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
29086 }
29087
29088 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
29089 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
29090
29091 if (!isHaystack) {
29092 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
29093 }
29094};
29095
29096CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
29097 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
29098 return;
29099 }
29100
29101 var self = this;
29102 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
29103
29104 if (arrowShape === 'none') {
29105 return;
29106 }
29107
29108 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
29109 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
29110 var edgeWidth = edge.pstyle('width').pfValue;
29111 var edgeOpacity = edge.pstyle('opacity').value;
29112
29113 if (opacity === undefined) {
29114 opacity = edgeOpacity;
29115 }
29116
29117 var gco = context.globalCompositeOperation;
29118
29119 if (opacity !== 1 || arrowFill === 'hollow') {
29120 // then extra clear is needed
29121 context.globalCompositeOperation = 'destination-out';
29122 self.colorFillStyle(context, 255, 255, 255, 1);
29123 self.colorStrokeStyle(context, 255, 255, 255, 1);
29124 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
29125 context.globalCompositeOperation = gco;
29126 } // otherwise, the opaque arrow clears it for free :)
29127
29128
29129 var color = edge.pstyle(prefix + '-arrow-color').value;
29130 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
29131 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
29132 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
29133};
29134
29135CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
29136 var r = this;
29137 var usePaths = this.usePaths() && shape !== 'triangle-cross';
29138 var pathCacheHit = false;
29139 var path;
29140 var canvasContext = context;
29141 var translation = {
29142 x: x,
29143 y: y
29144 };
29145 var scale = edge.pstyle('arrow-scale').value;
29146 var size = this.getArrowWidth(edgeWidth, scale);
29147 var shapeImpl = r.arrowShapes[shape];
29148
29149 if (usePaths) {
29150 var cache = r.arrowPathCache = r.arrowPathCache || [];
29151 var key = hashString(shape);
29152 var cachedPath = cache[key];
29153
29154 if (cachedPath != null) {
29155 path = context = cachedPath;
29156 pathCacheHit = true;
29157 } else {
29158 path = context = new Path2D();
29159 cache[key] = path;
29160 }
29161 }
29162
29163 if (!pathCacheHit) {
29164 if (context.beginPath) {
29165 context.beginPath();
29166 }
29167
29168 if (usePaths) {
29169 // store in the path cache with values easily manipulated later
29170 shapeImpl.draw(context, 1, 0, {
29171 x: 0,
29172 y: 0
29173 }, 1);
29174 } else {
29175 shapeImpl.draw(context, size, angle, translation, edgeWidth);
29176 }
29177
29178 if (context.closePath) {
29179 context.closePath();
29180 }
29181 }
29182
29183 context = canvasContext;
29184
29185 if (usePaths) {
29186 // set transform to arrow position/orientation
29187 context.translate(x, y);
29188 context.rotate(angle);
29189 context.scale(size, size);
29190 }
29191
29192 if (fill === 'filled' || fill === 'both') {
29193 if (usePaths) {
29194 context.fill(path);
29195 } else {
29196 context.fill();
29197 }
29198 }
29199
29200 if (fill === 'hollow' || fill === 'both') {
29201 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
29202 context.lineJoin = 'miter';
29203
29204 if (usePaths) {
29205 context.stroke(path);
29206 } else {
29207 context.stroke();
29208 }
29209 }
29210
29211 if (usePaths) {
29212 // reset transform by applying inverse
29213 context.scale(1 / size, 1 / size);
29214 context.rotate(-angle);
29215 context.translate(-x, -y);
29216 }
29217};
29218
29219var CRp$3 = {};
29220
29221CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29222 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29223 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29224 return;
29225 }
29226
29227 try {
29228 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29229 } catch (e) {
29230 warn(e);
29231 }
29232};
29233
29234CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29235 var r = this;
29236 var pos = node.position();
29237 var nodeX = pos.x;
29238 var nodeY = pos.y;
29239 var styleObj = node.cy().style();
29240 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29241 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29242 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29243 var nodeW = node.width();
29244 var nodeH = node.height();
29245 var paddingX2 = node.padding() * 2;
29246 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29247 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29248 var rs = node._private.rscratch;
29249 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29250 var shouldClip = clip === 'node';
29251 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29252 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29253 var imgW = img.width || img.cachedW;
29254 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29255
29256 if (null == imgW || null == imgH) {
29257 document.body.appendChild(img); // eslint-disable-line no-undef
29258
29259 imgW = img.cachedW = img.width || img.offsetWidth;
29260 imgH = img.cachedH = img.height || img.offsetHeight;
29261 document.body.removeChild(img); // eslint-disable-line no-undef
29262 }
29263
29264 var w = imgW;
29265 var h = imgH;
29266
29267 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29268 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29269 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29270 } else {
29271 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29272 }
29273 }
29274
29275 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29276 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29277 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29278 } else {
29279 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29280 }
29281 }
29282
29283 if (w === 0 || h === 0) {
29284 return; // no point in drawing empty image (and chrome is broken in this case)
29285 }
29286
29287 if (fit === 'contain') {
29288 var scale = Math.min(nodeTW / w, nodeTH / h);
29289 w *= scale;
29290 h *= scale;
29291 } else if (fit === 'cover') {
29292 var scale = Math.max(nodeTW / w, nodeTH / h);
29293 w *= scale;
29294 h *= scale;
29295 }
29296
29297 var x = nodeX - nodeTW / 2; // left
29298
29299 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29300 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29301
29302 if (posXUnits === '%') {
29303 x += (nodeTW - w) * posXPfVal;
29304 } else {
29305 x += posXPfVal;
29306 }
29307
29308 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29309 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29310
29311 if (offXUnits === '%') {
29312 x += (nodeTW - w) * offXPfVal;
29313 } else {
29314 x += offXPfVal;
29315 }
29316
29317 var y = nodeY - nodeTH / 2; // top
29318
29319 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29320 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29321
29322 if (posYUnits === '%') {
29323 y += (nodeTH - h) * posYPfVal;
29324 } else {
29325 y += posYPfVal;
29326 }
29327
29328 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29329 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29330
29331 if (offYUnits === '%') {
29332 y += (nodeTH - h) * offYPfVal;
29333 } else {
29334 y += offYPfVal;
29335 }
29336
29337 if (rs.pathCache) {
29338 x -= nodeX;
29339 y -= nodeY;
29340 nodeX = 0;
29341 nodeY = 0;
29342 }
29343
29344 var gAlpha = context.globalAlpha;
29345 context.globalAlpha = imgOpacity;
29346 var smoothingEnabled = r.getImgSmoothing(context);
29347 var isSmoothingSwitched = false;
29348
29349 if (smooth === 'no' && smoothingEnabled) {
29350 r.setImgSmoothing(context, false);
29351 isSmoothingSwitched = true;
29352 } else if (smooth === 'yes' && !smoothingEnabled) {
29353 r.setImgSmoothing(context, true);
29354 isSmoothingSwitched = true;
29355 }
29356
29357 if (repeat === 'no-repeat') {
29358 if (shouldClip) {
29359 context.save();
29360
29361 if (rs.pathCache) {
29362 context.clip(rs.pathCache);
29363 } else {
29364 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29365 context.clip();
29366 }
29367 }
29368
29369 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29370
29371 if (shouldClip) {
29372 context.restore();
29373 }
29374 } else {
29375 var pattern = context.createPattern(img, repeat);
29376 context.fillStyle = pattern;
29377 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29378 context.translate(x, y);
29379 context.fill();
29380 context.translate(-x, -y);
29381 }
29382
29383 context.globalAlpha = gAlpha;
29384
29385 if (isSmoothingSwitched) {
29386 r.setImgSmoothing(context, smoothingEnabled);
29387 }
29388};
29389
29390var CRp$4 = {};
29391
29392CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29393 if (!scale) {
29394 var zoom = ele.cy().zoom();
29395 var pxRatio = this.getPixelRatio();
29396 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29397
29398 scale = Math.pow(2, lvl);
29399 }
29400
29401 var computedSize = ele.pstyle('font-size').pfValue * scale;
29402 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29403
29404 if (computedSize < minSize) {
29405 return false;
29406 }
29407
29408 return true;
29409};
29410
29411CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29412 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29413 var r = this;
29414
29415 if (force == null) {
29416 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29417 return;
29418 }
29419 } else if (force === false) {
29420 return;
29421 }
29422
29423 if (ele.isNode()) {
29424 var label = ele.pstyle('label');
29425
29426 if (!label || !label.value) {
29427 return;
29428 }
29429
29430 var justification = r.getLabelJustification(ele);
29431 context.textAlign = justification;
29432 context.textBaseline = 'bottom';
29433 } else {
29434 var badLine = ele.element()._private.rscratch.badLine;
29435
29436 var _label = ele.pstyle('label');
29437
29438 var srcLabel = ele.pstyle('source-label');
29439 var tgtLabel = ele.pstyle('target-label');
29440
29441 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29442 return;
29443 }
29444
29445 context.textAlign = 'center';
29446 context.textBaseline = 'bottom';
29447 }
29448
29449 var applyRotation = !shiftToOriginWithBb;
29450 var bb;
29451
29452 if (shiftToOriginWithBb) {
29453 bb = shiftToOriginWithBb;
29454 context.translate(-bb.x1, -bb.y1);
29455 }
29456
29457 if (prefix == null) {
29458 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29459
29460 if (ele.isEdge()) {
29461 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29462 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29463 }
29464 } else {
29465 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29466 }
29467
29468 if (shiftToOriginWithBb) {
29469 context.translate(bb.x1, bb.y1);
29470 }
29471};
29472
29473CRp$4.getFontCache = function (context) {
29474 var cache;
29475 this.fontCaches = this.fontCaches || [];
29476
29477 for (var i = 0; i < this.fontCaches.length; i++) {
29478 cache = this.fontCaches[i];
29479
29480 if (cache.context === context) {
29481 return cache;
29482 }
29483 }
29484
29485 cache = {
29486 context: context
29487 };
29488 this.fontCaches.push(cache);
29489 return cache;
29490}; // set up canvas context with font
29491// returns transformed text string
29492
29493
29494CRp$4.setupTextStyle = function (context, ele) {
29495 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29496 // Font style
29497 var labelStyle = ele.pstyle('font-style').strValue;
29498 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29499 var labelFamily = ele.pstyle('font-family').strValue;
29500 var labelWeight = ele.pstyle('font-weight').strValue;
29501 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29502 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29503 var color = ele.pstyle('color').value;
29504 var outlineColor = ele.pstyle('text-outline-color').value;
29505 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29506 context.lineJoin = 'round'; // so text outlines aren't jagged
29507
29508 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29509 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29510}; // TODO ensure re-used
29511
29512
29513function roundRect(ctx, x, y, width, height) {
29514 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29515 ctx.beginPath();
29516 ctx.moveTo(x + radius, y);
29517 ctx.lineTo(x + width - radius, y);
29518 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29519 ctx.lineTo(x + width, y + height - radius);
29520 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29521 ctx.lineTo(x + radius, y + height);
29522 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29523 ctx.lineTo(x, y + radius);
29524 ctx.quadraticCurveTo(x, y, x + radius, y);
29525 ctx.closePath();
29526 ctx.fill();
29527}
29528
29529CRp$4.getTextAngle = function (ele, prefix) {
29530 var theta;
29531 var _p = ele._private;
29532 var rscratch = _p.rscratch;
29533 var pdash = prefix ? prefix + '-' : '';
29534 var rotation = ele.pstyle(pdash + 'text-rotation');
29535 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29536
29537 if (rotation.strValue === 'autorotate') {
29538 theta = ele.isEdge() ? textAngle : 0;
29539 } else if (rotation.strValue === 'none') {
29540 theta = 0;
29541 } else {
29542 theta = rotation.pfValue;
29543 }
29544
29545 return theta;
29546};
29547
29548CRp$4.drawText = function (context, ele, prefix) {
29549 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29550 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29551 var _p = ele._private;
29552 var rscratch = _p.rscratch;
29553 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29554
29555 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29556 return;
29557 } // use 'main' as an alias for the main label (i.e. null prefix)
29558
29559
29560 if (prefix === 'main') {
29561 prefix = null;
29562 }
29563
29564 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29565 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29566 var orgTextX, orgTextY; // used for rotation
29567
29568 var text = this.getLabelText(ele, prefix);
29569
29570 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29571 this.setupTextStyle(context, ele, useEleOpacity);
29572 var pdash = prefix ? prefix + '-' : '';
29573 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29574 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29575 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29576 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29577 var isEdge = ele.isEdge();
29578 var halign = ele.pstyle('text-halign').value;
29579 var valign = ele.pstyle('text-valign').value;
29580
29581 if (isEdge) {
29582 halign = 'center';
29583 valign = 'center';
29584 }
29585
29586 textX += marginX;
29587 textY += marginY;
29588 var theta;
29589
29590 if (!applyRotation) {
29591 theta = 0;
29592 } else {
29593 theta = this.getTextAngle(ele, prefix);
29594 }
29595
29596 if (theta !== 0) {
29597 orgTextX = textX;
29598 orgTextY = textY;
29599 context.translate(orgTextX, orgTextY);
29600 context.rotate(theta);
29601 textX = 0;
29602 textY = 0;
29603 }
29604
29605 switch (valign) {
29606 case 'top':
29607 break;
29608
29609 case 'center':
29610 textY += textH / 2;
29611 break;
29612
29613 case 'bottom':
29614 textY += textH;
29615 break;
29616 }
29617
29618 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29619 var borderOpacity = ele.pstyle('text-border-opacity').value;
29620 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29621 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29622
29623 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29624 var bgX = textX - backgroundPadding;
29625
29626 switch (halign) {
29627 case 'left':
29628 bgX -= textW;
29629 break;
29630
29631 case 'center':
29632 bgX -= textW / 2;
29633 break;
29634 }
29635
29636 var bgY = textY - textH - backgroundPadding;
29637 var bgW = textW + 2 * backgroundPadding;
29638 var bgH = textH + 2 * backgroundPadding;
29639
29640 if (backgroundOpacity > 0) {
29641 var textFill = context.fillStyle;
29642 var textBackgroundColor = ele.pstyle('text-background-color').value;
29643 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29644 var styleShape = ele.pstyle('text-background-shape').strValue;
29645
29646 if (styleShape.indexOf('round') === 0) {
29647 roundRect(context, bgX, bgY, bgW, bgH, 2);
29648 } else {
29649 context.fillRect(bgX, bgY, bgW, bgH);
29650 }
29651
29652 context.fillStyle = textFill;
29653 }
29654
29655 if (textBorderWidth > 0 && borderOpacity > 0) {
29656 var textStroke = context.strokeStyle;
29657 var textLineWidth = context.lineWidth;
29658 var textBorderColor = ele.pstyle('text-border-color').value;
29659 var textBorderStyle = ele.pstyle('text-border-style').value;
29660 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29661 context.lineWidth = textBorderWidth;
29662
29663 if (context.setLineDash) {
29664 // for very outofdate browsers
29665 switch (textBorderStyle) {
29666 case 'dotted':
29667 context.setLineDash([1, 1]);
29668 break;
29669
29670 case 'dashed':
29671 context.setLineDash([4, 2]);
29672 break;
29673
29674 case 'double':
29675 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29676
29677 context.setLineDash([]);
29678 break;
29679
29680 case 'solid':
29681 context.setLineDash([]);
29682 break;
29683 }
29684 }
29685
29686 context.strokeRect(bgX, bgY, bgW, bgH);
29687
29688 if (textBorderStyle === 'double') {
29689 var whiteWidth = textBorderWidth / 2;
29690 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29691 }
29692
29693 if (context.setLineDash) {
29694 // for very outofdate browsers
29695 context.setLineDash([]);
29696 }
29697
29698 context.lineWidth = textLineWidth;
29699 context.strokeStyle = textStroke;
29700 }
29701 }
29702
29703 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29704
29705 if (lineWidth > 0) {
29706 context.lineWidth = lineWidth;
29707 }
29708
29709 if (ele.pstyle('text-wrap').value === 'wrap') {
29710 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29711 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29712 var halfTextW = textW / 2;
29713 var justification = this.getLabelJustification(ele);
29714
29715 if (justification === 'auto') ; else if (halign === 'left') {
29716 // auto justification : right
29717 if (justification === 'left') {
29718 textX += -textW;
29719 } else if (justification === 'center') {
29720 textX += -halfTextW;
29721 } // else same as auto
29722
29723 } else if (halign === 'center') {
29724 // auto justfication : center
29725 if (justification === 'left') {
29726 textX += -halfTextW;
29727 } else if (justification === 'right') {
29728 textX += halfTextW;
29729 } // else same as auto
29730
29731 } else if (halign === 'right') {
29732 // auto justification : left
29733 if (justification === 'center') {
29734 textX += halfTextW;
29735 } else if (justification === 'right') {
29736 textX += textW;
29737 } // else same as auto
29738
29739 }
29740
29741 switch (valign) {
29742 case 'top':
29743 textY -= (lines.length - 1) * lineHeight;
29744 break;
29745
29746 case 'center':
29747 case 'bottom':
29748 textY -= (lines.length - 1) * lineHeight;
29749 break;
29750 }
29751
29752 for (var l = 0; l < lines.length; l++) {
29753 if (lineWidth > 0) {
29754 context.strokeText(lines[l], textX, textY);
29755 }
29756
29757 context.fillText(lines[l], textX, textY);
29758 textY += lineHeight;
29759 }
29760 } else {
29761 if (lineWidth > 0) {
29762 context.strokeText(text, textX, textY);
29763 }
29764
29765 context.fillText(text, textX, textY);
29766 }
29767
29768 if (theta !== 0) {
29769 context.rotate(-theta);
29770 context.translate(-orgTextX, -orgTextY);
29771 }
29772 }
29773};
29774
29775/* global Path2D */
29776var CRp$5 = {};
29777
29778CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29779 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29780 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29781 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29782 var r = this;
29783 var nodeWidth, nodeHeight;
29784 var _p = node._private;
29785 var rs = _p.rscratch;
29786 var pos = node.position();
29787
29788 if (!number(pos.x) || !number(pos.y)) {
29789 return; // can't draw node with undefined position
29790 }
29791
29792 if (shouldDrawOpacity && !node.visible()) {
29793 return;
29794 }
29795
29796 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29797 var usePaths = r.usePaths();
29798 var path;
29799 var pathCacheHit = false;
29800 var padding = node.padding();
29801 nodeWidth = node.width() + 2 * padding;
29802 nodeHeight = node.height() + 2 * padding; //
29803 // setup shift
29804
29805 var bb;
29806
29807 if (shiftToOriginWithBb) {
29808 bb = shiftToOriginWithBb;
29809 context.translate(-bb.x1, -bb.y1);
29810 } //
29811 // load bg image
29812
29813
29814 var bgImgProp = node.pstyle('background-image');
29815 var urls = bgImgProp.value;
29816 var urlDefined = new Array(urls.length);
29817 var image = new Array(urls.length);
29818 var numImages = 0;
29819
29820 for (var i = 0; i < urls.length; i++) {
29821 var url = urls[i];
29822 var defd = urlDefined[i] = url != null && url !== 'none';
29823
29824 if (defd) {
29825 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29826 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29827
29828 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29829 _p.backgroundTimestamp = Date.now();
29830 node.emitAndNotify('background');
29831 });
29832 }
29833 } //
29834 // setup styles
29835
29836
29837 var darkness = node.pstyle('background-blacken').value;
29838 var borderWidth = node.pstyle('border-width').pfValue;
29839 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29840 var borderColor = node.pstyle('border-color').value;
29841 var borderStyle = node.pstyle('border-style').value;
29842 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29843 context.lineJoin = 'miter'; // so borders are square with the node shape
29844
29845 var setupShapeColor = function setupShapeColor() {
29846 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29847 r.eleFillStyle(context, node, bgOpy);
29848 };
29849
29850 var setupBorderColor = function setupBorderColor() {
29851 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29852 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29853 }; //
29854 // setup shape
29855
29856
29857 var styleShape = node.pstyle('shape').strValue;
29858 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29859
29860 if (usePaths) {
29861 context.translate(pos.x, pos.y);
29862 var pathCache = r.nodePathCache = r.nodePathCache || [];
29863 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29864 var cachedPath = pathCache[key];
29865
29866 if (cachedPath != null) {
29867 path = cachedPath;
29868 pathCacheHit = true;
29869 rs.pathCache = path;
29870 } else {
29871 path = new Path2D();
29872 pathCache[key] = rs.pathCache = path;
29873 }
29874 }
29875
29876 var drawShape = function drawShape() {
29877 if (!pathCacheHit) {
29878 var npos = pos;
29879
29880 if (usePaths) {
29881 npos = {
29882 x: 0,
29883 y: 0
29884 };
29885 }
29886
29887 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29888 }
29889
29890 if (usePaths) {
29891 context.fill(path);
29892 } else {
29893 context.fill();
29894 }
29895 };
29896
29897 var drawImages = function drawImages() {
29898 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29899 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29900 var prevBging = _p.backgrounding;
29901 var totalCompleted = 0;
29902
29903 for (var _i = 0; _i < image.length; _i++) {
29904 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29905
29906 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29907 totalCompleted++;
29908 continue;
29909 }
29910
29911 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29912 totalCompleted++;
29913 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29914 }
29915 }
29916
29917 _p.backgrounding = !(totalCompleted === numImages);
29918
29919 if (prevBging !== _p.backgrounding) {
29920 // update style b/c :backgrounding state changed
29921 node.updateStyle(false);
29922 }
29923 };
29924
29925 var drawPie = function drawPie() {
29926 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29927 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29928
29929 if (r.hasPie(node)) {
29930 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29931
29932 if (redrawShape) {
29933 if (!usePaths) {
29934 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29935 }
29936 }
29937 }
29938 };
29939
29940 var darken = function darken() {
29941 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29942 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29943 var c = darkness > 0 ? 0 : 255;
29944
29945 if (darkness !== 0) {
29946 r.colorFillStyle(context, c, c, c, opacity);
29947
29948 if (usePaths) {
29949 context.fill(path);
29950 } else {
29951 context.fill();
29952 }
29953 }
29954 };
29955
29956 var drawBorder = function drawBorder() {
29957 if (borderWidth > 0) {
29958 context.lineWidth = borderWidth;
29959 context.lineCap = 'butt';
29960
29961 if (context.setLineDash) {
29962 // for very outofdate browsers
29963 switch (borderStyle) {
29964 case 'dotted':
29965 context.setLineDash([1, 1]);
29966 break;
29967
29968 case 'dashed':
29969 context.setLineDash([4, 2]);
29970 break;
29971
29972 case 'solid':
29973 case 'double':
29974 context.setLineDash([]);
29975 break;
29976 }
29977 }
29978
29979 if (usePaths) {
29980 context.stroke(path);
29981 } else {
29982 context.stroke();
29983 }
29984
29985 if (borderStyle === 'double') {
29986 context.lineWidth = borderWidth / 3;
29987 var gco = context.globalCompositeOperation;
29988 context.globalCompositeOperation = 'destination-out';
29989
29990 if (usePaths) {
29991 context.stroke(path);
29992 } else {
29993 context.stroke();
29994 }
29995
29996 context.globalCompositeOperation = gco;
29997 } // reset in case we changed the border style
29998
29999
30000 if (context.setLineDash) {
30001 // for very outofdate browsers
30002 context.setLineDash([]);
30003 }
30004 }
30005 };
30006
30007 var drawOverlay = function drawOverlay() {
30008 if (shouldDrawOverlay) {
30009 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
30010 }
30011 };
30012
30013 var drawUnderlay = function drawUnderlay() {
30014 if (shouldDrawOverlay) {
30015 r.drawNodeUnderlay(context, node, pos, nodeWidth, nodeHeight);
30016 }
30017 };
30018
30019 var drawText = function drawText() {
30020 r.drawElementText(context, node, null, drawLabel);
30021 };
30022
30023 var ghost = node.pstyle('ghost').value === 'yes';
30024
30025 if (ghost) {
30026 var gx = node.pstyle('ghost-offset-x').pfValue;
30027 var gy = node.pstyle('ghost-offset-y').pfValue;
30028 var ghostOpacity = node.pstyle('ghost-opacity').value;
30029 var effGhostOpacity = ghostOpacity * eleOpacity;
30030 context.translate(gx, gy);
30031 setupShapeColor(ghostOpacity * bgOpacity);
30032 drawShape();
30033 drawImages(effGhostOpacity, true);
30034 setupBorderColor(ghostOpacity * borderOpacity);
30035 drawBorder();
30036 drawPie(darkness !== 0 || borderWidth !== 0);
30037 drawImages(effGhostOpacity, false);
30038 darken(effGhostOpacity);
30039 context.translate(-gx, -gy);
30040 }
30041
30042 if (usePaths) {
30043 context.translate(-pos.x, -pos.y);
30044 }
30045
30046 drawUnderlay();
30047
30048 if (usePaths) {
30049 context.translate(pos.x, pos.y);
30050 }
30051
30052 setupShapeColor();
30053 drawShape();
30054 drawImages(eleOpacity, true);
30055 setupBorderColor();
30056 drawBorder();
30057 drawPie(darkness !== 0 || borderWidth !== 0);
30058 drawImages(eleOpacity, false);
30059 darken();
30060
30061 if (usePaths) {
30062 context.translate(-pos.x, -pos.y);
30063 }
30064
30065 drawText();
30066 drawOverlay(); //
30067 // clean up shift
30068
30069 if (shiftToOriginWithBb) {
30070 context.translate(bb.x1, bb.y1);
30071 }
30072};
30073
30074var drawNodeOverlayUnderlay = function drawNodeOverlayUnderlay(overlayOrUnderlay) {
30075 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
30076 throw new Error('Invalid state');
30077 }
30078
30079 return function (context, node, pos, nodeWidth, nodeHeight) {
30080 var r = this;
30081
30082 if (!node.visible()) {
30083 return;
30084 }
30085
30086 var padding = node.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
30087 var opacity = node.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
30088 var color = node.pstyle("".concat(overlayOrUnderlay, "-color")).value;
30089 var shape = node.pstyle("".concat(overlayOrUnderlay, "-shape")).value;
30090
30091 if (opacity > 0) {
30092 pos = pos || node.position();
30093
30094 if (nodeWidth == null || nodeHeight == null) {
30095 var _padding = node.padding();
30096
30097 nodeWidth = node.width() + 2 * _padding;
30098 nodeHeight = node.height() + 2 * _padding;
30099 }
30100
30101 r.colorFillStyle(context, color[0], color[1], color[2], opacity);
30102 r.nodeShapes[shape].draw(context, pos.x, pos.y, nodeWidth + padding * 2, nodeHeight + padding * 2);
30103 context.fill();
30104 }
30105 };
30106};
30107
30108CRp$5.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
30109CRp$5.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay'); // does the node have at least one pie piece?
30110
30111CRp$5.hasPie = function (node) {
30112 node = node[0]; // ensure ele ref
30113
30114 return node._private.hasPie;
30115};
30116
30117CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
30118 node = node[0]; // ensure ele ref
30119
30120 pos = pos || node.position();
30121 var cyStyle = node.cy().style();
30122 var pieSize = node.pstyle('pie-size');
30123 var x = pos.x;
30124 var y = pos.y;
30125 var nodeW = node.width();
30126 var nodeH = node.height();
30127 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
30128
30129 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
30130
30131 var usePaths = this.usePaths();
30132
30133 if (usePaths) {
30134 x = 0;
30135 y = 0;
30136 }
30137
30138 if (pieSize.units === '%') {
30139 radius = radius * pieSize.pfValue;
30140 } else if (pieSize.pfValue !== undefined) {
30141 radius = pieSize.pfValue / 2;
30142 }
30143
30144 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
30145 // 1..N
30146 var size = node.pstyle('pie-' + i + '-background-size').value;
30147 var color = node.pstyle('pie-' + i + '-background-color').value;
30148 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
30149 var percent = size / 100; // map integer range [0, 100] to [0, 1]
30150 // percent can't push beyond 1
30151
30152 if (percent + lastPercent > 1) {
30153 percent = 1 - lastPercent;
30154 }
30155
30156 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
30157
30158 var angleDelta = 2 * Math.PI * percent;
30159 var angleEnd = angleStart + angleDelta; // ignore if
30160 // - zero size
30161 // - we're already beyond the full circle
30162 // - adding the current slice would go beyond the full circle
30163
30164 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
30165 continue;
30166 }
30167
30168 context.beginPath();
30169 context.moveTo(x, y);
30170 context.arc(x, y, radius, angleStart, angleEnd);
30171 context.closePath();
30172 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30173 context.fill();
30174 lastPercent += percent;
30175 }
30176};
30177
30178var CRp$6 = {};
30179var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
30180
30181CRp$6.getPixelRatio = function () {
30182 var context = this.data.contexts[0];
30183
30184 if (this.forcedPixelRatio != null) {
30185 return this.forcedPixelRatio;
30186 }
30187
30188 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
30189 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
30190};
30191
30192CRp$6.paintCache = function (context) {
30193 var caches = this.paintCaches = this.paintCaches || [];
30194 var needToCreateCache = true;
30195 var cache;
30196
30197 for (var i = 0; i < caches.length; i++) {
30198 cache = caches[i];
30199
30200 if (cache.context === context) {
30201 needToCreateCache = false;
30202 break;
30203 }
30204 }
30205
30206 if (needToCreateCache) {
30207 cache = {
30208 context: context
30209 };
30210 caches.push(cache);
30211 }
30212
30213 return cache;
30214};
30215
30216CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
30217 var gradientStyle;
30218 var usePaths = this.usePaths();
30219 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
30220 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
30221
30222 if (fill === 'radial-gradient') {
30223 if (ele.isEdge()) {
30224 var start = ele.sourceEndpoint(),
30225 end = ele.targetEndpoint(),
30226 mid = ele.midpoint();
30227 var d1 = dist(start, mid);
30228 var d2 = dist(end, mid);
30229 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
30230 } else {
30231 var pos = usePaths ? {
30232 x: 0,
30233 y: 0
30234 } : ele.position(),
30235 width = ele.paddedWidth(),
30236 height = ele.paddedHeight();
30237 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
30238 }
30239 } else {
30240 if (ele.isEdge()) {
30241 var _start = ele.sourceEndpoint(),
30242 _end = ele.targetEndpoint();
30243
30244 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30245 } else {
30246 var _pos = usePaths ? {
30247 x: 0,
30248 y: 0
30249 } : ele.position(),
30250 _width = ele.paddedWidth(),
30251 _height = ele.paddedHeight(),
30252 halfWidth = _width / 2,
30253 halfHeight = _height / 2;
30254
30255 var direction = ele.pstyle('background-gradient-direction').value;
30256
30257 switch (direction) {
30258 case 'to-bottom':
30259 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30260 break;
30261
30262 case 'to-top':
30263 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30264 break;
30265
30266 case 'to-left':
30267 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30268 break;
30269
30270 case 'to-right':
30271 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30272 break;
30273
30274 case 'to-bottom-right':
30275 case 'to-right-bottom':
30276 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30277 break;
30278
30279 case 'to-top-right':
30280 case 'to-right-top':
30281 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30282 break;
30283
30284 case 'to-bottom-left':
30285 case 'to-left-bottom':
30286 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30287 break;
30288
30289 case 'to-top-left':
30290 case 'to-left-top':
30291 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30292 break;
30293 }
30294 }
30295 }
30296
30297 if (!gradientStyle) return null; // invalid gradient style
30298
30299 var hasPositions = positions.length === colors.length;
30300 var length = colors.length;
30301
30302 for (var i = 0; i < length; i++) {
30303 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30304 }
30305
30306 return gradientStyle;
30307};
30308
30309CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30310 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30311 if (!gradientStyle) return null; // error
30312
30313 context.fillStyle = gradientStyle;
30314};
30315
30316CRp$6.colorFillStyle = function (context, r, g, b, a) {
30317 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30318 // var cache = this.paintCache(context);
30319 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30320 // if( cache.fillStyle !== fillStyle ){
30321 // context.fillStyle = cache.fillStyle = fillStyle;
30322 // }
30323};
30324
30325CRp$6.eleFillStyle = function (context, ele, opacity) {
30326 var backgroundFill = ele.pstyle('background-fill').value;
30327
30328 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30329 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30330 } else {
30331 var backgroundColor = ele.pstyle('background-color').value;
30332 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30333 }
30334};
30335
30336CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30337 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30338 if (!gradientStyle) return null; // error
30339
30340 context.strokeStyle = gradientStyle;
30341};
30342
30343CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30344 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30345 // var cache = this.paintCache(context);
30346 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30347 // if( cache.strokeStyle !== strokeStyle ){
30348 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30349 // }
30350};
30351
30352CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30353 var lineFill = ele.pstyle('line-fill').value;
30354
30355 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30356 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30357 } else {
30358 var lineColor = ele.pstyle('line-color').value;
30359 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30360 }
30361}; // Resize canvas
30362
30363
30364CRp$6.matchCanvasSize = function (container) {
30365 var r = this;
30366 var data = r.data;
30367 var bb = r.findContainerClientCoords();
30368 var width = bb[2];
30369 var height = bb[3];
30370 var pixelRatio = r.getPixelRatio();
30371 var mbPxRatio = r.motionBlurPxRatio;
30372
30373 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30374 pixelRatio = mbPxRatio;
30375 }
30376
30377 var canvasWidth = width * pixelRatio;
30378 var canvasHeight = height * pixelRatio;
30379 var canvas;
30380
30381 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30382 return; // save cycles if same
30383 }
30384
30385 r.fontCaches = null; // resizing resets the style
30386
30387 var canvasContainer = data.canvasContainer;
30388 canvasContainer.style.width = width + 'px';
30389 canvasContainer.style.height = height + 'px';
30390
30391 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30392 canvas = data.canvases[i];
30393 canvas.width = canvasWidth;
30394 canvas.height = canvasHeight;
30395 canvas.style.width = width + 'px';
30396 canvas.style.height = height + 'px';
30397 }
30398
30399 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30400 canvas = data.bufferCanvases[i];
30401 canvas.width = canvasWidth;
30402 canvas.height = canvasHeight;
30403 canvas.style.width = width + 'px';
30404 canvas.style.height = height + 'px';
30405 }
30406
30407 r.textureMult = 1;
30408
30409 if (pixelRatio <= 1) {
30410 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30411 r.textureMult = 2;
30412 canvas.width = canvasWidth * r.textureMult;
30413 canvas.height = canvasHeight * r.textureMult;
30414 }
30415
30416 r.canvasWidth = canvasWidth;
30417 r.canvasHeight = canvasHeight;
30418};
30419
30420CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30421 this.render({
30422 forcedContext: cxt,
30423 forcedZoom: zoom,
30424 forcedPan: pan,
30425 drawAllLayers: true,
30426 forcedPxRatio: pxRatio
30427 });
30428};
30429
30430CRp$6.render = function (options) {
30431 options = options || staticEmptyObject();
30432 var forcedContext = options.forcedContext;
30433 var drawAllLayers = options.drawAllLayers;
30434 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30435 var forcedZoom = options.forcedZoom;
30436 var forcedPan = options.forcedPan;
30437 var r = this;
30438 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30439 var cy = r.cy;
30440 var data = r.data;
30441 var needDraw = data.canvasNeedsRedraw;
30442 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30443 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30444 var mbPxRatio = r.motionBlurPxRatio;
30445 var hasCompoundNodes = cy.hasCompoundNodes();
30446 var inNodeDragGesture = r.hoverData.draggingEles;
30447 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30448 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30449 var motionBlurFadeEffect = motionBlur;
30450
30451 if (!forcedContext) {
30452 if (r.prevPxRatio !== pixelRatio) {
30453 r.invalidateContainerClientCoordsCache();
30454 r.matchCanvasSize(r.container);
30455 r.redrawHint('eles', true);
30456 r.redrawHint('drag', true);
30457 }
30458
30459 r.prevPxRatio = pixelRatio;
30460 }
30461
30462 if (!forcedContext && r.motionBlurTimeout) {
30463 clearTimeout(r.motionBlurTimeout);
30464 }
30465
30466 if (motionBlur) {
30467 if (r.mbFrames == null) {
30468 r.mbFrames = 0;
30469 }
30470
30471 r.mbFrames++;
30472
30473 if (r.mbFrames < 3) {
30474 // need several frames before even high quality motionblur
30475 motionBlurFadeEffect = false;
30476 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30477
30478
30479 if (r.mbFrames > r.minMbLowQualFrames) {
30480 //r.fullQualityMb = false;
30481 r.motionBlurPxRatio = r.mbPxRBlurry;
30482 }
30483 }
30484
30485 if (r.clearingMotionBlur) {
30486 r.motionBlurPxRatio = 1;
30487 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30488 // because a rogue async texture frame would clear needDraw
30489
30490
30491 if (r.textureDrawLastFrame && !textureDraw) {
30492 needDraw[r.NODE] = true;
30493 needDraw[r.SELECT_BOX] = true;
30494 }
30495
30496 var style = cy.style();
30497 var zoom = cy.zoom();
30498 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30499 var pan = cy.pan();
30500 var effectivePan = {
30501 x: pan.x,
30502 y: pan.y
30503 };
30504 var vp = {
30505 zoom: zoom,
30506 pan: {
30507 x: pan.x,
30508 y: pan.y
30509 }
30510 };
30511 var prevVp = r.prevViewport;
30512 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)
30513
30514 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30515 r.motionBlurPxRatio = 1;
30516 }
30517
30518 if (forcedPan) {
30519 effectivePan = forcedPan;
30520 } // apply pixel ratio
30521
30522
30523 effectiveZoom *= pixelRatio;
30524 effectivePan.x *= pixelRatio;
30525 effectivePan.y *= pixelRatio;
30526 var eles = r.getCachedZSortedEles();
30527
30528 function mbclear(context, x, y, w, h) {
30529 var gco = context.globalCompositeOperation;
30530 context.globalCompositeOperation = 'destination-out';
30531 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30532 context.fillRect(x, y, w, h);
30533 context.globalCompositeOperation = gco;
30534 }
30535
30536 function setContextTransform(context, clear) {
30537 var ePan, eZoom, w, h;
30538
30539 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30540 ePan = {
30541 x: pan.x * mbPxRatio,
30542 y: pan.y * mbPxRatio
30543 };
30544 eZoom = zoom * mbPxRatio;
30545 w = r.canvasWidth * mbPxRatio;
30546 h = r.canvasHeight * mbPxRatio;
30547 } else {
30548 ePan = effectivePan;
30549 eZoom = effectiveZoom;
30550 w = r.canvasWidth;
30551 h = r.canvasHeight;
30552 }
30553
30554 context.setTransform(1, 0, 0, 1, 0, 0);
30555
30556 if (clear === 'motionBlur') {
30557 mbclear(context, 0, 0, w, h);
30558 } else if (!forcedContext && (clear === undefined || clear)) {
30559 context.clearRect(0, 0, w, h);
30560 }
30561
30562 if (!drawAllLayers) {
30563 context.translate(ePan.x, ePan.y);
30564 context.scale(eZoom, eZoom);
30565 }
30566
30567 if (forcedPan) {
30568 context.translate(forcedPan.x, forcedPan.y);
30569 }
30570
30571 if (forcedZoom) {
30572 context.scale(forcedZoom, forcedZoom);
30573 }
30574 }
30575
30576 if (!textureDraw) {
30577 r.textureDrawLastFrame = false;
30578 }
30579
30580 if (textureDraw) {
30581 r.textureDrawLastFrame = true;
30582
30583 if (!r.textureCache) {
30584 r.textureCache = {};
30585 r.textureCache.bb = cy.mutableElements().boundingBox();
30586 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30587 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30588 cxt.setTransform(1, 0, 0, 1, 0, 0);
30589 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30590 r.render({
30591 forcedContext: cxt,
30592 drawOnlyNodeLayer: true,
30593 forcedPxRatio: pixelRatio * r.textureMult
30594 });
30595 var vp = r.textureCache.viewport = {
30596 zoom: cy.zoom(),
30597 pan: cy.pan(),
30598 width: r.canvasWidth,
30599 height: r.canvasHeight
30600 };
30601 vp.mpan = {
30602 x: (0 - vp.pan.x) / vp.zoom,
30603 y: (0 - vp.pan.y) / vp.zoom
30604 };
30605 }
30606
30607 needDraw[r.DRAG] = false;
30608 needDraw[r.NODE] = false;
30609 var context = data.contexts[r.NODE];
30610 var texture = r.textureCache.texture;
30611 var vp = r.textureCache.viewport;
30612 context.setTransform(1, 0, 0, 1, 0, 0);
30613
30614 if (motionBlur) {
30615 mbclear(context, 0, 0, vp.width, vp.height);
30616 } else {
30617 context.clearRect(0, 0, vp.width, vp.height);
30618 }
30619
30620 var outsideBgColor = style.core('outside-texture-bg-color').value;
30621 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30622 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30623 context.fillRect(0, 0, vp.width, vp.height);
30624 var zoom = cy.zoom();
30625 setContextTransform(context, false);
30626 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30627 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30628 } else if (r.textureOnViewport && !forcedContext) {
30629 // clear the cache since we don't need it
30630 r.textureCache = null;
30631 }
30632
30633 var extent = cy.extent();
30634 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30635 var hideEdges = r.hideEdgesOnViewport && vpManip;
30636 var needMbClear = [];
30637 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30638
30639 if (needMbClear[r.NODE]) {
30640 r.clearedForMotionBlur[r.NODE] = true;
30641 }
30642
30643 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30644
30645 if (needMbClear[r.DRAG]) {
30646 r.clearedForMotionBlur[r.DRAG] = true;
30647 }
30648
30649 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30650 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30651 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30652 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30653 setContextTransform(context, clear);
30654
30655 if (hideEdges) {
30656 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30657 } else {
30658 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30659 }
30660
30661 if (r.debug) {
30662 r.drawDebugPoints(context, eles.nondrag);
30663 }
30664
30665 if (!drawAllLayers && !motionBlur) {
30666 needDraw[r.NODE] = false;
30667 }
30668 }
30669
30670 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30671 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30672 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30673 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30674
30675 if (hideEdges) {
30676 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30677 } else {
30678 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30679 }
30680
30681 if (r.debug) {
30682 r.drawDebugPoints(context, eles.drag);
30683 }
30684
30685 if (!drawAllLayers && !motionBlur) {
30686 needDraw[r.DRAG] = false;
30687 }
30688 }
30689
30690 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30691 var context = forcedContext || data.contexts[r.SELECT_BOX];
30692 setContextTransform(context);
30693
30694 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30695 var zoom = r.cy.zoom();
30696 var borderWidth = style.core('selection-box-border-width').value / zoom;
30697 context.lineWidth = borderWidth;
30698 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 + ')';
30699 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30700
30701 if (borderWidth > 0) {
30702 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 + ')';
30703 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30704 }
30705 }
30706
30707 if (data.bgActivePosistion && !r.hoverData.selecting) {
30708 var zoom = r.cy.zoom();
30709 var pos = data.bgActivePosistion;
30710 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 + ')';
30711 context.beginPath();
30712 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30713 context.fill();
30714 }
30715
30716 var timeToRender = r.lastRedrawTime;
30717
30718 if (r.showFps && timeToRender) {
30719 timeToRender = Math.round(timeToRender);
30720 var fps = Math.round(1000 / timeToRender);
30721 context.setTransform(1, 0, 0, 1, 0, 0);
30722 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30723 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30724 context.lineWidth = 1;
30725 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30726 var maxFps = 60;
30727 context.strokeRect(0, 30, 250, 20);
30728 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30729 }
30730
30731 if (!drawAllLayers) {
30732 needDraw[r.SELECT_BOX] = false;
30733 }
30734 } // motionblur: blit rendered blurry frames
30735
30736
30737 if (motionBlur && mbPxRatio !== 1) {
30738 var cxtNode = data.contexts[r.NODE];
30739 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30740 var cxtDrag = data.contexts[r.DRAG];
30741 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30742
30743 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30744 cxt.setTransform(1, 0, 0, 1, 0, 0);
30745
30746 if (needClear || !motionBlurFadeEffect) {
30747 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30748 } else {
30749 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30750 }
30751
30752 var pxr = mbPxRatio;
30753 cxt.drawImage(txt, // img
30754 0, 0, // sx, sy
30755 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30756 0, 0, // x, y
30757 r.canvasWidth, r.canvasHeight // w, h
30758 );
30759 };
30760
30761 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30762 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30763 needDraw[r.NODE] = false;
30764 }
30765
30766 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30767 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30768 needDraw[r.DRAG] = false;
30769 }
30770 }
30771
30772 r.prevViewport = vp;
30773
30774 if (r.clearingMotionBlur) {
30775 r.clearingMotionBlur = false;
30776 r.motionBlurCleared = true;
30777 r.motionBlur = true;
30778 }
30779
30780 if (motionBlur) {
30781 r.motionBlurTimeout = setTimeout(function () {
30782 r.motionBlurTimeout = null;
30783 r.clearedForMotionBlur[r.NODE] = false;
30784 r.clearedForMotionBlur[r.DRAG] = false;
30785 r.motionBlur = false;
30786 r.clearingMotionBlur = !textureDraw;
30787 r.mbFrames = 0;
30788 needDraw[r.NODE] = true;
30789 needDraw[r.DRAG] = true;
30790 r.redraw();
30791 }, motionBlurDelay);
30792 }
30793
30794 if (!forcedContext) {
30795 cy.emit('render');
30796 }
30797};
30798
30799var CRp$7 = {}; // @O Polygon drawing
30800
30801CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30802 var halfW = width / 2;
30803 var halfH = height / 2;
30804
30805 if (context.beginPath) {
30806 context.beginPath();
30807 }
30808
30809 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30810
30811 for (var i = 1; i < points.length / 2; i++) {
30812 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30813 }
30814
30815 context.closePath();
30816};
30817
30818CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30819 var halfW = width / 2;
30820 var halfH = height / 2;
30821 var cornerRadius = getRoundPolygonRadius(width, height);
30822
30823 if (context.beginPath) {
30824 context.beginPath();
30825 }
30826
30827 for (var _i = 0; _i < points.length / 4; _i++) {
30828 var sourceUv = void 0,
30829 destUv = void 0;
30830
30831 if (_i === 0) {
30832 sourceUv = points.length - 2;
30833 } else {
30834 sourceUv = _i * 4 - 2;
30835 }
30836
30837 destUv = _i * 4 + 2;
30838 var px = x + halfW * points[_i * 4];
30839 var py = y + halfH * points[_i * 4 + 1];
30840 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30841 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30842 var cp0x = px - offset * points[sourceUv];
30843 var cp0y = py - offset * points[sourceUv + 1];
30844 var cp1x = px + offset * points[destUv];
30845 var cp1y = py + offset * points[destUv + 1];
30846
30847 if (_i === 0) {
30848 context.moveTo(cp0x, cp0y);
30849 } else {
30850 context.lineTo(cp0x, cp0y);
30851 }
30852
30853 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30854 }
30855
30856 context.closePath();
30857}; // Round rectangle drawing
30858
30859
30860CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30861 var halfWidth = width / 2;
30862 var halfHeight = height / 2;
30863 var cornerRadius = getRoundRectangleRadius(width, height);
30864
30865 if (context.beginPath) {
30866 context.beginPath();
30867 } // Start at top middle
30868
30869
30870 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30871
30872 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30873
30874 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30875
30876 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30877
30878 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30879
30880 context.lineTo(x, y - halfHeight);
30881 context.closePath();
30882};
30883
30884CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30885 var halfWidth = width / 2;
30886 var halfHeight = height / 2;
30887 var cornerRadius = getRoundRectangleRadius(width, height);
30888
30889 if (context.beginPath) {
30890 context.beginPath();
30891 } // Start at top middle
30892
30893
30894 context.moveTo(x, y - halfHeight);
30895 context.lineTo(x + halfWidth, y - halfHeight);
30896 context.lineTo(x + halfWidth, y);
30897 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30898 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30899 context.lineTo(x - halfWidth, y - halfHeight);
30900 context.lineTo(x, y - halfHeight);
30901 context.closePath();
30902};
30903
30904CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30905 var halfWidth = width / 2;
30906 var halfHeight = height / 2;
30907 var cornerLength = getCutRectangleCornerLength();
30908
30909 if (context.beginPath) {
30910 context.beginPath();
30911 }
30912
30913 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30914 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30915 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30916 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30917 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30918 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30919 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30920 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30921 context.closePath();
30922};
30923
30924CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30925 var halfWidth = width / 2;
30926 var halfHeight = height / 2;
30927 var xBegin = x - halfWidth;
30928 var xEnd = x + halfWidth;
30929 var yBegin = y - halfHeight;
30930 var yEnd = y + halfHeight;
30931 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30932 var wOffset = barrelCurveConstants.widthOffset;
30933 var hOffset = barrelCurveConstants.heightOffset;
30934 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30935
30936 if (context.beginPath) {
30937 context.beginPath();
30938 }
30939
30940 context.moveTo(xBegin, yBegin + hOffset);
30941 context.lineTo(xBegin, yEnd - hOffset);
30942 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30943 context.lineTo(xEnd - wOffset, yEnd);
30944 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30945 context.lineTo(xEnd, yBegin + hOffset);
30946 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30947 context.lineTo(xBegin + wOffset, yBegin);
30948 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30949 context.closePath();
30950};
30951
30952var sin0 = Math.sin(0);
30953var cos0 = Math.cos(0);
30954var sin = {};
30955var cos = {};
30956var ellipseStepSize = Math.PI / 40;
30957
30958for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30959 sin[i] = Math.sin(i);
30960 cos[i] = Math.cos(i);
30961}
30962
30963CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30964 if (context.beginPath) {
30965 context.beginPath();
30966 }
30967
30968 if (context.ellipse) {
30969 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30970 } else {
30971 var xPos, yPos;
30972 var rw = width / 2;
30973 var rh = height / 2;
30974
30975 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30976 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30977 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30978
30979 if (i === 0) {
30980 context.moveTo(xPos, yPos);
30981 } else {
30982 context.lineTo(xPos, yPos);
30983 }
30984 }
30985 }
30986
30987 context.closePath();
30988};
30989
30990/* global atob, ArrayBuffer, Uint8Array, Blob */
30991var CRp$8 = {};
30992
30993CRp$8.createBuffer = function (w, h) {
30994 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30995
30996 buffer.width = w;
30997 buffer.height = h;
30998 return [buffer, buffer.getContext('2d')];
30999};
31000
31001CRp$8.bufferCanvasImage = function (options) {
31002 var cy = this.cy;
31003 var eles = cy.mutableElements();
31004 var bb = eles.boundingBox();
31005 var ctrRect = this.findContainerClientCoords();
31006 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
31007 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
31008 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
31009 var pxRatio = this.getPixelRatio();
31010 var scale = 1;
31011
31012 if (options.scale !== undefined) {
31013 width *= options.scale;
31014 height *= options.scale;
31015 scale = options.scale;
31016 } else if (specdMaxDims) {
31017 var maxScaleW = Infinity;
31018 var maxScaleH = Infinity;
31019
31020 if (number(options.maxWidth)) {
31021 maxScaleW = scale * options.maxWidth / width;
31022 }
31023
31024 if (number(options.maxHeight)) {
31025 maxScaleH = scale * options.maxHeight / height;
31026 }
31027
31028 scale = Math.min(maxScaleW, maxScaleH);
31029 width *= scale;
31030 height *= scale;
31031 }
31032
31033 if (!specdMaxDims) {
31034 width *= pxRatio;
31035 height *= pxRatio;
31036 scale *= pxRatio;
31037 }
31038
31039 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
31040
31041 buffCanvas.width = width;
31042 buffCanvas.height = height;
31043 buffCanvas.style.width = width + 'px';
31044 buffCanvas.style.height = height + 'px';
31045 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
31046
31047 if (width > 0 && height > 0) {
31048 buffCxt.clearRect(0, 0, width, height);
31049 buffCxt.globalCompositeOperation = 'source-over';
31050 var zsortedEles = this.getCachedZSortedEles();
31051
31052 if (options.full) {
31053 // draw the full bounds of the graph
31054 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
31055 buffCxt.scale(scale, scale);
31056 this.drawElements(buffCxt, zsortedEles);
31057 buffCxt.scale(1 / scale, 1 / scale);
31058 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
31059 } else {
31060 // draw the current view
31061 var pan = cy.pan();
31062 var translation = {
31063 x: pan.x * scale,
31064 y: pan.y * scale
31065 };
31066 scale *= cy.zoom();
31067 buffCxt.translate(translation.x, translation.y);
31068 buffCxt.scale(scale, scale);
31069 this.drawElements(buffCxt, zsortedEles);
31070 buffCxt.scale(1 / scale, 1 / scale);
31071 buffCxt.translate(-translation.x, -translation.y);
31072 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
31073
31074
31075 if (options.bg) {
31076 buffCxt.globalCompositeOperation = 'destination-over';
31077 buffCxt.fillStyle = options.bg;
31078 buffCxt.rect(0, 0, width, height);
31079 buffCxt.fill();
31080 }
31081 }
31082
31083 return buffCanvas;
31084};
31085
31086function b64ToBlob(b64, mimeType) {
31087 var bytes = atob(b64);
31088 var buff = new ArrayBuffer(bytes.length);
31089 var buffUint8 = new Uint8Array(buff);
31090
31091 for (var i = 0; i < bytes.length; i++) {
31092 buffUint8[i] = bytes.charCodeAt(i);
31093 }
31094
31095 return new Blob([buff], {
31096 type: mimeType
31097 });
31098}
31099
31100function b64UriToB64(b64uri) {
31101 var i = b64uri.indexOf(',');
31102 return b64uri.substr(i + 1);
31103}
31104
31105function output(options, canvas, mimeType) {
31106 var getB64Uri = function getB64Uri() {
31107 return canvas.toDataURL(mimeType, options.quality);
31108 };
31109
31110 switch (options.output) {
31111 case 'blob-promise':
31112 return new Promise$1(function (resolve, reject) {
31113 try {
31114 canvas.toBlob(function (blob) {
31115 if (blob != null) {
31116 resolve(blob);
31117 } else {
31118 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
31119 }
31120 }, mimeType, options.quality);
31121 } catch (err) {
31122 reject(err);
31123 }
31124 });
31125
31126 case 'blob':
31127 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
31128
31129 case 'base64':
31130 return b64UriToB64(getB64Uri());
31131
31132 case 'base64uri':
31133 default:
31134 return getB64Uri();
31135 }
31136}
31137
31138CRp$8.png = function (options) {
31139 return output(options, this.bufferCanvasImage(options), 'image/png');
31140};
31141
31142CRp$8.jpg = function (options) {
31143 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
31144};
31145
31146var CRp$9 = {};
31147
31148CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
31149 switch (name) {
31150 case 'ellipse':
31151 return this.drawEllipsePath(context, centerX, centerY, width, height);
31152
31153 case 'polygon':
31154 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
31155
31156 case 'round-polygon':
31157 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
31158
31159 case 'roundrectangle':
31160 case 'round-rectangle':
31161 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
31162
31163 case 'cutrectangle':
31164 case 'cut-rectangle':
31165 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
31166
31167 case 'bottomroundrectangle':
31168 case 'bottom-round-rectangle':
31169 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
31170
31171 case 'barrel':
31172 return this.drawBarrelPath(context, centerX, centerY, width, height);
31173 }
31174};
31175
31176var CR = CanvasRenderer;
31177var CRp$a = CanvasRenderer.prototype;
31178CRp$a.CANVAS_LAYERS = 3; //
31179
31180CRp$a.SELECT_BOX = 0;
31181CRp$a.DRAG = 1;
31182CRp$a.NODE = 2;
31183CRp$a.BUFFER_COUNT = 3; //
31184
31185CRp$a.TEXTURE_BUFFER = 0;
31186CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
31187CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
31188
31189function CanvasRenderer(options) {
31190 var r = this;
31191 r.data = {
31192 canvases: new Array(CRp$a.CANVAS_LAYERS),
31193 contexts: new Array(CRp$a.CANVAS_LAYERS),
31194 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
31195 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
31196 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
31197 };
31198 var tapHlOffAttr = '-webkit-tap-highlight-color';
31199 var tapHlOffStyle = 'rgba(0,0,0,0)';
31200 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
31201
31202 var containerStyle = r.data.canvasContainer.style;
31203 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
31204 containerStyle.position = 'relative';
31205 containerStyle.zIndex = '0';
31206 containerStyle.overflow = 'hidden';
31207 var container = options.cy.container();
31208 container.appendChild(r.data.canvasContainer);
31209 container.style[tapHlOffAttr] = tapHlOffStyle;
31210 var styleMap = {
31211 '-webkit-user-select': 'none',
31212 '-moz-user-select': '-moz-none',
31213 'user-select': 'none',
31214 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
31215 'outline-style': 'none'
31216 };
31217
31218 if (ms()) {
31219 styleMap['-ms-touch-action'] = 'none';
31220 styleMap['touch-action'] = 'none';
31221 }
31222
31223 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
31224 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31225
31226 r.data.contexts[i] = canvas.getContext('2d');
31227 Object.keys(styleMap).forEach(function (k) {
31228 canvas.style[k] = styleMap[k];
31229 });
31230 canvas.style.position = 'absolute';
31231 canvas.setAttribute('data-id', 'layer' + i);
31232 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
31233 r.data.canvasContainer.appendChild(canvas);
31234 r.data.canvasNeedsRedraw[i] = false;
31235 }
31236
31237 r.data.topCanvas = r.data.canvases[0];
31238 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
31239 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
31240 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
31241
31242 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
31243 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31244
31245 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31246 r.data.bufferCanvases[i].style.position = 'absolute';
31247 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31248 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31249 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31250 }
31251
31252 r.pathsEnabled = true;
31253 var emptyBb = makeBoundingBox();
31254
31255 var getBoxCenter = function getBoxCenter(bb) {
31256 return {
31257 x: (bb.x1 + bb.x2) / 2,
31258 y: (bb.y1 + bb.y2) / 2
31259 };
31260 };
31261
31262 var getCenterOffset = function getCenterOffset(bb) {
31263 return {
31264 x: -bb.w / 2,
31265 y: -bb.h / 2
31266 };
31267 };
31268
31269 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31270 var _p = ele[0]._private;
31271 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31272 return !same;
31273 };
31274
31275 var getStyleKey = function getStyleKey(ele) {
31276 return ele[0]._private.nodeKey;
31277 };
31278
31279 var getLabelKey = function getLabelKey(ele) {
31280 return ele[0]._private.labelStyleKey;
31281 };
31282
31283 var getSourceLabelKey = function getSourceLabelKey(ele) {
31284 return ele[0]._private.sourceLabelStyleKey;
31285 };
31286
31287 var getTargetLabelKey = function getTargetLabelKey(ele) {
31288 return ele[0]._private.targetLabelStyleKey;
31289 };
31290
31291 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31292 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31293 };
31294
31295 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31296 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31297 };
31298
31299 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31300 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31301 };
31302
31303 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31304 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31305 };
31306
31307 var getElementBox = function getElementBox(ele) {
31308 ele.boundingBox();
31309 return ele[0]._private.bodyBounds;
31310 };
31311
31312 var getLabelBox = function getLabelBox(ele) {
31313 ele.boundingBox();
31314 return ele[0]._private.labelBounds.main || emptyBb;
31315 };
31316
31317 var getSourceLabelBox = function getSourceLabelBox(ele) {
31318 ele.boundingBox();
31319 return ele[0]._private.labelBounds.source || emptyBb;
31320 };
31321
31322 var getTargetLabelBox = function getTargetLabelBox(ele) {
31323 ele.boundingBox();
31324 return ele[0]._private.labelBounds.target || emptyBb;
31325 };
31326
31327 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31328 return scaledLabelShown;
31329 };
31330
31331 var getElementRotationPoint = function getElementRotationPoint(ele) {
31332 return getBoxCenter(getElementBox(ele));
31333 };
31334
31335 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31336 var pre = prefix ? prefix + '-' : '';
31337 return {
31338 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31339 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31340 };
31341 };
31342
31343 var getRsPt = function getRsPt(ele, x, y) {
31344 var rs = ele[0]._private.rscratch;
31345 return {
31346 x: rs[x],
31347 y: rs[y]
31348 };
31349 };
31350
31351 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31352 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31353 };
31354
31355 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31356 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31357 };
31358
31359 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31360 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31361 };
31362
31363 var getElementRotationOffset = function getElementRotationOffset(ele) {
31364 return getCenterOffset(getElementBox(ele));
31365 };
31366
31367 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31368 return getCenterOffset(getSourceLabelBox(ele));
31369 };
31370
31371 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31372 return getCenterOffset(getTargetLabelBox(ele));
31373 };
31374
31375 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31376 var bb = getLabelBox(ele);
31377 var p = getCenterOffset(getLabelBox(ele));
31378
31379 if (ele.isNode()) {
31380 switch (ele.pstyle('text-halign').value) {
31381 case 'left':
31382 p.x = -bb.w;
31383 break;
31384
31385 case 'right':
31386 p.x = 0;
31387 break;
31388 }
31389
31390 switch (ele.pstyle('text-valign').value) {
31391 case 'top':
31392 p.y = -bb.h;
31393 break;
31394
31395 case 'bottom':
31396 p.y = 0;
31397 break;
31398 }
31399 }
31400
31401 return p;
31402 };
31403
31404 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31405 getKey: getStyleKey,
31406 doesEleInvalidateKey: backgroundTimestampHasChanged,
31407 drawElement: drawElement,
31408 getBoundingBox: getElementBox,
31409 getRotationPoint: getElementRotationPoint,
31410 getRotationOffset: getElementRotationOffset,
31411 allowEdgeTxrCaching: false,
31412 allowParentTxrCaching: false
31413 });
31414 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31415 getKey: getLabelKey,
31416 drawElement: drawLabel,
31417 getBoundingBox: getLabelBox,
31418 getRotationPoint: getLabelRotationPoint,
31419 getRotationOffset: getLabelRotationOffset,
31420 isVisible: isLabelVisibleAtScale
31421 });
31422 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31423 getKey: getSourceLabelKey,
31424 drawElement: drawSourceLabel,
31425 getBoundingBox: getSourceLabelBox,
31426 getRotationPoint: getSourceLabelRotationPoint,
31427 getRotationOffset: getSourceLabelRotationOffset,
31428 isVisible: isLabelVisibleAtScale
31429 });
31430 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31431 getKey: getTargetLabelKey,
31432 drawElement: drawTargetLabel,
31433 getBoundingBox: getTargetLabelBox,
31434 getRotationPoint: getTargetLabelRotationPoint,
31435 getRotationOffset: getTargetLabelRotationOffset,
31436 isVisible: isLabelVisibleAtScale
31437 });
31438 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31439 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31440 // each cache should check for sub-key diff to see that the update affects that cache particularly
31441 eleTxrCache.invalidateElements(eles);
31442 lblTxrCache.invalidateElements(eles);
31443 slbTxrCache.invalidateElements(eles);
31444 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31445
31446 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31447
31448 for (var _i = 0; _i < eles.length; _i++) {
31449 var _p = eles[_i]._private;
31450 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31451 }
31452 });
31453
31454 var refineInLayers = function refineInLayers(reqs) {
31455 for (var i = 0; i < reqs.length; i++) {
31456 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31457 }
31458 };
31459
31460 eleTxrCache.onDequeue(refineInLayers);
31461 lblTxrCache.onDequeue(refineInLayers);
31462 slbTxrCache.onDequeue(refineInLayers);
31463 tlbTxrCache.onDequeue(refineInLayers);
31464}
31465
31466CRp$a.redrawHint = function (group, bool) {
31467 var r = this;
31468
31469 switch (group) {
31470 case 'eles':
31471 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31472 break;
31473
31474 case 'drag':
31475 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31476 break;
31477
31478 case 'select':
31479 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31480 break;
31481 }
31482}; // whether to use Path2D caching for drawing
31483
31484
31485var pathsImpld = typeof Path2D !== 'undefined';
31486
31487CRp$a.path2dEnabled = function (on) {
31488 if (on === undefined) {
31489 return this.pathsEnabled;
31490 }
31491
31492 this.pathsEnabled = on ? true : false;
31493};
31494
31495CRp$a.usePaths = function () {
31496 return pathsImpld && this.pathsEnabled;
31497};
31498
31499CRp$a.setImgSmoothing = function (context, bool) {
31500 if (context.imageSmoothingEnabled != null) {
31501 context.imageSmoothingEnabled = bool;
31502 } else {
31503 context.webkitImageSmoothingEnabled = bool;
31504 context.mozImageSmoothingEnabled = bool;
31505 context.msImageSmoothingEnabled = bool;
31506 }
31507};
31508
31509CRp$a.getImgSmoothing = function (context) {
31510 if (context.imageSmoothingEnabled != null) {
31511 return context.imageSmoothingEnabled;
31512 } else {
31513 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31514 }
31515};
31516
31517CRp$a.makeOffscreenCanvas = function (width, height) {
31518 var canvas;
31519
31520 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31521 canvas = new OffscreenCanvas(width, height);
31522 } else {
31523 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31524
31525 canvas.width = width;
31526 canvas.height = height;
31527 }
31528
31529 return canvas;
31530};
31531
31532[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31533 extend(CRp$a, props);
31534});
31535
31536var renderer = [{
31537 name: 'null',
31538 impl: NullRenderer
31539}, {
31540 name: 'base',
31541 impl: BR
31542}, {
31543 name: 'canvas',
31544 impl: CR
31545}];
31546
31547var incExts = [{
31548 type: 'layout',
31549 extensions: layout
31550}, {
31551 type: 'renderer',
31552 extensions: renderer
31553}];
31554
31555var extensions = {}; // registered modules for extensions, indexed by name
31556
31557var modules = {};
31558
31559function setExtension(type, name, registrant) {
31560 var ext = registrant;
31561
31562 var overrideErr = function overrideErr(field) {
31563 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31564 };
31565
31566 if (type === 'core') {
31567 if (Core.prototype[name]) {
31568 return overrideErr(name);
31569 } else {
31570 Core.prototype[name] = registrant;
31571 }
31572 } else if (type === 'collection') {
31573 if (Collection.prototype[name]) {
31574 return overrideErr(name);
31575 } else {
31576 Collection.prototype[name] = registrant;
31577 }
31578 } else if (type === 'layout') {
31579 // fill in missing layout functions in the prototype
31580 var Layout = function Layout(options) {
31581 this.options = options;
31582 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31583
31584 if (!plainObject(this._private)) {
31585 this._private = {};
31586 }
31587
31588 this._private.cy = options.cy;
31589 this._private.listeners = [];
31590 this.createEmitter();
31591 };
31592
31593 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31594 var optLayoutFns = [];
31595
31596 for (var i = 0; i < optLayoutFns.length; i++) {
31597 var fnName = optLayoutFns[i];
31598
31599 layoutProto[fnName] = layoutProto[fnName] || function () {
31600 return this;
31601 };
31602 } // either .start() or .run() is defined, so autogen the other
31603
31604
31605 if (layoutProto.start && !layoutProto.run) {
31606 layoutProto.run = function () {
31607 this.start();
31608 return this;
31609 };
31610 } else if (!layoutProto.start && layoutProto.run) {
31611 layoutProto.start = function () {
31612 this.run();
31613 return this;
31614 };
31615 }
31616
31617 var regStop = registrant.prototype.stop;
31618
31619 layoutProto.stop = function () {
31620 var opts = this.options;
31621
31622 if (opts && opts.animate) {
31623 var anis = this.animations;
31624
31625 if (anis) {
31626 for (var _i = 0; _i < anis.length; _i++) {
31627 anis[_i].stop();
31628 }
31629 }
31630 }
31631
31632 if (regStop) {
31633 regStop.call(this);
31634 } else {
31635 this.emit('layoutstop');
31636 }
31637
31638 return this;
31639 };
31640
31641 if (!layoutProto.destroy) {
31642 layoutProto.destroy = function () {
31643 return this;
31644 };
31645 }
31646
31647 layoutProto.cy = function () {
31648 return this._private.cy;
31649 };
31650
31651 var getCy = function getCy(layout) {
31652 return layout._private.cy;
31653 };
31654
31655 var emitterOpts = {
31656 addEventFields: function addEventFields(layout, evt) {
31657 evt.layout = layout;
31658 evt.cy = getCy(layout);
31659 evt.target = layout;
31660 },
31661 bubble: function bubble() {
31662 return true;
31663 },
31664 parent: function parent(layout) {
31665 return getCy(layout);
31666 }
31667 };
31668 extend(layoutProto, {
31669 createEmitter: function createEmitter() {
31670 this._private.emitter = new Emitter(emitterOpts, this);
31671 return this;
31672 },
31673 emitter: function emitter() {
31674 return this._private.emitter;
31675 },
31676 on: function on(evt, cb) {
31677 this.emitter().on(evt, cb);
31678 return this;
31679 },
31680 one: function one(evt, cb) {
31681 this.emitter().one(evt, cb);
31682 return this;
31683 },
31684 once: function once(evt, cb) {
31685 this.emitter().one(evt, cb);
31686 return this;
31687 },
31688 removeListener: function removeListener(evt, cb) {
31689 this.emitter().removeListener(evt, cb);
31690 return this;
31691 },
31692 removeAllListeners: function removeAllListeners() {
31693 this.emitter().removeAllListeners();
31694 return this;
31695 },
31696 emit: function emit(evt, params) {
31697 this.emitter().emit(evt, params);
31698 return this;
31699 }
31700 });
31701 define$3.eventAliasesOn(layoutProto);
31702 ext = Layout; // replace with our wrapped layout
31703 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31704 // user registered renderers inherit from base
31705 var BaseRenderer = getExtension('renderer', 'base');
31706 var bProto = BaseRenderer.prototype;
31707 var RegistrantRenderer = registrant;
31708 var rProto = registrant.prototype;
31709
31710 var Renderer = function Renderer() {
31711 BaseRenderer.apply(this, arguments);
31712 RegistrantRenderer.apply(this, arguments);
31713 };
31714
31715 var proto = Renderer.prototype;
31716
31717 for (var pName in bProto) {
31718 var pVal = bProto[pName];
31719 var existsInR = rProto[pName] != null;
31720
31721 if (existsInR) {
31722 return overrideErr(pName);
31723 }
31724
31725 proto[pName] = pVal; // take impl from base
31726 }
31727
31728 for (var _pName in rProto) {
31729 proto[_pName] = rProto[_pName]; // take impl from registrant
31730 }
31731
31732 bProto.clientFunctions.forEach(function (name) {
31733 proto[name] = proto[name] || function () {
31734 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31735 };
31736 });
31737 ext = Renderer;
31738 } else if (type === '__proto__' || type === 'constructor' || type === 'prototype') {
31739 // to avoid potential prototype pollution
31740 return error(type + ' is an illegal type to be registered, possibly lead to prototype pollutions');
31741 }
31742
31743 return setMap({
31744 map: extensions,
31745 keys: [type, name],
31746 value: ext
31747 });
31748}
31749
31750function getExtension(type, name) {
31751 return getMap({
31752 map: extensions,
31753 keys: [type, name]
31754 });
31755}
31756
31757function setModule(type, name, moduleType, moduleName, registrant) {
31758 return setMap({
31759 map: modules,
31760 keys: [type, name, moduleType, moduleName],
31761 value: registrant
31762 });
31763}
31764
31765function getModule(type, name, moduleType, moduleName) {
31766 return getMap({
31767 map: modules,
31768 keys: [type, name, moduleType, moduleName]
31769 });
31770}
31771
31772var extension = function extension() {
31773 // e.g. extension('renderer', 'svg')
31774 if (arguments.length === 2) {
31775 return getExtension.apply(null, arguments);
31776 } // e.g. extension('renderer', 'svg', { ... })
31777 else if (arguments.length === 3) {
31778 return setExtension.apply(null, arguments);
31779 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31780 else if (arguments.length === 4) {
31781 return getModule.apply(null, arguments);
31782 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31783 else if (arguments.length === 5) {
31784 return setModule.apply(null, arguments);
31785 } else {
31786 error('Invalid extension access syntax');
31787 }
31788}; // allows a core instance to access extensions internally
31789
31790
31791Core.prototype.extension = extension; // included extensions
31792
31793incExts.forEach(function (group) {
31794 group.extensions.forEach(function (ext) {
31795 setExtension(group.type, ext.name, ext.impl);
31796 });
31797});
31798
31799// (useful for init)
31800
31801var Stylesheet = function Stylesheet() {
31802 if (!(this instanceof Stylesheet)) {
31803 return new Stylesheet();
31804 }
31805
31806 this.length = 0;
31807};
31808
31809var sheetfn = Stylesheet.prototype;
31810
31811sheetfn.instanceString = function () {
31812 return 'stylesheet';
31813}; // just store the selector to be parsed later
31814
31815
31816sheetfn.selector = function (selector) {
31817 var i = this.length++;
31818 this[i] = {
31819 selector: selector,
31820 properties: []
31821 };
31822 return this; // chaining
31823}; // just store the property to be parsed later
31824
31825
31826sheetfn.css = function (name, value) {
31827 var i = this.length - 1;
31828
31829 if (string(name)) {
31830 this[i].properties.push({
31831 name: name,
31832 value: value
31833 });
31834 } else if (plainObject(name)) {
31835 var map = name;
31836 var propNames = Object.keys(map);
31837
31838 for (var j = 0; j < propNames.length; j++) {
31839 var key = propNames[j];
31840 var mapVal = map[key];
31841
31842 if (mapVal == null) {
31843 continue;
31844 }
31845
31846 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31847
31848 if (prop == null) {
31849 continue;
31850 }
31851
31852 var _name = prop.name;
31853 var _value = mapVal;
31854 this[i].properties.push({
31855 name: _name,
31856 value: _value
31857 });
31858 }
31859 }
31860
31861 return this; // chaining
31862};
31863
31864sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31865
31866sheetfn.generateStyle = function (cy) {
31867 var style = new Style(cy);
31868 return this.appendToStyle(style);
31869}; // append a dummy stylesheet object on a real style object
31870
31871
31872sheetfn.appendToStyle = function (style) {
31873 for (var i = 0; i < this.length; i++) {
31874 var context = this[i];
31875 var selector = context.selector;
31876 var props = context.properties;
31877 style.selector(selector); // apply selector
31878
31879 for (var j = 0; j < props.length; j++) {
31880 var prop = props[j];
31881 style.css(prop.name, prop.value); // apply property
31882 }
31883 }
31884
31885 return style;
31886};
31887
31888var version = "3.21.2";
31889
31890var cytoscape = function cytoscape(options) {
31891 // if no options specified, use default
31892 if (options === undefined) {
31893 options = {};
31894 } // create instance
31895
31896
31897 if (plainObject(options)) {
31898 return new Core(options);
31899 } // allow for registration of extensions
31900 else if (string(options)) {
31901 return extension.apply(extension, arguments);
31902 }
31903}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31904
31905
31906cytoscape.use = function (ext) {
31907 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31908
31909 args.unshift(cytoscape); // cytoscape is first arg to ext
31910
31911 ext.apply(null, args);
31912 return this;
31913};
31914
31915cytoscape.warnings = function (bool) {
31916 return warnings(bool);
31917}; // replaced by build system
31918
31919
31920cytoscape.version = version; // expose public apis (mostly for extensions)
31921
31922cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31923
31924export default cytoscape;