UNPKG

847 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2019, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23'use strict';
24
25function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
26
27var util = _interopDefault(require('lodash.debounce'));
28var Heap = _interopDefault(require('heap'));
29
30function _typeof(obj) {
31 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
32 _typeof = function (obj) {
33 return typeof obj;
34 };
35 } else {
36 _typeof = function (obj) {
37 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
38 };
39 }
40
41 return _typeof(obj);
42}
43
44function _classCallCheck(instance, Constructor) {
45 if (!(instance instanceof Constructor)) {
46 throw new TypeError("Cannot call a class as a function");
47 }
48}
49
50function _defineProperties(target, props) {
51 for (var i = 0; i < props.length; i++) {
52 var descriptor = props[i];
53 descriptor.enumerable = descriptor.enumerable || false;
54 descriptor.configurable = true;
55 if ("value" in descriptor) descriptor.writable = true;
56 Object.defineProperty(target, descriptor.key, descriptor);
57 }
58}
59
60function _createClass(Constructor, protoProps, staticProps) {
61 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
62 if (staticProps) _defineProperties(Constructor, staticProps);
63 return Constructor;
64}
65
66function _defineProperty(obj, key, value) {
67 if (key in obj) {
68 Object.defineProperty(obj, key, {
69 value: value,
70 enumerable: true,
71 configurable: true,
72 writable: true
73 });
74 } else {
75 obj[key] = value;
76 }
77
78 return obj;
79}
80
81function _slicedToArray(arr, i) {
82 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
83}
84
85function _arrayWithHoles(arr) {
86 if (Array.isArray(arr)) return arr;
87}
88
89function _iterableToArrayLimit(arr, i) {
90 var _arr = [];
91 var _n = true;
92 var _d = false;
93 var _e = undefined;
94
95 try {
96 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
97 _arr.push(_s.value);
98
99 if (i && _arr.length === i) break;
100 }
101 } catch (err) {
102 _d = true;
103 _e = err;
104 } finally {
105 try {
106 if (!_n && _i["return"] != null) _i["return"]();
107 } finally {
108 if (_d) throw _e;
109 }
110 }
111
112 return _arr;
113}
114
115function _nonIterableRest() {
116 throw new TypeError("Invalid attempt to destructure non-iterable instance");
117}
118
119var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
120
121var navigator = window$1 ? window$1.navigator : null;
122var document$1 = window$1 ? window$1.document : null;
123
124var typeofstr = _typeof('');
125
126var typeofobj = _typeof({});
127
128var typeoffn = _typeof(function () {});
129
130var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
131
132var instanceStr = function instanceStr(obj) {
133 return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
134};
135
136var string = function string(obj) {
137 return obj != null && _typeof(obj) == typeofstr;
138};
139var fn = function fn(obj) {
140 return obj != null && _typeof(obj) === typeoffn;
141};
142var array = function array(obj) {
143 return Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array;
144};
145var plainObject = function plainObject(obj) {
146 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
147};
148var object = function object(obj) {
149 return obj != null && _typeof(obj) === typeofobj;
150};
151var number = function number(obj) {
152 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
153};
154var integer = function integer(obj) {
155 return number(obj) && Math.floor(obj) === obj;
156};
157var htmlElement = function htmlElement(obj) {
158 if ('undefined' === typeofhtmlele) {
159 return undefined;
160 } else {
161 return null != obj && obj instanceof HTMLElement;
162 }
163};
164var elementOrCollection = function elementOrCollection(obj) {
165 return element(obj) || collection(obj);
166};
167var element = function element(obj) {
168 return instanceStr(obj) === 'collection' && obj._private.single;
169};
170var collection = function collection(obj) {
171 return instanceStr(obj) === 'collection' && !obj._private.single;
172};
173var core = function core(obj) {
174 return instanceStr(obj) === 'core';
175};
176var stylesheet = function stylesheet(obj) {
177 return instanceStr(obj) === 'stylesheet';
178};
179var event = function event(obj) {
180 return instanceStr(obj) === 'event';
181};
182var emptyString = function emptyString(obj) {
183 if (obj === undefined || obj === null) {
184 // null is empty
185 return true;
186 } else if (obj === '' || obj.match(/^\s+$/)) {
187 return true; // empty string is empty
188 }
189
190 return false; // otherwise, we don't know what we've got
191};
192var domElement = function domElement(obj) {
193 if (typeof HTMLElement === 'undefined') {
194 return false; // we're not in a browser so it doesn't matter
195 } else {
196 return obj instanceof HTMLElement;
197 }
198};
199var boundingBox = function boundingBox(obj) {
200 return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
201};
202var promise = function promise(obj) {
203 return object(obj) && fn(obj.then);
204};
205var ms = function ms() {
206 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
207}; // probably a better way to detect this...
208
209var memoize = function memoize(fn, keyFn) {
210 if (!keyFn) {
211 keyFn = function keyFn() {
212 if (arguments.length === 1) {
213 return arguments[0];
214 } else if (arguments.length === 0) {
215 return 'undefined';
216 }
217
218 var args = [];
219
220 for (var i = 0; i < arguments.length; i++) {
221 args.push(arguments[i]);
222 }
223
224 return args.join('$');
225 };
226 }
227
228 var memoizedFn = function memoizedFn() {
229 var self = this;
230 var args = arguments;
231 var ret;
232 var k = keyFn.apply(self, args);
233 var cache = memoizedFn.cache;
234
235 if (!(ret = cache[k])) {
236 ret = cache[k] = fn.apply(self, args);
237 }
238
239 return ret;
240 };
241
242 memoizedFn.cache = {};
243 return memoizedFn;
244};
245
246var camel2dash = memoize(function (str) {
247 return str.replace(/([A-Z])/g, function (v) {
248 return '-' + v.toLowerCase();
249 });
250});
251var dash2camel = memoize(function (str) {
252 return str.replace(/(-\w)/g, function (v) {
253 return v[1].toUpperCase();
254 });
255});
256var prependCamel = memoize(function (prefix, str) {
257 return prefix + str[0].toUpperCase() + str.substring(1);
258}, function (prefix, str) {
259 return prefix + '$' + str;
260});
261var capitalize = function capitalize(str) {
262 if (emptyString(str)) {
263 return str;
264 }
265
266 return str.charAt(0).toUpperCase() + str.substring(1);
267};
268
269var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
270var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
271var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
272var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
273var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
274var hex3 = '\\#[0-9a-fA-F]{3}';
275var hex6 = '\\#[0-9a-fA-F]{6}';
276
277var ascending = function ascending(a, b) {
278 if (a < b) {
279 return -1;
280 } else if (a > b) {
281 return 1;
282 } else {
283 return 0;
284 }
285};
286var descending = function descending(a, b) {
287 return -1 * ascending(a, b);
288};
289
290var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
291 var args = arguments;
292
293 for (var i = 1; i < args.length; i++) {
294 var obj = args[i];
295
296 if (obj == null) {
297 continue;
298 }
299
300 var keys = Object.keys(obj);
301
302 for (var j = 0; j < keys.length; j++) {
303 var k = keys[j];
304 tgt[k] = obj[k];
305 }
306 }
307
308 return tgt;
309};
310
311var hex2tuple = function hex2tuple(hex) {
312 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
313 return;
314 }
315
316 var shortHex = hex.length === 4;
317 var r, g, b;
318 var base = 16;
319
320 if (shortHex) {
321 r = parseInt(hex[1] + hex[1], base);
322 g = parseInt(hex[2] + hex[2], base);
323 b = parseInt(hex[3] + hex[3], base);
324 } else {
325 r = parseInt(hex[1] + hex[2], base);
326 g = parseInt(hex[3] + hex[4], base);
327 b = parseInt(hex[5] + hex[6], base);
328 }
329
330 return [r, g, b];
331}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
332
333var hsl2tuple = function hsl2tuple(hsl) {
334 var ret;
335 var h, s, l, a, r, g, b;
336
337 function hue2rgb(p, q, t) {
338 if (t < 0) t += 1;
339 if (t > 1) t -= 1;
340 if (t < 1 / 6) return p + (q - p) * 6 * t;
341 if (t < 1 / 2) return q;
342 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
343 return p;
344 }
345
346 var m = new RegExp('^' + hsla + '$').exec(hsl);
347
348 if (m) {
349 // get hue
350 h = parseInt(m[1]);
351
352 if (h < 0) {
353 h = (360 - -1 * h % 360) % 360;
354 } else if (h > 360) {
355 h = h % 360;
356 }
357
358 h /= 360; // normalise on [0, 1]
359
360 s = parseFloat(m[2]);
361
362 if (s < 0 || s > 100) {
363 return;
364 } // saturation is [0, 100]
365
366
367 s = s / 100; // normalise on [0, 1]
368
369 l = parseFloat(m[3]);
370
371 if (l < 0 || l > 100) {
372 return;
373 } // lightness is [0, 100]
374
375
376 l = l / 100; // normalise on [0, 1]
377
378 a = m[4];
379
380 if (a !== undefined) {
381 a = parseFloat(a);
382
383 if (a < 0 || a > 1) {
384 return;
385 } // alpha is [0, 1]
386
387 } // now, convert to rgb
388 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
389
390
391 if (s === 0) {
392 r = g = b = Math.round(l * 255); // achromatic
393 } else {
394 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
395 var p = 2 * l - q;
396 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
397 g = Math.round(255 * hue2rgb(p, q, h));
398 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
399 }
400
401 ret = [r, g, b, a];
402 }
403
404 return ret;
405}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
406
407var rgb2tuple = function rgb2tuple(rgb) {
408 var ret;
409 var m = new RegExp('^' + rgba + '$').exec(rgb);
410
411 if (m) {
412 ret = [];
413 var isPct = [];
414
415 for (var i = 1; i <= 3; i++) {
416 var channel = m[i];
417
418 if (channel[channel.length - 1] === '%') {
419 isPct[i] = true;
420 }
421
422 channel = parseFloat(channel);
423
424 if (isPct[i]) {
425 channel = channel / 100 * 255; // normalise to [0, 255]
426 }
427
428 if (channel < 0 || channel > 255) {
429 return;
430 } // invalid channel value
431
432
433 ret.push(Math.floor(channel));
434 }
435
436 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
437 var allArePct = isPct[1] && isPct[2] && isPct[3];
438
439 if (atLeastOneIsPct && !allArePct) {
440 return;
441 } // must all be percent values if one is
442
443
444 var alpha = m[4];
445
446 if (alpha !== undefined) {
447 alpha = parseFloat(alpha);
448
449 if (alpha < 0 || alpha > 1) {
450 return;
451 } // invalid alpha value
452
453
454 ret.push(alpha);
455 }
456 }
457
458 return ret;
459};
460var colorname2tuple = function colorname2tuple(color) {
461 return colors[color.toLowerCase()];
462};
463var color2tuple = function color2tuple(color) {
464 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
465};
466var colors = {
467 // special colour names
468 transparent: [0, 0, 0, 0],
469 // NB alpha === 0
470 // regular colours
471 aliceblue: [240, 248, 255],
472 antiquewhite: [250, 235, 215],
473 aqua: [0, 255, 255],
474 aquamarine: [127, 255, 212],
475 azure: [240, 255, 255],
476 beige: [245, 245, 220],
477 bisque: [255, 228, 196],
478 black: [0, 0, 0],
479 blanchedalmond: [255, 235, 205],
480 blue: [0, 0, 255],
481 blueviolet: [138, 43, 226],
482 brown: [165, 42, 42],
483 burlywood: [222, 184, 135],
484 cadetblue: [95, 158, 160],
485 chartreuse: [127, 255, 0],
486 chocolate: [210, 105, 30],
487 coral: [255, 127, 80],
488 cornflowerblue: [100, 149, 237],
489 cornsilk: [255, 248, 220],
490 crimson: [220, 20, 60],
491 cyan: [0, 255, 255],
492 darkblue: [0, 0, 139],
493 darkcyan: [0, 139, 139],
494 darkgoldenrod: [184, 134, 11],
495 darkgray: [169, 169, 169],
496 darkgreen: [0, 100, 0],
497 darkgrey: [169, 169, 169],
498 darkkhaki: [189, 183, 107],
499 darkmagenta: [139, 0, 139],
500 darkolivegreen: [85, 107, 47],
501 darkorange: [255, 140, 0],
502 darkorchid: [153, 50, 204],
503 darkred: [139, 0, 0],
504 darksalmon: [233, 150, 122],
505 darkseagreen: [143, 188, 143],
506 darkslateblue: [72, 61, 139],
507 darkslategray: [47, 79, 79],
508 darkslategrey: [47, 79, 79],
509 darkturquoise: [0, 206, 209],
510 darkviolet: [148, 0, 211],
511 deeppink: [255, 20, 147],
512 deepskyblue: [0, 191, 255],
513 dimgray: [105, 105, 105],
514 dimgrey: [105, 105, 105],
515 dodgerblue: [30, 144, 255],
516 firebrick: [178, 34, 34],
517 floralwhite: [255, 250, 240],
518 forestgreen: [34, 139, 34],
519 fuchsia: [255, 0, 255],
520 gainsboro: [220, 220, 220],
521 ghostwhite: [248, 248, 255],
522 gold: [255, 215, 0],
523 goldenrod: [218, 165, 32],
524 gray: [128, 128, 128],
525 grey: [128, 128, 128],
526 green: [0, 128, 0],
527 greenyellow: [173, 255, 47],
528 honeydew: [240, 255, 240],
529 hotpink: [255, 105, 180],
530 indianred: [205, 92, 92],
531 indigo: [75, 0, 130],
532 ivory: [255, 255, 240],
533 khaki: [240, 230, 140],
534 lavender: [230, 230, 250],
535 lavenderblush: [255, 240, 245],
536 lawngreen: [124, 252, 0],
537 lemonchiffon: [255, 250, 205],
538 lightblue: [173, 216, 230],
539 lightcoral: [240, 128, 128],
540 lightcyan: [224, 255, 255],
541 lightgoldenrodyellow: [250, 250, 210],
542 lightgray: [211, 211, 211],
543 lightgreen: [144, 238, 144],
544 lightgrey: [211, 211, 211],
545 lightpink: [255, 182, 193],
546 lightsalmon: [255, 160, 122],
547 lightseagreen: [32, 178, 170],
548 lightskyblue: [135, 206, 250],
549 lightslategray: [119, 136, 153],
550 lightslategrey: [119, 136, 153],
551 lightsteelblue: [176, 196, 222],
552 lightyellow: [255, 255, 224],
553 lime: [0, 255, 0],
554 limegreen: [50, 205, 50],
555 linen: [250, 240, 230],
556 magenta: [255, 0, 255],
557 maroon: [128, 0, 0],
558 mediumaquamarine: [102, 205, 170],
559 mediumblue: [0, 0, 205],
560 mediumorchid: [186, 85, 211],
561 mediumpurple: [147, 112, 219],
562 mediumseagreen: [60, 179, 113],
563 mediumslateblue: [123, 104, 238],
564 mediumspringgreen: [0, 250, 154],
565 mediumturquoise: [72, 209, 204],
566 mediumvioletred: [199, 21, 133],
567 midnightblue: [25, 25, 112],
568 mintcream: [245, 255, 250],
569 mistyrose: [255, 228, 225],
570 moccasin: [255, 228, 181],
571 navajowhite: [255, 222, 173],
572 navy: [0, 0, 128],
573 oldlace: [253, 245, 230],
574 olive: [128, 128, 0],
575 olivedrab: [107, 142, 35],
576 orange: [255, 165, 0],
577 orangered: [255, 69, 0],
578 orchid: [218, 112, 214],
579 palegoldenrod: [238, 232, 170],
580 palegreen: [152, 251, 152],
581 paleturquoise: [175, 238, 238],
582 palevioletred: [219, 112, 147],
583 papayawhip: [255, 239, 213],
584 peachpuff: [255, 218, 185],
585 peru: [205, 133, 63],
586 pink: [255, 192, 203],
587 plum: [221, 160, 221],
588 powderblue: [176, 224, 230],
589 purple: [128, 0, 128],
590 red: [255, 0, 0],
591 rosybrown: [188, 143, 143],
592 royalblue: [65, 105, 225],
593 saddlebrown: [139, 69, 19],
594 salmon: [250, 128, 114],
595 sandybrown: [244, 164, 96],
596 seagreen: [46, 139, 87],
597 seashell: [255, 245, 238],
598 sienna: [160, 82, 45],
599 silver: [192, 192, 192],
600 skyblue: [135, 206, 235],
601 slateblue: [106, 90, 205],
602 slategray: [112, 128, 144],
603 slategrey: [112, 128, 144],
604 snow: [255, 250, 250],
605 springgreen: [0, 255, 127],
606 steelblue: [70, 130, 180],
607 tan: [210, 180, 140],
608 teal: [0, 128, 128],
609 thistle: [216, 191, 216],
610 tomato: [255, 99, 71],
611 turquoise: [64, 224, 208],
612 violet: [238, 130, 238],
613 wheat: [245, 222, 179],
614 white: [255, 255, 255],
615 whitesmoke: [245, 245, 245],
616 yellow: [255, 255, 0],
617 yellowgreen: [154, 205, 50]
618};
619
620var setMap = function setMap(options) {
621 var obj = options.map;
622 var keys = options.keys;
623 var l = keys.length;
624
625 for (var i = 0; i < l; i++) {
626 var key = keys[i];
627
628 if (plainObject(key)) {
629 throw Error('Tried to set map with object key');
630 }
631
632 if (i < keys.length - 1) {
633 // extend the map if necessary
634 if (obj[key] == null) {
635 obj[key] = {};
636 }
637
638 obj = obj[key];
639 } else {
640 // set the value
641 obj[key] = options.value;
642 }
643 }
644}; // gets the value in a map even if it's not built in places
645
646var getMap = function getMap(options) {
647 var obj = options.map;
648 var keys = options.keys;
649 var l = keys.length;
650
651 for (var i = 0; i < l; i++) {
652 var key = keys[i];
653
654 if (plainObject(key)) {
655 throw Error('Tried to get map with object key');
656 }
657
658 obj = obj[key];
659
660 if (obj == null) {
661 return obj;
662 }
663 }
664
665 return obj;
666}; // deletes the entry in the map
667
668var performance = window$1 ? window$1.performance : null;
669var pnow = performance && performance.now ? function () {
670 return performance.now();
671} : function () {
672 return Date.now();
673};
674
675var raf = function () {
676 if (window$1) {
677 if (window$1.requestAnimationFrame) {
678 return function (fn) {
679 window$1.requestAnimationFrame(fn);
680 };
681 } else if (window$1.mozRequestAnimationFrame) {
682 return function (fn) {
683 window$1.mozRequestAnimationFrame(fn);
684 };
685 } else if (window$1.webkitRequestAnimationFrame) {
686 return function (fn) {
687 window$1.webkitRequestAnimationFrame(fn);
688 };
689 } else if (window$1.msRequestAnimationFrame) {
690 return function (fn) {
691 window$1.msRequestAnimationFrame(fn);
692 };
693 }
694 }
695
696 return function (fn) {
697 if (fn) {
698 setTimeout(function () {
699 fn(pnow());
700 }, 1000 / 60);
701 }
702 };
703}();
704
705var requestAnimationFrame = function requestAnimationFrame(fn) {
706 return raf(fn);
707};
708var performanceNow = pnow;
709
710var DEFAULT_SEED = 5381;
711var hashIterableInts = function hashIterableInts(iterator) {
712 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
713 // djb2/string-hash
714 var hash = seed;
715 var entry;
716
717 for (;;) {
718 entry = iterator.next();
719
720 if (entry.done) {
721 break;
722 }
723
724 hash = (hash << 5) + hash + entry.value | 0;
725 }
726
727 return hash;
728};
729var hashInt = function hashInt(num) {
730 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
731 // djb2/string-hash
732 return (seed << 5) + seed + num | 0;
733};
734var hashIntsArray = function hashIntsArray(ints, seed) {
735 var entry = {
736 value: 0,
737 done: false
738 };
739 var i = 0;
740 var length = ints.length;
741 var iterator = {
742 next: function next() {
743 if (i < length) {
744 entry.value = ints[i++];
745 } else {
746 entry.done = true;
747 }
748
749 return entry;
750 }
751 };
752 return hashIterableInts(iterator, seed);
753};
754var hashString = function hashString(str, seed) {
755 var entry = {
756 value: 0,
757 done: false
758 };
759 var i = 0;
760 var length = str.length;
761 var iterator = {
762 next: function next() {
763 if (i < length) {
764 entry.value = str.charCodeAt(i++);
765 } else {
766 entry.done = true;
767 }
768
769 return entry;
770 }
771 };
772 return hashIterableInts(iterator, seed);
773};
774var hashStrings = function hashStrings() {
775 return hashStringsArray(arguments);
776};
777var hashStringsArray = function hashStringsArray(strs) {
778 var hash;
779
780 for (var i = 0; i < strs.length; i++) {
781 var str = strs[i];
782
783 if (i === 0) {
784 hash = hashString(str);
785 } else {
786 hash = hashString(str, hash);
787 }
788 }
789
790 return hash;
791};
792
793/*global console */
794var warningsEnabled = true;
795var warnSupported = console.warn != null; // eslint-disable-line no-console
796
797var traceSupported = console.trace != null; // eslint-disable-line no-console
798
799var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
800var trueify = function trueify() {
801 return true;
802};
803var falsify = function falsify() {
804 return false;
805};
806var zeroify = function zeroify() {
807 return 0;
808};
809var noop = function noop() {};
810var error = function error(msg) {
811 throw new Error(msg);
812};
813var warnings = function warnings(enabled) {
814 if (enabled !== undefined) {
815 warningsEnabled = !!enabled;
816 } else {
817 return warningsEnabled;
818 }
819};
820var warn = function warn(msg) {
821 /* eslint-disable no-console */
822 if (!warnings()) {
823 return;
824 }
825
826 if (warnSupported) {
827 console.warn(msg);
828 } else {
829 console.log(msg);
830
831 if (traceSupported) {
832 console.trace();
833 }
834 }
835};
836/* eslint-enable */
837
838var clone = function clone(obj) {
839 return extend({}, obj);
840}; // gets a shallow copy of the argument
841
842var copy = function copy(obj) {
843 if (obj == null) {
844 return obj;
845 }
846
847 if (array(obj)) {
848 return obj.slice();
849 } else if (plainObject(obj)) {
850 return clone(obj);
851 } else {
852 return obj;
853 }
854};
855var copyArray = function copyArray(arr) {
856 return arr.slice();
857};
858var uuid = function uuid(a, b
859/* placeholders */
860) {
861 for ( // loop :)
862 b = a = ''; // b - result , a - numeric letiable
863 a++ < 36; //
864 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
865 ? // return a random number or 4
866 (a ^ 15 // if "a" is not 15
867 ? // genetate a random number from 0 to 15
868 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
869 : 4 // otherwise 4
870 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
871 ) {
872 }
873
874 return b;
875};
876var _staticEmptyObject = {};
877var staticEmptyObject = function staticEmptyObject() {
878 return _staticEmptyObject;
879};
880var defaults = function defaults(_defaults) {
881 var keys = Object.keys(_defaults);
882 return function (opts) {
883 var filledOpts = {};
884
885 for (var i = 0; i < keys.length; i++) {
886 var key = keys[i];
887 var optVal = opts == null ? undefined : opts[key];
888 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
889 }
890
891 return filledOpts;
892 };
893};
894var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
895 for (var i = arr.length; i >= 0; i--) {
896 if (arr[i] === ele) {
897 arr.splice(i, 1);
898
899 if (!manyCopies) {
900 break;
901 }
902 }
903 }
904};
905var clearArray = function clearArray(arr) {
906 arr.splice(0, arr.length);
907};
908var push = function push(arr, otherArr) {
909 for (var i = 0; i < otherArr.length; i++) {
910 var el = otherArr[i];
911 arr.push(el);
912 }
913};
914var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
915 if (prefix) {
916 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
917 }
918
919 return obj[propName];
920};
921var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
922 if (prefix) {
923 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
924 }
925
926 obj[propName] = value;
927};
928
929/* global Map */
930var ObjectMap =
931/*#__PURE__*/
932function () {
933 function ObjectMap() {
934 _classCallCheck(this, ObjectMap);
935
936 this._obj = {};
937 }
938
939 _createClass(ObjectMap, [{
940 key: "set",
941 value: function set(key, val) {
942 this._obj[key] = val;
943 return this;
944 }
945 }, {
946 key: "delete",
947 value: function _delete(key) {
948 this._obj[key] = undefined;
949 return this;
950 }
951 }, {
952 key: "clear",
953 value: function clear() {
954 this._obj = {};
955 }
956 }, {
957 key: "has",
958 value: function has(key) {
959 return this._obj[key] !== undefined;
960 }
961 }, {
962 key: "get",
963 value: function get(key) {
964 return this._obj[key];
965 }
966 }]);
967
968 return ObjectMap;
969}();
970
971var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
972
973/* global Set */
974var undef = "undefined" ;
975
976var ObjectSet =
977/*#__PURE__*/
978function () {
979 function ObjectSet(arrayOrObjectSet) {
980 _classCallCheck(this, ObjectSet);
981
982 this._obj = Object.create(null);
983 this.size = 0;
984
985 if (arrayOrObjectSet != null) {
986 var arr;
987
988 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
989 arr = arrayOrObjectSet.toArray();
990 } else {
991 arr = arrayOrObjectSet;
992 }
993
994 for (var i = 0; i < arr.length; i++) {
995 this.add(arr[i]);
996 }
997 }
998 }
999
1000 _createClass(ObjectSet, [{
1001 key: "instanceString",
1002 value: function instanceString() {
1003 return 'set';
1004 }
1005 }, {
1006 key: "add",
1007 value: function add(val) {
1008 var o = this._obj;
1009
1010 if (o[val] !== 1) {
1011 o[val] = 1;
1012 this.size++;
1013 }
1014 }
1015 }, {
1016 key: "delete",
1017 value: function _delete(val) {
1018 var o = this._obj;
1019
1020 if (o[val] === 1) {
1021 o[val] = 0;
1022 this.size--;
1023 }
1024 }
1025 }, {
1026 key: "clear",
1027 value: function clear() {
1028 this._obj = Object.create(null);
1029 }
1030 }, {
1031 key: "has",
1032 value: function has(val) {
1033 return this._obj[val] === 1;
1034 }
1035 }, {
1036 key: "toArray",
1037 value: function toArray() {
1038 var _this = this;
1039
1040 return Object.keys(this._obj).filter(function (key) {
1041 return _this.has(key);
1042 });
1043 }
1044 }, {
1045 key: "forEach",
1046 value: function forEach(callback, thisArg) {
1047 return this.toArray().forEach(callback, thisArg);
1048 }
1049 }]);
1050
1051 return ObjectSet;
1052}();
1053
1054var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1055
1056var Element = function Element(cy, params, restore) {
1057 restore = restore === undefined || restore ? true : false;
1058
1059 if (cy === undefined || params === undefined || !core(cy)) {
1060 error('An element must have a core reference and parameters set');
1061 return;
1062 }
1063
1064 var group = params.group; // try to automatically infer the group if unspecified
1065
1066 if (group == null) {
1067 if (params.data && params.data.source != null && params.data.target != null) {
1068 group = 'edges';
1069 } else {
1070 group = 'nodes';
1071 }
1072 } // validate group
1073
1074
1075 if (group !== 'nodes' && group !== 'edges') {
1076 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1077 return;
1078 } // make the element array-like, just like a collection
1079
1080
1081 this.length = 1;
1082 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1083
1084 var _p = this._private = {
1085 cy: cy,
1086 single: true,
1087 // indicates this is an element
1088 data: params.data || {},
1089 // data object
1090 position: params.position || {
1091 x: 0,
1092 y: 0
1093 },
1094 // (x, y) position pair
1095 autoWidth: undefined,
1096 // width and height of nodes calculated by the renderer when set to special 'auto' value
1097 autoHeight: undefined,
1098 autoPadding: undefined,
1099 compoundBoundsClean: false,
1100 // whether the compound dimensions need to be recalculated the next time dimensions are read
1101 listeners: [],
1102 // array of bound listeners
1103 group: group,
1104 // string; 'nodes' or 'edges'
1105 style: {},
1106 // properties as set by the style
1107 rstyle: {},
1108 // properties for style sent from the renderer to the core
1109 styleCxts: [],
1110 // applied style contexts from the styler
1111 styleKeys: {},
1112 // per-group keys of style property values
1113 removed: true,
1114 // whether it's inside the vis; true if removed (set true here since we call restore)
1115 selected: params.selected ? true : false,
1116 // whether it's selected
1117 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1118 // whether it's selectable
1119 locked: params.locked ? true : false,
1120 // whether the element is locked (cannot be moved)
1121 grabbed: false,
1122 // whether the element is grabbed by the mouse; renderer sets this privately
1123 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1124 // whether the element can be grabbed
1125 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1126 // whether the element has passthrough panning enabled
1127 active: false,
1128 // whether the element is active from user interaction
1129 classes: new Set$1(),
1130 // map ( className => true )
1131 animation: {
1132 // object for currently-running animations
1133 current: [],
1134 queue: []
1135 },
1136 rscratch: {},
1137 // object in which the renderer can store information
1138 scratch: params.scratch || {},
1139 // scratch objects
1140 edges: [],
1141 // array of connected edges
1142 children: [],
1143 // array of children
1144 parent: null,
1145 // parent ref
1146 traversalCache: {},
1147 // cache of output of traversal functions
1148 backgrounding: false,
1149 // whether background images are loading
1150 bbCache: null,
1151 // cache of the current bounding box
1152 bbCacheShift: {
1153 x: 0,
1154 y: 0
1155 },
1156 // shift applied to cached bb to be applied on next get
1157 bodyBounds: null,
1158 // bounds cache of element body, w/o overlay
1159 overlayBounds: null,
1160 // bounds cache of element body, including overlay
1161 labelBounds: {
1162 // bounds cache of labels
1163 all: null,
1164 source: null,
1165 target: null,
1166 main: null
1167 },
1168 arrowBounds: {
1169 // bounds cache of edge arrows
1170 source: null,
1171 target: null,
1172 'mid-source': null,
1173 'mid-target': null
1174 }
1175 };
1176
1177 if (_p.position.x == null) {
1178 _p.position.x = 0;
1179 }
1180
1181 if (_p.position.y == null) {
1182 _p.position.y = 0;
1183 } // renderedPosition overrides if specified
1184
1185
1186 if (params.renderedPosition) {
1187 var rpos = params.renderedPosition;
1188 var pan = cy.pan();
1189 var zoom = cy.zoom();
1190 _p.position = {
1191 x: (rpos.x - pan.x) / zoom,
1192 y: (rpos.y - pan.y) / zoom
1193 };
1194 }
1195
1196 var classes = [];
1197
1198 if (array(params.classes)) {
1199 classes = params.classes;
1200 } else if (string(params.classes)) {
1201 classes = params.classes.split(/\s+/);
1202 }
1203
1204 for (var i = 0, l = classes.length; i < l; i++) {
1205 var cls = classes[i];
1206
1207 if (!cls || cls === '') {
1208 continue;
1209 }
1210
1211 _p.classes.add(cls);
1212 }
1213
1214 this.createEmitter();
1215 var bypass = params.style || params.css;
1216
1217 if (bypass) {
1218 warn('Setting a `style` bypass at element creation is deprecated');
1219 this.style(bypass);
1220 }
1221
1222 if (restore === undefined || restore) {
1223 this.restore();
1224 }
1225};
1226
1227var defineSearch = function defineSearch(params) {
1228 params = {
1229 bfs: params.bfs || !params.dfs,
1230 dfs: params.dfs || !params.bfs
1231 }; // from pseudocode on wikipedia
1232
1233 return function searchFn(roots, fn$1, directed) {
1234 var options;
1235
1236 if (plainObject(roots) && !elementOrCollection(roots)) {
1237 options = roots;
1238 roots = options.roots || options.root;
1239 fn$1 = options.visit;
1240 directed = options.directed;
1241 }
1242
1243 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1244 fn$1 = fn(fn$1) ? fn$1 : function () {};
1245 var cy = this._private.cy;
1246 var v = roots = string(roots) ? this.filter(roots) : roots;
1247 var Q = [];
1248 var connectedNodes = [];
1249 var connectedBy = {};
1250 var id2depth = {};
1251 var V = {};
1252 var j = 0;
1253 var found;
1254
1255 var _this$byGroup = this.byGroup(),
1256 nodes = _this$byGroup.nodes,
1257 edges = _this$byGroup.edges; // enqueue v
1258
1259
1260 for (var i = 0; i < v.length; i++) {
1261 var vi = v[i];
1262 var viId = vi.id();
1263
1264 if (vi.isNode()) {
1265 Q.unshift(vi);
1266
1267 if (params.bfs) {
1268 V[viId] = true;
1269 connectedNodes.push(vi);
1270 }
1271
1272 id2depth[viId] = 0;
1273 }
1274 }
1275
1276 var _loop2 = function _loop2() {
1277 var v = params.bfs ? Q.shift() : Q.pop();
1278 var vId = v.id();
1279
1280 if (params.dfs) {
1281 if (V[vId]) {
1282 return "continue";
1283 }
1284
1285 V[vId] = true;
1286 connectedNodes.push(v);
1287 }
1288
1289 var depth = id2depth[vId];
1290 var prevEdge = connectedBy[vId];
1291 var src = prevEdge != null ? prevEdge.source() : null;
1292 var tgt = prevEdge != null ? prevEdge.target() : null;
1293 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1294 var ret = void 0;
1295 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1296
1297 if (ret === true) {
1298 found = v;
1299 return "break";
1300 }
1301
1302 if (ret === false) {
1303 return "break";
1304 }
1305
1306 var vwEdges = v.connectedEdges().filter(function (e) {
1307 return (!directed || e.source().same(v)) && edges.has(e);
1308 });
1309
1310 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1311 var e = vwEdges[_i2];
1312 var w = e.connectedNodes().filter(function (n) {
1313 return !n.same(v) && nodes.has(n);
1314 });
1315 var wId = w.id();
1316
1317 if (w.length !== 0 && !V[wId]) {
1318 w = w[0];
1319 Q.push(w);
1320
1321 if (params.bfs) {
1322 V[wId] = true;
1323 connectedNodes.push(w);
1324 }
1325
1326 connectedBy[wId] = e;
1327 id2depth[wId] = id2depth[vId] + 1;
1328 }
1329 }
1330 };
1331
1332 _loop: while (Q.length !== 0) {
1333 var _ret = _loop2();
1334
1335 switch (_ret) {
1336 case "continue":
1337 continue;
1338
1339 case "break":
1340 break _loop;
1341 }
1342 }
1343
1344 var connectedEles = cy.collection();
1345
1346 for (var _i = 0; _i < connectedNodes.length; _i++) {
1347 var node = connectedNodes[_i];
1348 var edge = connectedBy[node.id()];
1349
1350 if (edge != null) {
1351 connectedEles.merge(edge);
1352 }
1353
1354 connectedEles.merge(node);
1355 }
1356
1357 return {
1358 path: cy.collection(connectedEles),
1359 found: cy.collection(found)
1360 };
1361 };
1362}; // search, spanning trees, etc
1363
1364
1365var elesfn = {
1366 breadthFirstSearch: defineSearch({
1367 bfs: true
1368 }),
1369 depthFirstSearch: defineSearch({
1370 dfs: true
1371 })
1372}; // nice, short mathemathical alias
1373
1374elesfn.bfs = elesfn.breadthFirstSearch;
1375elesfn.dfs = elesfn.depthFirstSearch;
1376
1377var dijkstraDefaults = defaults({
1378 root: null,
1379 weight: function weight(edge) {
1380 return 1;
1381 },
1382 directed: false
1383});
1384var elesfn$1 = {
1385 dijkstra: function dijkstra(options) {
1386 if (!plainObject(options)) {
1387 var args = arguments;
1388 options = {
1389 root: args[0],
1390 weight: args[1],
1391 directed: args[2]
1392 };
1393 }
1394
1395 var _dijkstraDefaults = dijkstraDefaults(options),
1396 root = _dijkstraDefaults.root,
1397 weight = _dijkstraDefaults.weight,
1398 directed = _dijkstraDefaults.directed;
1399
1400 var eles = this;
1401 var weightFn = weight;
1402 var source = string(root) ? this.filter(root)[0] : root[0];
1403 var dist = {};
1404 var prev = {};
1405 var knownDist = {};
1406
1407 var _this$byGroup = this.byGroup(),
1408 nodes = _this$byGroup.nodes,
1409 edges = _this$byGroup.edges;
1410
1411 edges.unmergeBy(function (ele) {
1412 return ele.isLoop();
1413 });
1414
1415 var getDist = function getDist(node) {
1416 return dist[node.id()];
1417 };
1418
1419 var setDist = function setDist(node, d) {
1420 dist[node.id()] = d;
1421 Q.updateItem(node);
1422 };
1423
1424 var Q = new Heap(function (a, b) {
1425 return getDist(a) - getDist(b);
1426 });
1427
1428 for (var i = 0; i < nodes.length; i++) {
1429 var node = nodes[i];
1430 dist[node.id()] = node.same(source) ? 0 : Infinity;
1431 Q.push(node);
1432 }
1433
1434 var distBetween = function distBetween(u, v) {
1435 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1436 var smallestDistance = Infinity;
1437 var smallestEdge;
1438
1439 for (var _i = 0; _i < uvs.length; _i++) {
1440 var edge = uvs[_i];
1441
1442 var _weight = weightFn(edge);
1443
1444 if (_weight < smallestDistance || !smallestEdge) {
1445 smallestDistance = _weight;
1446 smallestEdge = edge;
1447 }
1448 }
1449
1450 return {
1451 edge: smallestEdge,
1452 dist: smallestDistance
1453 };
1454 };
1455
1456 while (Q.size() > 0) {
1457 var u = Q.pop();
1458 var smalletsDist = getDist(u);
1459 var uid = u.id();
1460 knownDist[uid] = smalletsDist;
1461
1462 if (smalletsDist === Infinity) {
1463 continue;
1464 }
1465
1466 var neighbors = u.neighborhood().intersect(nodes);
1467
1468 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1469 var v = neighbors[_i2];
1470 var vid = v.id();
1471 var vDist = distBetween(u, v);
1472 var alt = smalletsDist + vDist.dist;
1473
1474 if (alt < getDist(v)) {
1475 setDist(v, alt);
1476 prev[vid] = {
1477 node: u,
1478 edge: vDist.edge
1479 };
1480 }
1481 } // for
1482
1483 } // while
1484
1485
1486 return {
1487 distanceTo: function distanceTo(node) {
1488 var target = string(node) ? nodes.filter(node)[0] : node[0];
1489 return knownDist[target.id()];
1490 },
1491 pathTo: function pathTo(node) {
1492 var target = string(node) ? nodes.filter(node)[0] : node[0];
1493 var S = [];
1494 var u = target;
1495 var uid = u.id();
1496
1497 if (target.length > 0) {
1498 S.unshift(target);
1499
1500 while (prev[uid]) {
1501 var p = prev[uid];
1502 S.unshift(p.edge);
1503 S.unshift(p.node);
1504 u = p.node;
1505 uid = u.id();
1506 }
1507 }
1508
1509 return eles.spawn(S);
1510 }
1511 };
1512 }
1513};
1514
1515var elesfn$2 = {
1516 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1517 // implemented from pseudocode from wikipedia
1518 kruskal: function kruskal(weightFn) {
1519 weightFn = weightFn || function (edge) {
1520 return 1;
1521 };
1522
1523 var _this$byGroup = this.byGroup(),
1524 nodes = _this$byGroup.nodes,
1525 edges = _this$byGroup.edges;
1526
1527 var numNodes = nodes.length;
1528 var forest = new Array(numNodes);
1529 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1530
1531 var findSetIndex = function findSetIndex(ele) {
1532 for (var i = 0; i < forest.length; i++) {
1533 var eles = forest[i];
1534
1535 if (eles.has(ele)) {
1536 return i;
1537 }
1538 }
1539 }; // start with one forest per node
1540
1541
1542 for (var i = 0; i < numNodes; i++) {
1543 forest[i] = this.spawn(nodes[i]);
1544 }
1545
1546 var S = edges.sort(function (a, b) {
1547 return weightFn(a) - weightFn(b);
1548 });
1549
1550 for (var _i = 0; _i < S.length; _i++) {
1551 var edge = S[_i];
1552 var u = edge.source()[0];
1553 var v = edge.target()[0];
1554 var setUIndex = findSetIndex(u);
1555 var setVIndex = findSetIndex(v);
1556 var setU = forest[setUIndex];
1557 var setV = forest[setVIndex];
1558
1559 if (setUIndex !== setVIndex) {
1560 A.merge(edge); // combine forests for u and v
1561
1562 setU.merge(setV);
1563 forest.splice(setVIndex, 1);
1564 }
1565 }
1566
1567 return A;
1568 }
1569};
1570
1571var aStarDefaults = defaults({
1572 root: null,
1573 goal: null,
1574 weight: function weight(edge) {
1575 return 1;
1576 },
1577 heuristic: function heuristic(edge) {
1578 return 0;
1579 },
1580 directed: false
1581});
1582var elesfn$3 = {
1583 // Implemented from pseudocode from wikipedia
1584 aStar: function aStar(options) {
1585 var cy = this.cy();
1586
1587 var _aStarDefaults = aStarDefaults(options),
1588 root = _aStarDefaults.root,
1589 goal = _aStarDefaults.goal,
1590 heuristic = _aStarDefaults.heuristic,
1591 directed = _aStarDefaults.directed,
1592 weight = _aStarDefaults.weight;
1593
1594 root = cy.collection(root)[0];
1595 goal = cy.collection(goal)[0];
1596 var sid = root.id();
1597 var tid = goal.id();
1598 var gScore = {};
1599 var fScore = {};
1600 var closedSetIds = {};
1601 var openSet = new Heap(function (a, b) {
1602 return fScore[a.id()] - fScore[b.id()];
1603 });
1604 var openSetIds = new Set$1();
1605 var cameFrom = {};
1606 var cameFromEdge = {};
1607
1608 var addToOpenSet = function addToOpenSet(ele, id) {
1609 openSet.push(ele);
1610 openSetIds.add(id);
1611 };
1612
1613 var cMin, cMinId;
1614
1615 var popFromOpenSet = function popFromOpenSet() {
1616 cMin = openSet.pop();
1617 cMinId = cMin.id();
1618 openSetIds["delete"](cMinId);
1619 };
1620
1621 var isInOpenSet = function isInOpenSet(id) {
1622 return openSetIds.has(id);
1623 };
1624
1625 addToOpenSet(root, sid);
1626 gScore[sid] = 0;
1627 fScore[sid] = heuristic(root); // Counter
1628
1629 var steps = 0; // Main loop
1630
1631 while (openSet.size() > 0) {
1632 popFromOpenSet();
1633 steps++; // If we've found our goal, then we are done
1634
1635 if (cMinId === tid) {
1636 var path = [];
1637 var pathNode = goal;
1638 var pathNodeId = tid;
1639 var pathEdge = cameFromEdge[pathNodeId];
1640
1641 for (;;) {
1642 path.unshift(pathNode);
1643
1644 if (pathEdge != null) {
1645 path.unshift(pathEdge);
1646 }
1647
1648 pathNode = cameFrom[pathNodeId];
1649
1650 if (pathNode == null) {
1651 break;
1652 }
1653
1654 pathNodeId = pathNode.id();
1655 pathEdge = cameFromEdge[pathNodeId];
1656 }
1657
1658 return {
1659 found: true,
1660 distance: gScore[cMinId],
1661 path: this.spawn(path),
1662 steps: steps
1663 };
1664 } // Add cMin to processed nodes
1665
1666
1667 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1668 // Take into account if graph is directed or not
1669
1670 var vwEdges = cMin._private.edges;
1671
1672 for (var i = 0; i < vwEdges.length; i++) {
1673 var e = vwEdges[i]; // edge must be in set of calling eles
1674
1675 if (!this.hasElementWithId(e.id())) {
1676 continue;
1677 } // cMin must be the source of edge if directed
1678
1679
1680 if (directed && e.data('source') !== cMinId) {
1681 continue;
1682 }
1683
1684 var wSrc = e.source();
1685 var wTgt = e.target();
1686 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1687 var wid = w.id(); // node must be in set of calling eles
1688
1689 if (!this.hasElementWithId(wid)) {
1690 continue;
1691 } // if node is in closedSet, ignore it
1692
1693
1694 if (closedSetIds[wid]) {
1695 continue;
1696 } // New tentative score for node w
1697
1698
1699 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1700 // w not present in openSet
1701 // OR
1702 // tentative gScore is less than previous value
1703 // w not in openSet
1704
1705 if (!isInOpenSet(wid)) {
1706 gScore[wid] = tempScore;
1707 fScore[wid] = tempScore + heuristic(w);
1708 addToOpenSet(w, wid);
1709 cameFrom[wid] = cMin;
1710 cameFromEdge[wid] = e;
1711 continue;
1712 } // w already in openSet, but with greater gScore
1713
1714
1715 if (tempScore < gScore[wid]) {
1716 gScore[wid] = tempScore;
1717 fScore[wid] = tempScore + heuristic(w);
1718 cameFrom[wid] = cMin;
1719 }
1720 } // End of neighbors update
1721
1722 } // End of main loop
1723 // If we've reached here, then we've not reached our goal
1724
1725
1726 return {
1727 found: false,
1728 distance: undefined,
1729 path: undefined,
1730 steps: steps
1731 };
1732 }
1733}; // elesfn
1734
1735var floydWarshallDefaults = defaults({
1736 weight: function weight(edge) {
1737 return 1;
1738 },
1739 directed: false
1740});
1741var elesfn$4 = {
1742 // Implemented from pseudocode from wikipedia
1743 floydWarshall: function floydWarshall(options) {
1744 var cy = this.cy();
1745
1746 var _floydWarshallDefault = floydWarshallDefaults(options),
1747 weight = _floydWarshallDefault.weight,
1748 directed = _floydWarshallDefault.directed;
1749
1750 var weightFn = weight;
1751
1752 var _this$byGroup = this.byGroup(),
1753 nodes = _this$byGroup.nodes,
1754 edges = _this$byGroup.edges;
1755
1756 var N = nodes.length;
1757 var Nsq = N * N;
1758
1759 var indexOf = function indexOf(node) {
1760 return nodes.indexOf(node);
1761 };
1762
1763 var atIndex = function atIndex(i) {
1764 return nodes[i];
1765 }; // Initialize distance matrix
1766
1767
1768 var dist = new Array(Nsq);
1769
1770 for (var n = 0; n < Nsq; n++) {
1771 var j = n % N;
1772 var i = (n - j) / N;
1773
1774 if (i === j) {
1775 dist[n] = 0;
1776 } else {
1777 dist[n] = Infinity;
1778 }
1779 } // Initialize matrix used for path reconstruction
1780 // Initialize distance matrix
1781
1782
1783 var next = new Array(Nsq);
1784 var edgeNext = new Array(Nsq); // Process edges
1785
1786 for (var _i = 0; _i < edges.length; _i++) {
1787 var edge = edges[_i];
1788 var src = edge.source()[0];
1789 var tgt = edge.target()[0];
1790
1791 if (src === tgt) {
1792 continue;
1793 } // exclude loops
1794
1795
1796 var s = indexOf(src);
1797 var t = indexOf(tgt);
1798 var st = s * N + t; // source to target index
1799
1800 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1801
1802
1803 if (dist[st] > _weight) {
1804 dist[st] = _weight;
1805 next[st] = t;
1806 edgeNext[st] = edge;
1807 } // If undirected graph, process 'reversed' edge
1808
1809
1810 if (!directed) {
1811 var ts = t * N + s; // target to source index
1812
1813 if (!directed && dist[ts] > _weight) {
1814 dist[ts] = _weight;
1815 next[ts] = s;
1816 edgeNext[ts] = edge;
1817 }
1818 }
1819 } // Main loop
1820
1821
1822 for (var k = 0; k < N; k++) {
1823 for (var _i2 = 0; _i2 < N; _i2++) {
1824 var ik = _i2 * N + k;
1825
1826 for (var _j = 0; _j < N; _j++) {
1827 var ij = _i2 * N + _j;
1828 var kj = k * N + _j;
1829
1830 if (dist[ik] + dist[kj] < dist[ij]) {
1831 dist[ij] = dist[ik] + dist[kj];
1832 next[ij] = next[ik];
1833 }
1834 }
1835 }
1836 }
1837
1838 var getArgEle = function getArgEle(ele) {
1839 return (string(ele) ? cy.filter(ele) : ele)[0];
1840 };
1841
1842 var indexOfArgEle = function indexOfArgEle(ele) {
1843 return indexOf(getArgEle(ele));
1844 };
1845
1846 var res = {
1847 distance: function distance(from, to) {
1848 var i = indexOfArgEle(from);
1849 var j = indexOfArgEle(to);
1850 return dist[i * N + j];
1851 },
1852 path: function path(from, to) {
1853 var i = indexOfArgEle(from);
1854 var j = indexOfArgEle(to);
1855 var fromNode = atIndex(i);
1856
1857 if (i === j) {
1858 return fromNode.collection();
1859 }
1860
1861 if (next[i * N + j] == null) {
1862 return cy.collection();
1863 }
1864
1865 var path = cy.collection();
1866 var prev = i;
1867 var edge;
1868 path.merge(fromNode);
1869
1870 while (i !== j) {
1871 prev = i;
1872 i = next[i * N + j];
1873 edge = edgeNext[prev * N + i];
1874 path.merge(edge);
1875 path.merge(atIndex(i));
1876 }
1877
1878 return path;
1879 }
1880 };
1881 return res;
1882 } // floydWarshall
1883
1884}; // elesfn
1885
1886var bellmanFordDefaults = defaults({
1887 weight: function weight(edge) {
1888 return 1;
1889 },
1890 directed: false,
1891 root: null
1892});
1893var elesfn$5 = {
1894 // Implemented from pseudocode from wikipedia
1895 bellmanFord: function bellmanFord(options) {
1896 var _this = this;
1897
1898 var _bellmanFordDefaults = bellmanFordDefaults(options),
1899 weight = _bellmanFordDefaults.weight,
1900 directed = _bellmanFordDefaults.directed,
1901 root = _bellmanFordDefaults.root;
1902
1903 var weightFn = weight;
1904 var eles = this;
1905 var cy = this.cy();
1906
1907 var _this$byGroup = this.byGroup(),
1908 edges = _this$byGroup.edges,
1909 nodes = _this$byGroup.nodes;
1910
1911 var numNodes = nodes.length;
1912 var infoMap = new Map$1();
1913 var hasNegativeWeightCycle = false;
1914 var negativeWeightCycles = [];
1915 root = cy.collection(root)[0]; // in case selector passed
1916
1917 edges.unmergeBy(function (edge) {
1918 return edge.isLoop();
1919 });
1920 var numEdges = edges.length;
1921
1922 var getInfo = function getInfo(node) {
1923 var obj = infoMap.get(node.id());
1924
1925 if (!obj) {
1926 obj = {};
1927 infoMap.set(node.id(), obj);
1928 }
1929
1930 return obj;
1931 };
1932
1933 var getNodeFromTo = function getNodeFromTo(to) {
1934 return (string(to) ? cy.$(to) : to)[0];
1935 };
1936
1937 var distanceTo = function distanceTo(to) {
1938 return getInfo(getNodeFromTo(to)).dist;
1939 };
1940
1941 var pathTo = function pathTo(to) {
1942 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1943 var end = getNodeFromTo(to);
1944 var path = [];
1945 var node = end;
1946
1947 for (;;) {
1948 if (node == null) {
1949 return _this.spawn();
1950 }
1951
1952 var _getInfo = getInfo(node),
1953 edge = _getInfo.edge,
1954 pred = _getInfo.pred;
1955
1956 path.unshift(node[0]);
1957
1958 if (node.same(thisStart) && path.length > 0) {
1959 break;
1960 }
1961
1962 if (edge != null) {
1963 path.unshift(edge);
1964 }
1965
1966 node = pred;
1967 }
1968
1969 return eles.spawn(path);
1970 }; // Initializations { dist, pred, edge }
1971
1972
1973 for (var i = 0; i < numNodes; i++) {
1974 var node = nodes[i];
1975 var info = getInfo(node);
1976
1977 if (node.same(root)) {
1978 info.dist = 0;
1979 } else {
1980 info.dist = Infinity;
1981 }
1982
1983 info.pred = null;
1984 info.edge = null;
1985 } // Edges relaxation
1986
1987
1988 var replacedEdge = false;
1989
1990 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
1991 var dist = info1.dist + weight;
1992
1993 if (dist < info2.dist && !edge.same(info1.edge)) {
1994 info2.dist = dist;
1995 info2.pred = node1;
1996 info2.edge = edge;
1997 replacedEdge = true;
1998 }
1999 };
2000
2001 for (var _i = 1; _i < numNodes; _i++) {
2002 replacedEdge = false;
2003
2004 for (var e = 0; e < numEdges; e++) {
2005 var edge = edges[e];
2006 var src = edge.source();
2007 var tgt = edge.target();
2008
2009 var _weight = weightFn(edge);
2010
2011 var srcInfo = getInfo(src);
2012 var tgtInfo = getInfo(tgt);
2013 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2014
2015 if (!directed) {
2016 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2017 }
2018 }
2019
2020 if (!replacedEdge) {
2021 break;
2022 }
2023 }
2024
2025 if (replacedEdge) {
2026 // Check for negative weight cycles
2027 for (var _e = 0; _e < numEdges; _e++) {
2028 var _edge = edges[_e];
2029
2030 var _src = _edge.source();
2031
2032 var _tgt = _edge.target();
2033
2034 var _weight2 = weightFn(_edge);
2035
2036 var srcDist = getInfo(_src).dist;
2037 var tgtDist = getInfo(_tgt).dist;
2038
2039 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2040 warn('Graph contains a negative weight cycle for Bellman-Ford');
2041 hasNegativeWeightCycle = true;
2042 break;
2043 }
2044 }
2045 }
2046
2047 return {
2048 distanceTo: distanceTo,
2049 pathTo: pathTo,
2050 hasNegativeWeightCycle: hasNegativeWeightCycle,
2051 negativeWeightCycles: negativeWeightCycles
2052 };
2053 } // bellmanFord
2054
2055}; // elesfn
2056
2057var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2058// Updates the remaining edge lists
2059// Receives as a paramater the edge which causes the collapse
2060
2061var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2062 if (remainingEdges.length === 0) {
2063 error("Karger-Stein must be run on a connected (sub)graph");
2064 }
2065
2066 var edgeInfo = remainingEdges[edgeIndex];
2067 var sourceIn = edgeInfo[1];
2068 var targetIn = edgeInfo[2];
2069 var partition1 = nodeMap[sourceIn];
2070 var partition2 = nodeMap[targetIn];
2071 var newEdges = remainingEdges; // re-use array
2072 // Delete all edges between partition1 and partition2
2073
2074 for (var i = newEdges.length - 1; i >= 0; i--) {
2075 var edge = newEdges[i];
2076 var src = edge[1];
2077 var tgt = edge[2];
2078
2079 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2080 newEdges.splice(i, 1);
2081 }
2082 } // All edges pointing to partition2 should now point to partition1
2083
2084
2085 for (var _i = 0; _i < newEdges.length; _i++) {
2086 var _edge = newEdges[_i];
2087
2088 if (_edge[1] === partition2) {
2089 // Check source
2090 newEdges[_i] = _edge.slice(); // copy
2091
2092 newEdges[_i][1] = partition1;
2093 } else if (_edge[2] === partition2) {
2094 // Check target
2095 newEdges[_i] = _edge.slice(); // copy
2096
2097 newEdges[_i][2] = partition1;
2098 }
2099 } // Move all nodes from partition2 to partition1
2100
2101
2102 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2103 if (nodeMap[_i2] === partition2) {
2104 nodeMap[_i2] = partition1;
2105 }
2106 }
2107
2108 return newEdges;
2109}; // Contracts a graph until we reach a certain number of meta nodes
2110
2111
2112var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2113 while (size > sizeLimit) {
2114 // Choose an edge randomly
2115 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2116
2117 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2118 size--;
2119 }
2120
2121 return remainingEdges;
2122};
2123
2124var elesfn$6 = {
2125 // Computes the minimum cut of an undirected graph
2126 // Returns the correct answer with high probability
2127 kargerStein: function kargerStein() {
2128 var _this$byGroup = this.byGroup(),
2129 nodes = _this$byGroup.nodes,
2130 edges = _this$byGroup.edges;
2131
2132 edges.unmergeBy(function (edge) {
2133 return edge.isLoop();
2134 });
2135 var numNodes = nodes.length;
2136 var numEdges = edges.length;
2137 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2138 var stopSize = Math.floor(numNodes / sqrt2);
2139
2140 if (numNodes < 2) {
2141 error('At least 2 nodes are required for Karger-Stein algorithm');
2142 return undefined;
2143 } // Now store edge destination as indexes
2144 // Format for each edge (edge index, source node index, target node index)
2145
2146
2147 var edgeIndexes = [];
2148
2149 for (var i = 0; i < numEdges; i++) {
2150 var e = edges[i];
2151 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2152 } // We will store the best cut found here
2153
2154
2155 var minCutSize = Infinity;
2156 var minCutEdgeIndexes = [];
2157 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2158
2159 var metaNodeMap = new Array(numNodes);
2160 var metaNodeMap2 = new Array(numNodes);
2161
2162 var copyNodesMap = function copyNodesMap(from, to) {
2163 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2164 to[_i3] = from[_i3];
2165 }
2166 }; // Main loop
2167
2168
2169 for (var iter = 0; iter <= numIter; iter++) {
2170 // Reset meta node partition
2171 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2172 metaNodeMap[_i4] = _i4;
2173 } // Contract until stop point (stopSize nodes)
2174
2175
2176 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2177 var edgesState2 = edgesState.slice(); // copy
2178 // Create a copy of the colapsed nodes state
2179
2180 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2181
2182 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2183 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2184
2185 if (res1.length <= res2.length && res1.length < minCutSize) {
2186 minCutSize = res1.length;
2187 minCutEdgeIndexes = res1;
2188 copyNodesMap(metaNodeMap, minCutNodeMap);
2189 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2190 minCutSize = res2.length;
2191 minCutEdgeIndexes = res2;
2192 copyNodesMap(metaNodeMap2, minCutNodeMap);
2193 }
2194 } // end of main loop
2195 // Construct result
2196
2197
2198 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2199 return edges[e[0]];
2200 }));
2201 var partition1 = this.spawn();
2202 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2203
2204 var witnessNodePartition = minCutNodeMap[0];
2205
2206 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2207 var partitionId = minCutNodeMap[_i5];
2208 var node = nodes[_i5];
2209
2210 if (partitionId === witnessNodePartition) {
2211 partition1.merge(node);
2212 } else {
2213 partition2.merge(node);
2214 }
2215 }
2216
2217 var ret = {
2218 cut: cut,
2219 partition1: partition1,
2220 partition2: partition2
2221 };
2222 return ret;
2223 }
2224}; // elesfn
2225
2226var copyPosition = function copyPosition(p) {
2227 return {
2228 x: p.x,
2229 y: p.y
2230 };
2231};
2232var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2233 return {
2234 x: p.x * zoom + pan.x,
2235 y: p.y * zoom + pan.y
2236 };
2237};
2238var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2239 return {
2240 x: (p.x - pan.x) / zoom,
2241 y: (p.y - pan.y) / zoom
2242 };
2243};
2244var array2point = function array2point(arr) {
2245 return {
2246 x: arr[0],
2247 y: arr[1]
2248 };
2249};
2250var min = function min(arr) {
2251 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2252 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2253 var min = Infinity;
2254
2255 for (var i = begin; i < end; i++) {
2256 var val = arr[i];
2257
2258 if (isFinite(val)) {
2259 min = Math.min(val, min);
2260 }
2261 }
2262
2263 return min;
2264};
2265var max = function max(arr) {
2266 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2267 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2268 var max = -Infinity;
2269
2270 for (var i = begin; i < end; i++) {
2271 var val = arr[i];
2272
2273 if (isFinite(val)) {
2274 max = Math.max(val, max);
2275 }
2276 }
2277
2278 return max;
2279};
2280var mean = function mean(arr) {
2281 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2282 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2283 var total = 0;
2284 var n = 0;
2285
2286 for (var i = begin; i < end; i++) {
2287 var val = arr[i];
2288
2289 if (isFinite(val)) {
2290 total += val;
2291 n++;
2292 }
2293 }
2294
2295 return total / n;
2296};
2297var median = function median(arr) {
2298 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2299 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2300 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2301 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2302 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2303
2304 if (copy) {
2305 arr = arr.slice(begin, end);
2306 } else {
2307 if (end < arr.length) {
2308 arr.splice(end, arr.length - end);
2309 }
2310
2311 if (begin > 0) {
2312 arr.splice(0, begin);
2313 }
2314 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2315
2316
2317 var off = 0; // offset from non-finite values
2318
2319 for (var i = arr.length - 1; i >= 0; i--) {
2320 var v = arr[i];
2321
2322 if (includeHoles) {
2323 if (!isFinite(v)) {
2324 arr[i] = -Infinity;
2325 off++;
2326 }
2327 } else {
2328 // just remove it if we don't want to consider holes
2329 arr.splice(i, 1);
2330 }
2331 }
2332
2333 if (sort) {
2334 arr.sort(function (a, b) {
2335 return a - b;
2336 }); // requires copy = true if you don't want to change the orig
2337 }
2338
2339 var len = arr.length;
2340 var mid = Math.floor(len / 2);
2341
2342 if (len % 2 !== 0) {
2343 return arr[mid + 1 + off];
2344 } else {
2345 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2346 }
2347};
2348var deg2rad = function deg2rad(deg) {
2349 return Math.PI * deg / 180;
2350};
2351var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2352 return Math.atan2(dispY, dispX) - Math.PI / 2;
2353};
2354var log2 = Math.log2 || function (n) {
2355 return Math.log(n) / Math.log(2);
2356};
2357var signum = function signum(x) {
2358 if (x > 0) {
2359 return 1;
2360 } else if (x < 0) {
2361 return -1;
2362 } else {
2363 return 0;
2364 }
2365};
2366var dist = function dist(p1, p2) {
2367 return Math.sqrt(sqdist(p1, p2));
2368};
2369var sqdist = function sqdist(p1, p2) {
2370 var dx = p2.x - p1.x;
2371 var dy = p2.y - p1.y;
2372 return dx * dx + dy * dy;
2373};
2374var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2375 var length = v.length; // First, get sum of all elements
2376
2377 var total = 0;
2378
2379 for (var i = 0; i < length; i++) {
2380 total += v[i];
2381 } // Now, divide each by the sum of all elements
2382
2383
2384 for (var _i = 0; _i < length; _i++) {
2385 v[_i] = v[_i] / total;
2386 }
2387
2388 return v;
2389};
2390
2391var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2392 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2393};
2394var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2395 return {
2396 x: qbezierAt(p0.x, p1.x, p2.x, t),
2397 y: qbezierAt(p0.y, p1.y, p2.y, t)
2398 };
2399};
2400var lineAt = function lineAt(p0, p1, t, d) {
2401 var vec = {
2402 x: p1.x - p0.x,
2403 y: p1.y - p0.y
2404 };
2405 var vecDist = dist(p0, p1);
2406 var normVec = {
2407 x: vec.x / vecDist,
2408 y: vec.y / vecDist
2409 };
2410 t = t == null ? 0 : t;
2411 d = d != null ? d : t * vecDist;
2412 return {
2413 x: p0.x + normVec.x * d,
2414 y: p0.y + normVec.y * d
2415 };
2416};
2417var bound = function bound(min, val, max) {
2418 return Math.max(min, Math.min(max, val));
2419}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2420
2421var makeBoundingBox = function makeBoundingBox(bb) {
2422 if (bb == null) {
2423 return {
2424 x1: Infinity,
2425 y1: Infinity,
2426 x2: -Infinity,
2427 y2: -Infinity,
2428 w: 0,
2429 h: 0
2430 };
2431 } else if (bb.x1 != null && bb.y1 != null) {
2432 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2433 return {
2434 x1: bb.x1,
2435 y1: bb.y1,
2436 x2: bb.x2,
2437 y2: bb.y2,
2438 w: bb.x2 - bb.x1,
2439 h: bb.y2 - bb.y1
2440 };
2441 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2442 return {
2443 x1: bb.x1,
2444 y1: bb.y1,
2445 x2: bb.x1 + bb.w,
2446 y2: bb.y1 + bb.h,
2447 w: bb.w,
2448 h: bb.h
2449 };
2450 }
2451 }
2452};
2453var copyBoundingBox = function copyBoundingBox(bb) {
2454 return {
2455 x1: bb.x1,
2456 x2: bb.x2,
2457 w: bb.w,
2458 y1: bb.y1,
2459 y2: bb.y2,
2460 h: bb.h
2461 };
2462};
2463var clearBoundingBox = function clearBoundingBox(bb) {
2464 bb.x1 = Infinity;
2465 bb.y1 = Infinity;
2466 bb.x2 = -Infinity;
2467 bb.y2 = -Infinity;
2468 bb.w = 0;
2469 bb.h = 0;
2470};
2471var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2472 // update bb1 with bb2 bounds
2473 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2474 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2475 bb1.w = bb1.x2 - bb1.x1;
2476 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2477 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2478 bb1.h = bb1.y2 - bb1.y1;
2479};
2480var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2481 bb.x1 = Math.min(bb.x1, x);
2482 bb.x2 = Math.max(bb.x2, x);
2483 bb.w = bb.x2 - bb.x1;
2484 bb.y1 = Math.min(bb.y1, y);
2485 bb.y2 = Math.max(bb.y2, y);
2486 bb.h = bb.y2 - bb.y1;
2487};
2488var expandBoundingBox = function expandBoundingBox(bb) {
2489 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2490 bb.x1 -= padding;
2491 bb.x2 += padding;
2492 bb.y1 -= padding;
2493 bb.y2 += padding;
2494 bb.w = bb.x2 - bb.x1;
2495 bb.h = bb.y2 - bb.y1;
2496 return bb;
2497};
2498var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2499 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2500 var top, right, bottom, left;
2501
2502 if (padding.length === 1) {
2503 top = right = bottom = left = padding[0];
2504 } else if (padding.length === 2) {
2505 top = bottom = padding[0];
2506 left = right = padding[1];
2507 } else if (padding.length === 4) {
2508 var _padding = _slicedToArray(padding, 4);
2509
2510 top = _padding[0];
2511 right = _padding[1];
2512 bottom = _padding[2];
2513 left = _padding[3];
2514 }
2515
2516 bb.x1 -= left;
2517 bb.x2 += right;
2518 bb.y1 -= top;
2519 bb.y2 += bottom;
2520 bb.w = bb.x2 - bb.x1;
2521 bb.h = bb.y2 - bb.y1;
2522 return bb;
2523};
2524
2525var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2526 bb1.x1 = bb2.x1;
2527 bb1.y1 = bb2.y1;
2528 bb1.x2 = bb2.x2;
2529 bb1.y2 = bb2.y2;
2530 bb1.w = bb1.x2 - bb1.x1;
2531 bb1.h = bb1.y2 - bb1.y1;
2532};
2533var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
2534 bb.x1 += delta.x;
2535 bb.x2 += delta.x;
2536 bb.y1 += delta.y;
2537 bb.y2 += delta.y;
2538};
2539var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2540 // case: one bb to right of other
2541 if (bb1.x1 > bb2.x2) {
2542 return false;
2543 }
2544
2545 if (bb2.x1 > bb1.x2) {
2546 return false;
2547 } // case: one bb to left of other
2548
2549
2550 if (bb1.x2 < bb2.x1) {
2551 return false;
2552 }
2553
2554 if (bb2.x2 < bb1.x1) {
2555 return false;
2556 } // case: one bb above other
2557
2558
2559 if (bb1.y2 < bb2.y1) {
2560 return false;
2561 }
2562
2563 if (bb2.y2 < bb1.y1) {
2564 return false;
2565 } // case: one bb below other
2566
2567
2568 if (bb1.y1 > bb2.y2) {
2569 return false;
2570 }
2571
2572 if (bb2.y1 > bb1.y2) {
2573 return false;
2574 } // otherwise, must have some overlap
2575
2576
2577 return true;
2578};
2579var inBoundingBox = function inBoundingBox(bb, x, y) {
2580 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2581};
2582var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2583 return inBoundingBox(bb, pt.x, pt.y);
2584};
2585var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2586 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2587};
2588var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2589 var cornerRadius = getRoundRectangleRadius(width, height);
2590 var halfWidth = width / 2;
2591 var halfHeight = height / 2; // Check intersections with straight line segments
2592
2593 var straightLineIntersections; // Top segment, left to right
2594
2595 {
2596 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2597 var topStartY = nodeY - halfHeight - padding;
2598 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2599 var topEndY = topStartY;
2600 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2601
2602 if (straightLineIntersections.length > 0) {
2603 return straightLineIntersections;
2604 }
2605 } // Right segment, top to bottom
2606
2607 {
2608 var rightStartX = nodeX + halfWidth + padding;
2609 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2610 var rightEndX = rightStartX;
2611 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2612 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2613
2614 if (straightLineIntersections.length > 0) {
2615 return straightLineIntersections;
2616 }
2617 } // Bottom segment, left to right
2618
2619 {
2620 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2621 var bottomStartY = nodeY + halfHeight + padding;
2622 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2623 var bottomEndY = bottomStartY;
2624 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2625
2626 if (straightLineIntersections.length > 0) {
2627 return straightLineIntersections;
2628 }
2629 } // Left segment, top to bottom
2630
2631 {
2632 var leftStartX = nodeX - halfWidth - padding;
2633 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2634 var leftEndX = leftStartX;
2635 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2636 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2637
2638 if (straightLineIntersections.length > 0) {
2639 return straightLineIntersections;
2640 }
2641 } // Check intersections with arc segments
2642
2643 var arcIntersections; // Top Left
2644
2645 {
2646 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2647 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2648 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2649
2650 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2651 return [arcIntersections[0], arcIntersections[1]];
2652 }
2653 } // Top Right
2654
2655 {
2656 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2657 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2658 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2659
2660 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2661 return [arcIntersections[0], arcIntersections[1]];
2662 }
2663 } // Bottom Right
2664
2665 {
2666 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2667 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2668 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2669
2670 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2671 return [arcIntersections[0], arcIntersections[1]];
2672 }
2673 } // Bottom Left
2674
2675 {
2676 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2677 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2678 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2679
2680 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2681 return [arcIntersections[0], arcIntersections[1]];
2682 }
2683 }
2684 return []; // if nothing
2685};
2686var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2687 var t = tolerance;
2688 var x1 = Math.min(lx1, lx2);
2689 var x2 = Math.max(lx1, lx2);
2690 var y1 = Math.min(ly1, ly2);
2691 var y2 = Math.max(ly1, ly2);
2692 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2693};
2694var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2695 var bb = {
2696 x1: Math.min(x1, x3, x2) - tolerance,
2697 x2: Math.max(x1, x3, x2) + tolerance,
2698 y1: Math.min(y1, y3, y2) - tolerance,
2699 y2: Math.max(y1, y3, y2) + tolerance
2700 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2701
2702 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2703 // console.log('bezier out of rough bb')
2704 return false;
2705 } else {
2706 // console.log('do more expensive check');
2707 return true;
2708 }
2709};
2710var solveQuadratic = function solveQuadratic(a, b, c, val) {
2711 c -= val;
2712 var r = b * b - 4 * a * c;
2713
2714 if (r < 0) {
2715 return [];
2716 }
2717
2718 var sqrtR = Math.sqrt(r);
2719 var denom = 2 * a;
2720 var root1 = (-b + sqrtR) / denom;
2721 var root2 = (-b - sqrtR) / denom;
2722 return [root1, root2];
2723};
2724var solveCubic = function solveCubic(a, b, c, d, result) {
2725 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2726 // r is the real component, i is the imaginary component
2727 // An implementation of the Cardano method from the year 1545
2728 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2729 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2730
2731 if (a === 0) {
2732 a = epsilon;
2733 }
2734
2735 b /= a;
2736 c /= a;
2737 d /= a;
2738 var discriminant, q, r, dum1, s, t, term1, r13;
2739 q = (3.0 * c - b * b) / 9.0;
2740 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2741 r /= 54.0;
2742 discriminant = q * q * q + r * r;
2743 result[1] = 0;
2744 term1 = b / 3.0;
2745
2746 if (discriminant > 0) {
2747 s = r + Math.sqrt(discriminant);
2748 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2749 t = r - Math.sqrt(discriminant);
2750 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2751 result[0] = -term1 + s + t;
2752 term1 += (s + t) / 2.0;
2753 result[4] = result[2] = -term1;
2754 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2755 result[3] = term1;
2756 result[5] = -term1;
2757 return;
2758 }
2759
2760 result[5] = result[3] = 0;
2761
2762 if (discriminant === 0) {
2763 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2764 result[0] = -term1 + 2.0 * r13;
2765 result[4] = result[2] = -(r13 + term1);
2766 return;
2767 }
2768
2769 q = -q;
2770 dum1 = q * q * q;
2771 dum1 = Math.acos(r / Math.sqrt(dum1));
2772 r13 = 2.0 * Math.sqrt(q);
2773 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2774 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2775 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2776 return;
2777};
2778var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2779 // Find minimum distance by using the minimum of the distance
2780 // function between the given point and the curve
2781 // This gives the coefficients of the resulting cubic equation
2782 // whose roots tell us where a possible minimum is
2783 // (Coefficients are divided by 4)
2784 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;
2785 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;
2786 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;
2787 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);
2788
2789 var roots = []; // Use the cubic solving algorithm
2790
2791 solveCubic(a, b, c, d, roots);
2792 var zeroThreshold = 0.0000001;
2793 var params = [];
2794
2795 for (var index = 0; index < 6; index += 2) {
2796 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2797 params.push(roots[index]);
2798 }
2799 }
2800
2801 params.push(1.0);
2802 params.push(0.0);
2803 var minDistanceSquared = -1;
2804 var curX, curY, distSquared;
2805
2806 for (var i = 0; i < params.length; i++) {
2807 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2808 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2809 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2810
2811 if (minDistanceSquared >= 0) {
2812 if (distSquared < minDistanceSquared) {
2813 minDistanceSquared = distSquared;
2814 }
2815 } else {
2816 minDistanceSquared = distSquared;
2817 }
2818 }
2819
2820 return minDistanceSquared;
2821};
2822var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2823 var offset = [x - x1, y - y1];
2824 var line = [x2 - x1, y2 - y1];
2825 var lineSq = line[0] * line[0] + line[1] * line[1];
2826 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2827 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2828 var adjSq = dotProduct * dotProduct / lineSq;
2829
2830 if (dotProduct < 0) {
2831 return hypSq;
2832 }
2833
2834 if (adjSq > lineSq) {
2835 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2836 }
2837
2838 return hypSq - adjSq;
2839};
2840var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2841 var x1, y1, x2, y2;
2842 var y3; // Intersect with vertical line through (x, y)
2843
2844 var up = 0; // let down = 0;
2845
2846 for (var i = 0; i < points.length / 2; i++) {
2847 x1 = points[i * 2];
2848 y1 = points[i * 2 + 1];
2849
2850 if (i + 1 < points.length / 2) {
2851 x2 = points[(i + 1) * 2];
2852 y2 = points[(i + 1) * 2 + 1];
2853 } else {
2854 x2 = points[(i + 1 - points.length / 2) * 2];
2855 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2856 }
2857
2858 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2859 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2860
2861 if (y3 > y) {
2862 up++;
2863 } // if( y3 < y ){
2864 // down++;
2865 // }
2866
2867 } else {
2868 continue;
2869 }
2870 }
2871
2872 if (up % 2 === 0) {
2873 return false;
2874 } else {
2875 return true;
2876 }
2877};
2878var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2879 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2880
2881 var angle;
2882
2883 if (direction[0] != null) {
2884 angle = Math.atan(direction[1] / direction[0]);
2885
2886 if (direction[0] < 0) {
2887 angle = angle + Math.PI / 2;
2888 } else {
2889 angle = -angle - Math.PI / 2;
2890 }
2891 } else {
2892 angle = direction;
2893 }
2894
2895 var cos = Math.cos(-angle);
2896 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2897
2898 for (var i = 0; i < transformedPoints.length / 2; i++) {
2899 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2900 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2901 transformedPoints[i * 2] += centerX;
2902 transformedPoints[i * 2 + 1] += centerY;
2903 }
2904
2905 var points;
2906
2907 if (padding > 0) {
2908 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2909 points = joinLines(expandedLineSet);
2910 } else {
2911 points = transformedPoints;
2912 }
2913
2914 return pointInsidePolygonPoints(x, y, points);
2915};
2916var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
2917 var cutPolygonPoints = new Array(basePoints.length);
2918 var halfW = width / 2;
2919 var halfH = height / 2;
2920 var cornerRadius = getRoundPolygonRadius(width, height);
2921 var squaredCornerRadius = cornerRadius * cornerRadius;
2922
2923 for (var i = 0; i < basePoints.length / 4; i++) {
2924 var sourceUv = void 0,
2925 destUv = void 0;
2926
2927 if (i === 0) {
2928 sourceUv = basePoints.length - 2;
2929 } else {
2930 sourceUv = i * 4 - 2;
2931 }
2932
2933 destUv = i * 4 + 2;
2934 var px = centerX + halfW * basePoints[i * 4];
2935 var py = centerY + halfH * basePoints[i * 4 + 1];
2936 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
2937 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
2938 var cp0x = px - offset * basePoints[sourceUv];
2939 var cp0y = py - offset * basePoints[sourceUv + 1];
2940 var cp1x = px + offset * basePoints[destUv];
2941 var cp1y = py + offset * basePoints[destUv + 1];
2942 cutPolygonPoints[i * 4] = cp0x;
2943 cutPolygonPoints[i * 4 + 1] = cp0y;
2944 cutPolygonPoints[i * 4 + 2] = cp1x;
2945 cutPolygonPoints[i * 4 + 3] = cp1y;
2946 var orthx = basePoints[sourceUv + 1];
2947 var orthy = -basePoints[sourceUv];
2948 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
2949
2950 if (cosAlpha < 0) {
2951 orthx *= -1;
2952 orthy *= -1;
2953 }
2954
2955 var cx = cp0x + orthx * cornerRadius;
2956 var cy = cp0y + orthy * cornerRadius;
2957 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
2958
2959 if (squaredDistance <= squaredCornerRadius) {
2960 return true;
2961 }
2962 }
2963
2964 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
2965};
2966var joinLines = function joinLines(lineSet) {
2967 var vertices = new Array(lineSet.length / 2);
2968 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
2969 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
2970
2971 for (var i = 0; i < lineSet.length / 4; i++) {
2972 currentLineStartX = lineSet[i * 4];
2973 currentLineStartY = lineSet[i * 4 + 1];
2974 currentLineEndX = lineSet[i * 4 + 2];
2975 currentLineEndY = lineSet[i * 4 + 3];
2976
2977 if (i < lineSet.length / 4 - 1) {
2978 nextLineStartX = lineSet[(i + 1) * 4];
2979 nextLineStartY = lineSet[(i + 1) * 4 + 1];
2980 nextLineEndX = lineSet[(i + 1) * 4 + 2];
2981 nextLineEndY = lineSet[(i + 1) * 4 + 3];
2982 } else {
2983 nextLineStartX = lineSet[0];
2984 nextLineStartY = lineSet[1];
2985 nextLineEndX = lineSet[2];
2986 nextLineEndY = lineSet[3];
2987 }
2988
2989 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
2990 vertices[i * 2] = intersection[0];
2991 vertices[i * 2 + 1] = intersection[1];
2992 }
2993
2994 return vertices;
2995};
2996var expandPolygon = function expandPolygon(points, pad) {
2997 var expandedLineSet = new Array(points.length * 2);
2998 var currentPointX, currentPointY, nextPointX, nextPointY;
2999
3000 for (var i = 0; i < points.length / 2; i++) {
3001 currentPointX = points[i * 2];
3002 currentPointY = points[i * 2 + 1];
3003
3004 if (i < points.length / 2 - 1) {
3005 nextPointX = points[(i + 1) * 2];
3006 nextPointY = points[(i + 1) * 2 + 1];
3007 } else {
3008 nextPointX = points[0];
3009 nextPointY = points[1];
3010 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3011 // Assume CCW polygon winding
3012
3013
3014 var offsetX = nextPointY - currentPointY;
3015 var offsetY = -(nextPointX - currentPointX); // Normalize
3016
3017 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3018 var normalizedOffsetX = offsetX / offsetLength;
3019 var normalizedOffsetY = offsetY / offsetLength;
3020 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3021 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3022 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3023 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3024 }
3025
3026 return expandedLineSet;
3027};
3028var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3029 var dispX = centerX - x;
3030 var dispY = centerY - y;
3031 dispX /= ellipseWradius;
3032 dispY /= ellipseHradius;
3033 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3034 var newLength = len - 1;
3035
3036 if (newLength < 0) {
3037 return [];
3038 }
3039
3040 var lenProportion = newLength / len;
3041 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3042};
3043var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3044 x -= centerX;
3045 y -= centerY;
3046 x /= width / 2 + padding;
3047 y /= height / 2 + padding;
3048 return x * x + y * y <= 1;
3049}; // Returns intersections of increasing distance from line's start point
3050
3051var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3052 // Calculate d, direction vector of line
3053 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3054
3055 var f = [x1 - centerX, y1 - centerY];
3056 var a = d[0] * d[0] + d[1] * d[1];
3057 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3058 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3059 var discriminant = b * b - 4 * a * c;
3060
3061 if (discriminant < 0) {
3062 return [];
3063 }
3064
3065 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3066 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3067 var tMin = Math.min(t1, t2);
3068 var tMax = Math.max(t1, t2);
3069 var inRangeParams = [];
3070
3071 if (tMin >= 0 && tMin <= 1) {
3072 inRangeParams.push(tMin);
3073 }
3074
3075 if (tMax >= 0 && tMax <= 1) {
3076 inRangeParams.push(tMax);
3077 }
3078
3079 if (inRangeParams.length === 0) {
3080 return [];
3081 }
3082
3083 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3084 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3085
3086 if (inRangeParams.length > 1) {
3087 if (inRangeParams[0] == inRangeParams[1]) {
3088 return [nearIntersectionX, nearIntersectionY];
3089 } else {
3090 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3091 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3092 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3093 }
3094 } else {
3095 return [nearIntersectionX, nearIntersectionY];
3096 }
3097};
3098var midOfThree = function midOfThree(a, b, c) {
3099 if (b <= a && a <= c || c <= a && a <= b) {
3100 return a;
3101 } else if (a <= b && b <= c || c <= b && b <= a) {
3102 return b;
3103 } else {
3104 return c;
3105 }
3106}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3107
3108var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3109 var dx13 = x1 - x3;
3110 var dx21 = x2 - x1;
3111 var dx43 = x4 - x3;
3112 var dy13 = y1 - y3;
3113 var dy21 = y2 - y1;
3114 var dy43 = y4 - y3;
3115 var ua_t = dx43 * dy13 - dy43 * dx13;
3116 var ub_t = dx21 * dy13 - dy21 * dx13;
3117 var u_b = dy43 * dx21 - dx43 * dy21;
3118
3119 if (u_b !== 0) {
3120 var ua = ua_t / u_b;
3121 var ub = ub_t / u_b;
3122 var flptThreshold = 0.001;
3123
3124 var _min = 0 - flptThreshold;
3125
3126 var _max = 1 + flptThreshold;
3127
3128 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3129 return [x1 + ua * dx21, y1 + ua * dy21];
3130 } else {
3131 if (!infiniteLines) {
3132 return [];
3133 } else {
3134 return [x1 + ua * dx21, y1 + ua * dy21];
3135 }
3136 }
3137 } else {
3138 if (ua_t === 0 || ub_t === 0) {
3139 // Parallel, coincident lines. Check if overlap
3140 // Check endpoint of second line
3141 if (midOfThree(x1, x2, x4) === x4) {
3142 return [x4, y4];
3143 } // Check start point of second line
3144
3145
3146 if (midOfThree(x1, x2, x3) === x3) {
3147 return [x3, y3];
3148 } // Endpoint of first line
3149
3150
3151 if (midOfThree(x3, x4, x2) === x2) {
3152 return [x2, y2];
3153 }
3154
3155 return [];
3156 } else {
3157 // Parallel, non-coincident
3158 return [];
3159 }
3160 }
3161}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3162// intersect a node polygon (pts transformed)
3163//
3164// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3165// intersect the points (no transform)
3166
3167var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3168 var intersections = [];
3169 var intersection;
3170 var transformedPoints = new Array(basePoints.length);
3171 var doTransform = true;
3172
3173 if (width == null) {
3174 doTransform = false;
3175 }
3176
3177 var points;
3178
3179 if (doTransform) {
3180 for (var i = 0; i < transformedPoints.length / 2; i++) {
3181 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3182 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3183 }
3184
3185 if (padding > 0) {
3186 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3187 points = joinLines(expandedLineSet);
3188 } else {
3189 points = transformedPoints;
3190 }
3191 } else {
3192 points = basePoints;
3193 }
3194
3195 var currentX, currentY, nextX, nextY;
3196
3197 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3198 currentX = points[_i2 * 2];
3199 currentY = points[_i2 * 2 + 1];
3200
3201 if (_i2 < points.length / 2 - 1) {
3202 nextX = points[(_i2 + 1) * 2];
3203 nextY = points[(_i2 + 1) * 2 + 1];
3204 } else {
3205 nextX = points[0];
3206 nextY = points[1];
3207 }
3208
3209 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3210
3211 if (intersection.length !== 0) {
3212 intersections.push(intersection[0], intersection[1]);
3213 }
3214 }
3215
3216 return intersections;
3217};
3218var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3219 var intersections = [];
3220 var intersection;
3221 var lines = new Array(basePoints.length);
3222 var halfW = width / 2;
3223 var halfH = height / 2;
3224 var cornerRadius = getRoundPolygonRadius(width, height);
3225
3226 for (var i = 0; i < basePoints.length / 4; i++) {
3227 var sourceUv = void 0,
3228 destUv = void 0;
3229
3230 if (i === 0) {
3231 sourceUv = basePoints.length - 2;
3232 } else {
3233 sourceUv = i * 4 - 2;
3234 }
3235
3236 destUv = i * 4 + 2;
3237 var px = centerX + halfW * basePoints[i * 4];
3238 var py = centerY + halfH * basePoints[i * 4 + 1];
3239 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3240 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3241 var cp0x = px - offset * basePoints[sourceUv];
3242 var cp0y = py - offset * basePoints[sourceUv + 1];
3243 var cp1x = px + offset * basePoints[destUv];
3244 var cp1y = py + offset * basePoints[destUv + 1];
3245
3246 if (i === 0) {
3247 lines[basePoints.length - 2] = cp0x;
3248 lines[basePoints.length - 1] = cp0y;
3249 } else {
3250 lines[i * 4 - 2] = cp0x;
3251 lines[i * 4 - 1] = cp0y;
3252 }
3253
3254 lines[i * 4] = cp1x;
3255 lines[i * 4 + 1] = cp1y;
3256 var orthx = basePoints[sourceUv + 1];
3257 var orthy = -basePoints[sourceUv];
3258 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3259
3260 if (cosAlpha < 0) {
3261 orthx *= -1;
3262 orthy *= -1;
3263 }
3264
3265 var cx = cp0x + orthx * cornerRadius;
3266 var cy = cp0y + orthy * cornerRadius;
3267 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3268
3269 if (intersection.length !== 0) {
3270 intersections.push(intersection[0], intersection[1]);
3271 }
3272 }
3273
3274 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3275 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3276
3277 if (intersection.length !== 0) {
3278 intersections.push(intersection[0], intersection[1]);
3279 }
3280 }
3281
3282 if (intersections.length > 2) {
3283 var lowestIntersection = [intersections[0], intersections[1]];
3284 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3285
3286 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3287 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3288
3289 if (squaredDistance <= lowestSquaredDistance) {
3290 lowestIntersection[0] = intersections[_i4 * 2];
3291 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3292 lowestSquaredDistance = squaredDistance;
3293 }
3294 }
3295
3296 return lowestIntersection;
3297 }
3298
3299 return intersections;
3300};
3301var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3302 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3303 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3304 var lenRatio = (length - amount) / length;
3305
3306 if (lenRatio < 0) {
3307 lenRatio = 0.00001;
3308 }
3309
3310 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3311};
3312var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3313 var points = generateUnitNgonPoints(sides, rotationRadians);
3314 points = fitPolygonToSquare(points);
3315 return points;
3316};
3317var fitPolygonToSquare = function fitPolygonToSquare(points) {
3318 var x, y;
3319 var sides = points.length / 2;
3320 var minX = Infinity,
3321 minY = Infinity,
3322 maxX = -Infinity,
3323 maxY = -Infinity;
3324
3325 for (var i = 0; i < sides; i++) {
3326 x = points[2 * i];
3327 y = points[2 * i + 1];
3328 minX = Math.min(minX, x);
3329 maxX = Math.max(maxX, x);
3330 minY = Math.min(minY, y);
3331 maxY = Math.max(maxY, y);
3332 } // stretch factors
3333
3334
3335 var sx = 2 / (maxX - minX);
3336 var sy = 2 / (maxY - minY);
3337
3338 for (var _i5 = 0; _i5 < sides; _i5++) {
3339 x = points[2 * _i5] = points[2 * _i5] * sx;
3340 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3341 minX = Math.min(minX, x);
3342 maxX = Math.max(maxX, x);
3343 minY = Math.min(minY, y);
3344 maxY = Math.max(maxY, y);
3345 }
3346
3347 if (minY < -1) {
3348 for (var _i6 = 0; _i6 < sides; _i6++) {
3349 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3350 }
3351 }
3352
3353 return points;
3354};
3355var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3356 var increment = 1.0 / sides * 2 * Math.PI;
3357 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3358 startAngle += rotationRadians;
3359 var points = new Array(sides * 2);
3360 var currentAngle;
3361
3362 for (var i = 0; i < sides; i++) {
3363 currentAngle = i * increment + startAngle;
3364 points[2 * i] = Math.cos(currentAngle); // x
3365
3366 points[2 * i + 1] = Math.sin(-currentAngle); // y
3367 }
3368
3369 return points;
3370}; // Set the default radius, unless half of width or height is smaller than default
3371
3372var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3373 return Math.min(width / 4, height / 4, 8);
3374}; // Set the default radius
3375
3376var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3377 return Math.min(width / 10, height / 10, 8);
3378};
3379var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3380 return 8;
3381};
3382var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3383 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3384}; // get curve width, height, and control point position offsets as a percentage of node height / width
3385
3386var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3387 return {
3388 heightOffset: Math.min(15, 0.05 * height),
3389 widthOffset: Math.min(100, 0.25 * width),
3390 ctrlPtOffsetPct: 0.05
3391 };
3392};
3393
3394var pageRankDefaults = defaults({
3395 dampingFactor: 0.8,
3396 precision: 0.000001,
3397 iterations: 200,
3398 weight: function weight(edge) {
3399 return 1;
3400 }
3401});
3402var elesfn$7 = {
3403 pageRank: function pageRank(options) {
3404 var _pageRankDefaults = pageRankDefaults(options),
3405 dampingFactor = _pageRankDefaults.dampingFactor,
3406 precision = _pageRankDefaults.precision,
3407 iterations = _pageRankDefaults.iterations,
3408 weight = _pageRankDefaults.weight;
3409
3410 var cy = this._private.cy;
3411
3412 var _this$byGroup = this.byGroup(),
3413 nodes = _this$byGroup.nodes,
3414 edges = _this$byGroup.edges;
3415
3416 var numNodes = nodes.length;
3417 var numNodesSqd = numNodes * numNodes;
3418 var numEdges = edges.length; // Construct transposed adjacency matrix
3419 // First lets have a zeroed matrix of the right size
3420 // We'll also keep track of the sum of each column
3421
3422 var matrix = new Array(numNodesSqd);
3423 var columnSum = new Array(numNodes);
3424 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3425
3426 for (var i = 0; i < numNodes; i++) {
3427 for (var j = 0; j < numNodes; j++) {
3428 var n = i * numNodes + j;
3429 matrix[n] = 0;
3430 }
3431
3432 columnSum[i] = 0;
3433 } // Now, process edges
3434
3435
3436 for (var _i = 0; _i < numEdges; _i++) {
3437 var edge = edges[_i];
3438 var srcId = edge.data('source');
3439 var tgtId = edge.data('target'); // Don't include loops in the matrix
3440
3441 if (srcId === tgtId) {
3442 continue;
3443 }
3444
3445 var s = nodes.indexOfId(srcId);
3446 var t = nodes.indexOfId(tgtId);
3447 var w = weight(edge);
3448
3449 var _n = t * numNodes + s; // Update matrix
3450
3451
3452 matrix[_n] += w; // Update column sum
3453
3454 columnSum[s] += w;
3455 } // Add additional probability based on damping factor
3456 // Also, take into account columns that have sum = 0
3457
3458
3459 var p = 1.0 / numNodes + additionalProb; // Shorthand
3460 // Traverse matrix, column by column
3461
3462 for (var _j = 0; _j < numNodes; _j++) {
3463 if (columnSum[_j] === 0) {
3464 // No 'links' out from node jth, assume equal probability for each possible node
3465 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3466 var _n2 = _i2 * numNodes + _j;
3467
3468 matrix[_n2] = p;
3469 }
3470 } else {
3471 // Node jth has outgoing link, compute normalized probabilities
3472 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3473 var _n3 = _i3 * numNodes + _j;
3474
3475 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3476 }
3477 }
3478 } // Compute dominant eigenvector using power method
3479
3480
3481 var eigenvector = new Array(numNodes);
3482 var temp = new Array(numNodes);
3483 var previous; // Start with a vector of all 1's
3484 // Also, initialize a null vector which will be used as shorthand
3485
3486 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3487 eigenvector[_i4] = 1;
3488 }
3489
3490 for (var iter = 0; iter < iterations; iter++) {
3491 // Temp array with all 0's
3492 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3493 temp[_i5] = 0;
3494 } // Multiply matrix with previous result
3495
3496
3497 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3498 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3499 var _n4 = _i6 * numNodes + _j2;
3500
3501 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3502 }
3503 }
3504
3505 inPlaceSumNormalize(temp);
3506 previous = eigenvector;
3507 eigenvector = temp;
3508 temp = previous;
3509 var diff = 0; // Compute difference (squared module) of both vectors
3510
3511 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3512 var delta = previous[_i7] - eigenvector[_i7];
3513 diff += delta * delta;
3514 } // If difference is less than the desired threshold, stop iterating
3515
3516
3517 if (diff < precision) {
3518 break;
3519 }
3520 } // Construct result
3521
3522
3523 var res = {
3524 rank: function rank(node) {
3525 node = cy.collection(node)[0];
3526 return eigenvector[nodes.indexOf(node)];
3527 }
3528 };
3529 return res;
3530 } // pageRank
3531
3532}; // elesfn
3533
3534var defaults$1 = defaults({
3535 root: null,
3536 weight: function weight(edge) {
3537 return 1;
3538 },
3539 directed: false,
3540 alpha: 0
3541});
3542var elesfn$8 = {
3543 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3544 options = defaults$1(options);
3545 var cy = this.cy();
3546 var nodes = this.nodes();
3547 var numNodes = nodes.length;
3548
3549 if (!options.directed) {
3550 var degrees = {};
3551 var maxDegree = 0;
3552
3553 for (var i = 0; i < numNodes; i++) {
3554 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3555
3556 options.root = node;
3557 var currDegree = this.degreeCentrality(options);
3558
3559 if (maxDegree < currDegree.degree) {
3560 maxDegree = currDegree.degree;
3561 }
3562
3563 degrees[node.id()] = currDegree.degree;
3564 }
3565
3566 return {
3567 degree: function degree(node) {
3568 if (maxDegree === 0) {
3569 return 0;
3570 }
3571
3572 if (string(node)) {
3573 // from is a selector string
3574 node = cy.filter(node);
3575 }
3576
3577 return degrees[node.id()] / maxDegree;
3578 }
3579 };
3580 } else {
3581 var indegrees = {};
3582 var outdegrees = {};
3583 var maxIndegree = 0;
3584 var maxOutdegree = 0;
3585
3586 for (var _i = 0; _i < numNodes; _i++) {
3587 var _node = nodes[_i];
3588
3589 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3590
3591
3592 options.root = _node;
3593
3594 var _currDegree = this.degreeCentrality(options);
3595
3596 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3597 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3598 indegrees[id] = _currDegree.indegree;
3599 outdegrees[id] = _currDegree.outdegree;
3600 }
3601
3602 return {
3603 indegree: function indegree(node) {
3604 if (maxIndegree == 0) {
3605 return 0;
3606 }
3607
3608 if (string(node)) {
3609 // from is a selector string
3610 node = cy.filter(node);
3611 }
3612
3613 return indegrees[node.id()] / maxIndegree;
3614 },
3615 outdegree: function outdegree(node) {
3616 if (maxOutdegree === 0) {
3617 return 0;
3618 }
3619
3620 if (string(node)) {
3621 // from is a selector string
3622 node = cy.filter(node);
3623 }
3624
3625 return outdegrees[node.id()] / maxOutdegree;
3626 }
3627 };
3628 }
3629 },
3630 // degreeCentralityNormalized
3631 // Implemented from the algorithm in Opsahl's paper
3632 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3633 // check the heading 2 "Degree"
3634 degreeCentrality: function degreeCentrality(options) {
3635 options = defaults$1(options);
3636 var cy = this.cy();
3637 var callingEles = this;
3638 var _options = options,
3639 root = _options.root,
3640 weight = _options.weight,
3641 directed = _options.directed,
3642 alpha = _options.alpha;
3643 root = cy.collection(root)[0];
3644
3645 if (!directed) {
3646 var connEdges = root.connectedEdges().intersection(callingEles);
3647 var k = connEdges.length;
3648 var s = 0; // Now, sum edge weights
3649
3650 for (var i = 0; i < connEdges.length; i++) {
3651 s += weight(connEdges[i]);
3652 }
3653
3654 return {
3655 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3656 };
3657 } else {
3658 var edges = root.connectedEdges();
3659 var incoming = edges.filter(function (edge) {
3660 return edge.target().same(root) && callingEles.has(edge);
3661 });
3662 var outgoing = edges.filter(function (edge) {
3663 return edge.source().same(root) && callingEles.has(edge);
3664 });
3665 var k_in = incoming.length;
3666 var k_out = outgoing.length;
3667 var s_in = 0;
3668 var s_out = 0; // Now, sum incoming edge weights
3669
3670 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3671 s_in += weight(incoming[_i2]);
3672 } // Now, sum outgoing edge weights
3673
3674
3675 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3676 s_out += weight(outgoing[_i3]);
3677 }
3678
3679 return {
3680 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3681 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3682 };
3683 }
3684 } // degreeCentrality
3685
3686}; // elesfn
3687// nice, short mathemathical alias
3688
3689elesfn$8.dc = elesfn$8.degreeCentrality;
3690elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3691
3692var defaults$2 = defaults({
3693 harmonic: true,
3694 weight: function weight() {
3695 return 1;
3696 },
3697 directed: false,
3698 root: null
3699});
3700var elesfn$9 = {
3701 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3702 var _defaults = defaults$2(options),
3703 harmonic = _defaults.harmonic,
3704 weight = _defaults.weight,
3705 directed = _defaults.directed;
3706
3707 var cy = this.cy();
3708 var closenesses = {};
3709 var maxCloseness = 0;
3710 var nodes = this.nodes();
3711 var fw = this.floydWarshall({
3712 weight: weight,
3713 directed: directed
3714 }); // Compute closeness for every node and find the maximum closeness
3715
3716 for (var i = 0; i < nodes.length; i++) {
3717 var currCloseness = 0;
3718 var node_i = nodes[i];
3719
3720 for (var j = 0; j < nodes.length; j++) {
3721 if (i !== j) {
3722 var d = fw.distance(node_i, nodes[j]);
3723
3724 if (harmonic) {
3725 currCloseness += 1 / d;
3726 } else {
3727 currCloseness += d;
3728 }
3729 }
3730 }
3731
3732 if (!harmonic) {
3733 currCloseness = 1 / currCloseness;
3734 }
3735
3736 if (maxCloseness < currCloseness) {
3737 maxCloseness = currCloseness;
3738 }
3739
3740 closenesses[node_i.id()] = currCloseness;
3741 }
3742
3743 return {
3744 closeness: function closeness(node) {
3745 if (maxCloseness == 0) {
3746 return 0;
3747 }
3748
3749 if (string(node)) {
3750 // from is a selector string
3751 node = cy.filter(node)[0].id();
3752 } else {
3753 // from is a node
3754 node = node.id();
3755 }
3756
3757 return closenesses[node] / maxCloseness;
3758 }
3759 };
3760 },
3761 // Implemented from pseudocode from wikipedia
3762 closenessCentrality: function closenessCentrality(options) {
3763 var _defaults2 = defaults$2(options),
3764 root = _defaults2.root,
3765 weight = _defaults2.weight,
3766 directed = _defaults2.directed,
3767 harmonic = _defaults2.harmonic;
3768
3769 root = this.filter(root)[0]; // we need distance from this node to every other node
3770
3771 var dijkstra = this.dijkstra({
3772 root: root,
3773 weight: weight,
3774 directed: directed
3775 });
3776 var totalDistance = 0;
3777 var nodes = this.nodes();
3778
3779 for (var i = 0; i < nodes.length; i++) {
3780 var n = nodes[i];
3781
3782 if (!n.same(root)) {
3783 var d = dijkstra.distanceTo(n);
3784
3785 if (harmonic) {
3786 totalDistance += 1 / d;
3787 } else {
3788 totalDistance += d;
3789 }
3790 }
3791 }
3792
3793 return harmonic ? totalDistance : 1 / totalDistance;
3794 } // closenessCentrality
3795
3796}; // elesfn
3797// nice, short mathemathical alias
3798
3799elesfn$9.cc = elesfn$9.closenessCentrality;
3800elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3801
3802var defaults$3 = defaults({
3803 weight: null,
3804 directed: false
3805});
3806var elesfn$a = {
3807 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3808 betweennessCentrality: function betweennessCentrality(options) {
3809 var _defaults = defaults$3(options),
3810 directed = _defaults.directed,
3811 weight = _defaults.weight;
3812
3813 var weighted = weight != null;
3814 var cy = this.cy(); // starting
3815
3816 var V = this.nodes();
3817 var A = {};
3818 var _C = {};
3819 var max = 0;
3820 var C = {
3821 set: function set(key, val) {
3822 _C[key] = val;
3823
3824 if (val > max) {
3825 max = val;
3826 }
3827 },
3828 get: function get(key) {
3829 return _C[key];
3830 }
3831 }; // A contains the neighborhoods of every node
3832
3833 for (var i = 0; i < V.length; i++) {
3834 var v = V[i];
3835 var vid = v.id();
3836
3837 if (directed) {
3838 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3839 } else {
3840 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3841 }
3842
3843 C.set(vid, 0);
3844 }
3845
3846 var _loop = function _loop(s) {
3847 var sid = V[s].id();
3848 var S = []; // stack
3849
3850 var P = {};
3851 var g = {};
3852 var d = {};
3853 var Q = new Heap(function (a, b) {
3854 return d[a] - d[b];
3855 }); // queue
3856 // init dictionaries
3857
3858 for (var _i = 0; _i < V.length; _i++) {
3859 var _vid = V[_i].id();
3860
3861 P[_vid] = [];
3862 g[_vid] = 0;
3863 d[_vid] = Infinity;
3864 }
3865
3866 g[sid] = 1; // sigma
3867
3868 d[sid] = 0; // distance to s
3869
3870 Q.push(sid);
3871
3872 while (!Q.empty()) {
3873 var _v = Q.pop();
3874
3875 S.push(_v);
3876
3877 if (weighted) {
3878 for (var j = 0; j < A[_v].length; j++) {
3879 var w = A[_v][j];
3880 var vEle = cy.getElementById(_v);
3881 var edge = void 0;
3882
3883 if (vEle.edgesTo(w).length > 0) {
3884 edge = vEle.edgesTo(w)[0];
3885 } else {
3886 edge = w.edgesTo(vEle)[0];
3887 }
3888
3889 var edgeWeight = weight(edge);
3890 w = w.id();
3891
3892 if (d[w] > d[_v] + edgeWeight) {
3893 d[w] = d[_v] + edgeWeight;
3894
3895 if (Q.nodes.indexOf(w) < 0) {
3896 //if w is not in Q
3897 Q.push(w);
3898 } else {
3899 // update position if w is in Q
3900 Q.updateItem(w);
3901 }
3902
3903 g[w] = 0;
3904 P[w] = [];
3905 }
3906
3907 if (d[w] == d[_v] + edgeWeight) {
3908 g[w] = g[w] + g[_v];
3909 P[w].push(_v);
3910 }
3911 }
3912 } else {
3913 for (var _j = 0; _j < A[_v].length; _j++) {
3914 var _w = A[_v][_j].id();
3915
3916 if (d[_w] == Infinity) {
3917 Q.push(_w);
3918 d[_w] = d[_v] + 1;
3919 }
3920
3921 if (d[_w] == d[_v] + 1) {
3922 g[_w] = g[_w] + g[_v];
3923
3924 P[_w].push(_v);
3925 }
3926 }
3927 }
3928 }
3929
3930 var e = {};
3931
3932 for (var _i2 = 0; _i2 < V.length; _i2++) {
3933 e[V[_i2].id()] = 0;
3934 }
3935
3936 while (S.length > 0) {
3937 var _w2 = S.pop();
3938
3939 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
3940 var _v2 = P[_w2][_j2];
3941 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
3942
3943 if (_w2 != V[s].id()) {
3944 C.set(_w2, C.get(_w2) + e[_w2]);
3945 }
3946 }
3947 }
3948 };
3949
3950 for (var s = 0; s < V.length; s++) {
3951 _loop(s);
3952 }
3953
3954 var ret = {
3955 betweenness: function betweenness(node) {
3956 var id = cy.collection(node).id();
3957 return C.get(id);
3958 },
3959 betweennessNormalized: function betweennessNormalized(node) {
3960 if (max == 0) {
3961 return 0;
3962 }
3963
3964 var id = cy.collection(node).id();
3965 return C.get(id) / max;
3966 }
3967 }; // alias
3968
3969 ret.betweennessNormalised = ret.betweennessNormalized;
3970 return ret;
3971 } // betweennessCentrality
3972
3973}; // elesfn
3974// nice, short mathemathical alias
3975
3976elesfn$a.bc = elesfn$a.betweennessCentrality;
3977
3978// Implemented by Zoe Xi @zoexi for GSOC 2016
3979/* eslint-disable no-unused-vars */
3980
3981var defaults$4 = defaults({
3982 expandFactor: 2,
3983 // affects time of computation and cluster granularity to some extent: M * M
3984 inflateFactor: 2,
3985 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
3986 multFactor: 1,
3987 // optional self loops for each node. Use a neutral value to improve cluster computations.
3988 maxIterations: 20,
3989 // maximum number of iterations of the MCL algorithm in a single run
3990 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
3991 function (edge) {
3992 return 1;
3993 }]
3994});
3995/* eslint-enable */
3996
3997var setOptions = function setOptions(options) {
3998 return defaults$4(options);
3999};
4000/* eslint-enable */
4001
4002
4003var getSimilarity = function getSimilarity(edge, attributes) {
4004 var total = 0;
4005
4006 for (var i = 0; i < attributes.length; i++) {
4007 total += attributes[i](edge);
4008 }
4009
4010 return total;
4011};
4012
4013var addLoops = function addLoops(M, n, val) {
4014 for (var i = 0; i < n; i++) {
4015 M[i * n + i] = val;
4016 }
4017};
4018
4019var normalize = function normalize(M, n) {
4020 var sum;
4021
4022 for (var col = 0; col < n; col++) {
4023 sum = 0;
4024
4025 for (var row = 0; row < n; row++) {
4026 sum += M[row * n + col];
4027 }
4028
4029 for (var _row = 0; _row < n; _row++) {
4030 M[_row * n + col] = M[_row * n + col] / sum;
4031 }
4032 }
4033}; // TODO: blocked matrix multiplication?
4034
4035
4036var mmult = function mmult(A, B, n) {
4037 var C = new Array(n * n);
4038
4039 for (var i = 0; i < n; i++) {
4040 for (var j = 0; j < n; j++) {
4041 C[i * n + j] = 0;
4042 }
4043
4044 for (var k = 0; k < n; k++) {
4045 for (var _j = 0; _j < n; _j++) {
4046 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4047 }
4048 }
4049 }
4050
4051 return C;
4052};
4053
4054var expand = function expand(M, n, expandFactor
4055/** power **/
4056) {
4057 var _M = M.slice(0);
4058
4059 for (var p = 1; p < expandFactor; p++) {
4060 M = mmult(M, _M, n);
4061 }
4062
4063 return M;
4064};
4065
4066var inflate = function inflate(M, n, inflateFactor
4067/** r **/
4068) {
4069 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4070
4071
4072 for (var i = 0; i < n * n; i++) {
4073 _M[i] = Math.pow(M[i], inflateFactor);
4074 }
4075
4076 normalize(_M, n);
4077 return _M;
4078};
4079
4080var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4081 // Check that both matrices have the same elements (i,j)
4082 for (var i = 0; i < n2; i++) {
4083 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4084
4085 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4086
4087 if (v1 !== v2) {
4088 return false;
4089 }
4090 }
4091
4092 return true;
4093};
4094
4095var assign = function assign(M, n, nodes, cy) {
4096 var clusters = [];
4097
4098 for (var i = 0; i < n; i++) {
4099 var cluster = [];
4100
4101 for (var j = 0; j < n; j++) {
4102 // Row-wise attractors and elements that they attract belong in same cluster
4103 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4104 cluster.push(nodes[j]);
4105 }
4106 }
4107
4108 if (cluster.length !== 0) {
4109 clusters.push(cy.collection(cluster));
4110 }
4111 }
4112
4113 return clusters;
4114};
4115
4116var isDuplicate = function isDuplicate(c1, c2) {
4117 for (var i = 0; i < c1.length; i++) {
4118 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4119 return false;
4120 }
4121 }
4122
4123 return true;
4124};
4125
4126var removeDuplicates = function removeDuplicates(clusters) {
4127 for (var i = 0; i < clusters.length; i++) {
4128 for (var j = 0; j < clusters.length; j++) {
4129 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4130 clusters.splice(j, 1);
4131 }
4132 }
4133 }
4134
4135 return clusters;
4136};
4137
4138var markovClustering = function markovClustering(options) {
4139 var nodes = this.nodes();
4140 var edges = this.edges();
4141 var cy = this.cy(); // Set parameters of algorithm:
4142
4143 var opts = setOptions(options); // Map each node to its position in node array
4144
4145 var id2position = {};
4146
4147 for (var i = 0; i < nodes.length; i++) {
4148 id2position[nodes[i].id()] = i;
4149 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4150
4151
4152 var n = nodes.length,
4153 n2 = n * n;
4154
4155 var M = new Array(n2),
4156 _M;
4157
4158 for (var _i = 0; _i < n2; _i++) {
4159 M[_i] = 0;
4160 }
4161
4162 for (var e = 0; e < edges.length; e++) {
4163 var edge = edges[e];
4164 var _i2 = id2position[edge.source().id()];
4165 var j = id2position[edge.target().id()];
4166 var sim = getSimilarity(edge, opts.attributes);
4167 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4168
4169 M[j * n + _i2] += sim;
4170 } // Begin Markov cluster algorithm
4171 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4172
4173
4174 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4175
4176 normalize(M, n);
4177 var isStillMoving = true;
4178 var iterations = 0;
4179
4180 while (isStillMoving && iterations < opts.maxIterations) {
4181 isStillMoving = false; // Step 3:
4182
4183 _M = expand(M, n, opts.expandFactor); // Step 4:
4184
4185 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4186
4187 if (!hasConverged(M, _M, n2, 4)) {
4188 isStillMoving = true;
4189 }
4190
4191 iterations++;
4192 } // Build clusters from matrix
4193
4194
4195 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4196
4197 clusters = removeDuplicates(clusters);
4198 return clusters;
4199};
4200
4201var markovClustering$1 = {
4202 markovClustering: markovClustering,
4203 mcl: markovClustering
4204};
4205
4206// Common distance metrics for clustering algorithms
4207
4208var identity = function identity(x) {
4209 return x;
4210};
4211
4212var absDiff = function absDiff(p, q) {
4213 return Math.abs(q - p);
4214};
4215
4216var addAbsDiff = function addAbsDiff(total, p, q) {
4217 return total + absDiff(p, q);
4218};
4219
4220var addSquaredDiff = function addSquaredDiff(total, p, q) {
4221 return total + Math.pow(q - p, 2);
4222};
4223
4224var sqrt = function sqrt(x) {
4225 return Math.sqrt(x);
4226};
4227
4228var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4229 return Math.max(currentMax, absDiff(p, q));
4230};
4231
4232var getDistance = function getDistance(length, getP, getQ, init, visit) {
4233 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4234 var ret = init;
4235 var p, q;
4236
4237 for (var dim = 0; dim < length; dim++) {
4238 p = getP(dim);
4239 q = getQ(dim);
4240 ret = visit(ret, p, q);
4241 }
4242
4243 return post(ret);
4244};
4245
4246var distances = {
4247 euclidean: function euclidean(length, getP, getQ) {
4248 if (length >= 2) {
4249 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4250 } else {
4251 // for single attr case, more efficient to avoid sqrt
4252 return getDistance(length, getP, getQ, 0, addAbsDiff);
4253 }
4254 },
4255 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4256 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4257 },
4258 manhattan: function manhattan(length, getP, getQ) {
4259 return getDistance(length, getP, getQ, 0, addAbsDiff);
4260 },
4261 max: function max(length, getP, getQ) {
4262 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4263 }
4264}; // in case the user accidentally doesn't use camel case
4265
4266distances['squared-euclidean'] = distances['squaredEuclidean'];
4267distances['squaredeuclidean'] = distances['squaredEuclidean'];
4268function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4269 var impl;
4270
4271 if (fn(method)) {
4272 impl = method;
4273 } else {
4274 impl = distances[method] || distances.euclidean;
4275 }
4276
4277 if (length === 0 && fn(method)) {
4278 return impl(nodeP, nodeQ);
4279 } else {
4280 return impl(length, getP, getQ, nodeP, nodeQ);
4281 }
4282}
4283
4284var defaults$5 = defaults({
4285 k: 2,
4286 m: 2,
4287 sensitivityThreshold: 0.0001,
4288 distance: 'euclidean',
4289 maxIterations: 10,
4290 attributes: [],
4291 testMode: false,
4292 testCentroids: null
4293});
4294
4295var setOptions$1 = function setOptions(options) {
4296 return defaults$5(options);
4297};
4298/* eslint-enable */
4299
4300
4301var getDist = function getDist(type, node, centroid, attributes, mode) {
4302 var noNodeP = mode !== 'kMedoids';
4303 var getP = noNodeP ? function (i) {
4304 return centroid[i];
4305 } : function (i) {
4306 return attributes[i](centroid);
4307 };
4308
4309 var getQ = function getQ(i) {
4310 return attributes[i](node);
4311 };
4312
4313 var nodeP = centroid;
4314 var nodeQ = node;
4315 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4316};
4317
4318var randomCentroids = function randomCentroids(nodes, k, attributes) {
4319 var ndim = attributes.length;
4320 var min = new Array(ndim);
4321 var max = new Array(ndim);
4322 var centroids = new Array(k);
4323 var centroid = null; // Find min, max values for each attribute dimension
4324
4325 for (var i = 0; i < ndim; i++) {
4326 min[i] = nodes.min(attributes[i]).value;
4327 max[i] = nodes.max(attributes[i]).value;
4328 } // Build k centroids, each represented as an n-dim feature vector
4329
4330
4331 for (var c = 0; c < k; c++) {
4332 centroid = [];
4333
4334 for (var _i = 0; _i < ndim; _i++) {
4335 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4336 }
4337
4338 centroids[c] = centroid;
4339 }
4340
4341 return centroids;
4342};
4343
4344var classify = function classify(node, centroids, distance, attributes, type) {
4345 var min = Infinity;
4346 var index = 0;
4347
4348 for (var i = 0; i < centroids.length; i++) {
4349 var dist = getDist(distance, node, centroids[i], attributes, type);
4350
4351 if (dist < min) {
4352 min = dist;
4353 index = i;
4354 }
4355 }
4356
4357 return index;
4358};
4359
4360var buildCluster = function buildCluster(centroid, nodes, assignment) {
4361 var cluster = [];
4362 var node = null;
4363
4364 for (var n = 0; n < nodes.length; n++) {
4365 node = nodes[n];
4366
4367 if (assignment[node.id()] === centroid) {
4368 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4369 cluster.push(node);
4370 }
4371 }
4372
4373 return cluster;
4374};
4375
4376var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4377 return Math.abs(v2 - v1) <= sensitivityThreshold;
4378};
4379
4380var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4381 for (var i = 0; i < v1.length; i++) {
4382 for (var j = 0; j < v1[i].length; j++) {
4383 var diff = Math.abs(v1[i][j] - v2[i][j]);
4384
4385 if (diff > sensitivityThreshold) {
4386 return false;
4387 }
4388 }
4389 }
4390
4391 return true;
4392};
4393
4394var seenBefore = function seenBefore(node, medoids, n) {
4395 for (var i = 0; i < n; i++) {
4396 if (node === medoids[i]) return true;
4397 }
4398
4399 return false;
4400};
4401
4402var randomMedoids = function randomMedoids(nodes, k) {
4403 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4404 // so we need to check to see if we've already seen or chose this node before.
4405
4406 if (nodes.length < 50) {
4407 // Randomly select k medoids from the n nodes
4408 for (var i = 0; i < k; i++) {
4409 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).
4410 // Instead choose a different random node.
4411
4412 while (seenBefore(node, medoids, i)) {
4413 node = nodes[Math.floor(Math.random() * nodes.length)];
4414 }
4415
4416 medoids[i] = node;
4417 }
4418 } else {
4419 // Relatively large data set, so pretty safe to not check and just select random nodes
4420 for (var _i2 = 0; _i2 < k; _i2++) {
4421 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4422 }
4423 }
4424
4425 return medoids;
4426};
4427
4428var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4429 var cost = 0;
4430
4431 for (var n = 0; n < cluster.length; n++) {
4432 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4433 }
4434
4435 return cost;
4436};
4437
4438var kMeans = function kMeans(options) {
4439 var cy = this.cy();
4440 var nodes = this.nodes();
4441 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4442
4443 var opts = setOptions$1(options); // Begin k-means algorithm
4444
4445 var clusters = new Array(opts.k);
4446 var assignment = {};
4447 var centroids; // Step 1: Initialize centroid positions
4448
4449 if (opts.testMode) {
4450 if (typeof opts.testCentroids === 'number') {
4451 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4452 } else if (_typeof(opts.testCentroids) === 'object') {
4453 centroids = opts.testCentroids;
4454 } else {
4455 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4456 }
4457 } else {
4458 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4459 }
4460
4461 var isStillMoving = true;
4462 var iterations = 0;
4463
4464 while (isStillMoving && iterations < opts.maxIterations) {
4465 // Step 2: Assign nodes to the nearest centroid
4466 for (var n = 0; n < nodes.length; n++) {
4467 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4468
4469 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4470 } // Step 3: For each of the k clusters, update its centroid
4471
4472
4473 isStillMoving = false;
4474
4475 for (var c = 0; c < opts.k; c++) {
4476 // Get all nodes that belong to this cluster
4477 var cluster = buildCluster(c, nodes, assignment);
4478
4479 if (cluster.length === 0) {
4480 // If cluster is empty, break out early & move to next cluster
4481 continue;
4482 } // Update centroids by calculating avg of all nodes within the cluster.
4483
4484
4485 var ndim = opts.attributes.length;
4486 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4487
4488 var newCentroid = new Array(ndim);
4489 var sum = new Array(ndim);
4490
4491 for (var d = 0; d < ndim; d++) {
4492 sum[d] = 0.0;
4493
4494 for (var i = 0; i < cluster.length; i++) {
4495 node = cluster[i];
4496 sum[d] += opts.attributes[d](node);
4497 }
4498
4499 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4500
4501 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4502 isStillMoving = true;
4503 }
4504 }
4505
4506 centroids[c] = newCentroid;
4507 clusters[c] = cy.collection(cluster);
4508 }
4509
4510 iterations++;
4511 }
4512
4513 return clusters;
4514};
4515
4516var kMedoids = function kMedoids(options) {
4517 var cy = this.cy();
4518 var nodes = this.nodes();
4519 var node = null;
4520 var opts = setOptions$1(options); // Begin k-medoids algorithm
4521
4522 var clusters = new Array(opts.k);
4523 var medoids;
4524 var assignment = {};
4525 var curCost;
4526 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4527 // Step 1: Initialize k medoids
4528
4529 if (opts.testMode) {
4530 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4531 medoids = opts.testCentroids;
4532 } else {
4533 medoids = randomMedoids(nodes, opts.k);
4534 }
4535 } else {
4536 medoids = randomMedoids(nodes, opts.k);
4537 }
4538
4539 var isStillMoving = true;
4540 var iterations = 0;
4541
4542 while (isStillMoving && iterations < opts.maxIterations) {
4543 // Step 2: Assign nodes to the nearest medoid
4544 for (var n = 0; n < nodes.length; n++) {
4545 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4546
4547 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4548 }
4549
4550 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4551 // select the node with the lowest configuration cost as new medoid.
4552
4553 for (var m = 0; m < medoids.length; m++) {
4554 // Get all nodes that belong to this medoid
4555 var cluster = buildCluster(m, nodes, assignment);
4556
4557 if (cluster.length === 0) {
4558 // If cluster is empty, break out early & move to next cluster
4559 continue;
4560 }
4561
4562 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4563 // Select different medoid if its configuration has the lowest cost
4564
4565 for (var _n = 0; _n < cluster.length; _n++) {
4566 curCost = findCost(cluster[_n], cluster, opts.attributes);
4567
4568 if (curCost < minCosts[m]) {
4569 minCosts[m] = curCost;
4570 medoids[m] = cluster[_n];
4571 isStillMoving = true;
4572 }
4573 }
4574
4575 clusters[m] = cy.collection(cluster);
4576 }
4577
4578 iterations++;
4579 }
4580
4581 return clusters;
4582};
4583
4584var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4585 var numerator, denominator;
4586
4587 for (var n = 0; n < nodes.length; n++) {
4588 for (var c = 0; c < centroids.length; c++) {
4589 weight[n][c] = Math.pow(U[n][c], opts.m);
4590 }
4591 }
4592
4593 for (var _c = 0; _c < centroids.length; _c++) {
4594 for (var dim = 0; dim < opts.attributes.length; dim++) {
4595 numerator = 0;
4596 denominator = 0;
4597
4598 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4599 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4600 denominator += weight[_n2][_c];
4601 }
4602
4603 centroids[_c][dim] = numerator / denominator;
4604 }
4605 }
4606};
4607
4608var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4609 // Save previous step
4610 for (var i = 0; i < U.length; i++) {
4611 _U[i] = U[i].slice();
4612 }
4613
4614 var sum, numerator, denominator;
4615 var pow = 2 / (opts.m - 1);
4616
4617 for (var c = 0; c < centroids.length; c++) {
4618 for (var n = 0; n < nodes.length; n++) {
4619 sum = 0;
4620
4621 for (var k = 0; k < centroids.length; k++) {
4622 // against all other centroids
4623 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4624 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4625 sum += Math.pow(numerator / denominator, pow);
4626 }
4627
4628 U[n][c] = 1 / sum;
4629 }
4630 }
4631};
4632
4633var assign$1 = function assign(nodes, U, opts, cy) {
4634 var clusters = new Array(opts.k);
4635
4636 for (var c = 0; c < clusters.length; c++) {
4637 clusters[c] = [];
4638 }
4639
4640 var max;
4641 var index;
4642
4643 for (var n = 0; n < U.length; n++) {
4644 // for each node (U is N x C matrix)
4645 max = -Infinity;
4646 index = -1; // Determine which cluster the node is most likely to belong in
4647
4648 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4649 if (U[n][_c2] > max) {
4650 max = U[n][_c2];
4651 index = _c2;
4652 }
4653 }
4654
4655 clusters[index].push(nodes[n]);
4656 } // Turn every array into a collection of nodes
4657
4658
4659 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4660 clusters[_c3] = cy.collection(clusters[_c3]);
4661 }
4662
4663 return clusters;
4664};
4665
4666var fuzzyCMeans = function fuzzyCMeans(options) {
4667 var cy = this.cy();
4668 var nodes = this.nodes();
4669 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4670
4671 var clusters;
4672 var centroids;
4673 var U;
4674
4675 var _U;
4676
4677 var weight; // Step 1: Initialize letiables.
4678
4679 _U = new Array(nodes.length);
4680
4681 for (var i = 0; i < nodes.length; i++) {
4682 // N x C matrix
4683 _U[i] = new Array(opts.k);
4684 }
4685
4686 U = new Array(nodes.length);
4687
4688 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4689 // N x C matrix
4690 U[_i3] = new Array(opts.k);
4691 }
4692
4693 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4694 var total = 0;
4695
4696 for (var j = 0; j < opts.k; j++) {
4697 U[_i4][j] = Math.random();
4698 total += U[_i4][j];
4699 }
4700
4701 for (var _j = 0; _j < opts.k; _j++) {
4702 U[_i4][_j] = U[_i4][_j] / total;
4703 }
4704 }
4705
4706 centroids = new Array(opts.k);
4707
4708 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4709 centroids[_i5] = new Array(opts.attributes.length);
4710 }
4711
4712 weight = new Array(nodes.length);
4713
4714 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4715 // N x C matrix
4716 weight[_i6] = new Array(opts.k);
4717 } // end init FCM
4718
4719
4720 var isStillMoving = true;
4721 var iterations = 0;
4722
4723 while (isStillMoving && iterations < opts.maxIterations) {
4724 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4725
4726 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4727
4728 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4729
4730 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4731 isStillMoving = true;
4732 }
4733
4734 iterations++;
4735 } // Assign nodes to clusters with highest probability.
4736
4737
4738 clusters = assign$1(nodes, U, opts, cy);
4739 return {
4740 clusters: clusters,
4741 degreeOfMembership: U
4742 };
4743};
4744
4745var kClustering = {
4746 kMeans: kMeans,
4747 kMedoids: kMedoids,
4748 fuzzyCMeans: fuzzyCMeans,
4749 fcm: fuzzyCMeans
4750};
4751
4752// Implemented by Zoe Xi @zoexi for GSOC 2016
4753var defaults$6 = defaults({
4754 distance: 'euclidean',
4755 // distance metric to compare nodes
4756 linkage: 'min',
4757 // linkage criterion : how to determine the distance between clusters of nodes
4758 mode: 'threshold',
4759 // mode:'threshold' => clusters must be threshold distance apart
4760 threshold: Infinity,
4761 // the distance threshold
4762 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4763 addDendrogram: false,
4764 // whether to add the dendrogram to the graph for viz
4765 dendrogramDepth: 0,
4766 // depth at which dendrogram branches are merged into the returned clusters
4767 attributes: [] // array of attr functions
4768
4769});
4770var linkageAliases = {
4771 'single': 'min',
4772 'complete': 'max'
4773};
4774
4775var setOptions$2 = function setOptions(options) {
4776 var opts = defaults$6(options);
4777 var preferredAlias = linkageAliases[opts.linkage];
4778
4779 if (preferredAlias != null) {
4780 opts.linkage = preferredAlias;
4781 }
4782
4783 return opts;
4784};
4785
4786var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4787 // Find two closest clusters from cached mins
4788 var minKey = 0;
4789 var min = Infinity;
4790 var dist;
4791 var attrs = opts.attributes;
4792
4793 var getDist = function getDist(n1, n2) {
4794 return clusteringDistance(opts.distance, attrs.length, function (i) {
4795 return attrs[i](n1);
4796 }, function (i) {
4797 return attrs[i](n2);
4798 }, n1, n2);
4799 };
4800
4801 for (var i = 0; i < clusters.length; i++) {
4802 var key = clusters[i].key;
4803 var _dist = dists[key][mins[key]];
4804
4805 if (_dist < min) {
4806 minKey = key;
4807 min = _dist;
4808 }
4809 }
4810
4811 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4812 return false;
4813 }
4814
4815 var c1 = index[minKey];
4816 var c2 = index[mins[minKey]];
4817 var merged; // Merge two closest clusters
4818
4819 if (opts.mode === 'dendrogram') {
4820 merged = {
4821 left: c1,
4822 right: c2,
4823 key: c1.key
4824 };
4825 } else {
4826 merged = {
4827 value: c1.value.concat(c2.value),
4828 key: c1.key
4829 };
4830 }
4831
4832 clusters[c1.index] = merged;
4833 clusters.splice(c2.index, 1);
4834 index[c1.key] = merged; // Update distances with new merged cluster
4835
4836 for (var _i = 0; _i < clusters.length; _i++) {
4837 var cur = clusters[_i];
4838
4839 if (c1.key === cur.key) {
4840 dist = Infinity;
4841 } else if (opts.linkage === 'min') {
4842 dist = dists[c1.key][cur.key];
4843
4844 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4845 dist = dists[c2.key][cur.key];
4846 }
4847 } else if (opts.linkage === 'max') {
4848 dist = dists[c1.key][cur.key];
4849
4850 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4851 dist = dists[c2.key][cur.key];
4852 }
4853 } else if (opts.linkage === 'mean') {
4854 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4855 } else {
4856 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4857 }
4858
4859 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4860 } // Update cached mins
4861
4862
4863 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4864 var key1 = clusters[_i2].key;
4865
4866 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4867 var _min = key1;
4868
4869 for (var j = 0; j < clusters.length; j++) {
4870 var key2 = clusters[j].key;
4871
4872 if (dists[key1][key2] < dists[key1][_min]) {
4873 _min = key2;
4874 }
4875 }
4876
4877 mins[key1] = _min;
4878 }
4879
4880 clusters[_i2].index = _i2;
4881 } // Clean up meta data used for clustering
4882
4883
4884 c1.key = c2.key = c1.index = c2.index = null;
4885 return true;
4886};
4887
4888var getAllChildren = function getAllChildren(root, arr, cy) {
4889 if (!root) return;
4890
4891 if (root.value) {
4892 arr.push(root.value);
4893 } else {
4894 if (root.left) getAllChildren(root.left, arr);
4895 if (root.right) getAllChildren(root.right, arr);
4896 }
4897};
4898
4899var buildDendrogram = function buildDendrogram(root, cy) {
4900 if (!root) return '';
4901
4902 if (root.left && root.right) {
4903 var leftStr = buildDendrogram(root.left, cy);
4904 var rightStr = buildDendrogram(root.right, cy);
4905 var node = cy.add({
4906 group: 'nodes',
4907 data: {
4908 id: leftStr + ',' + rightStr
4909 }
4910 });
4911 cy.add({
4912 group: 'edges',
4913 data: {
4914 source: leftStr,
4915 target: node.id()
4916 }
4917 });
4918 cy.add({
4919 group: 'edges',
4920 data: {
4921 source: rightStr,
4922 target: node.id()
4923 }
4924 });
4925 return node.id();
4926 } else if (root.value) {
4927 return root.value.id();
4928 }
4929};
4930
4931var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
4932 if (!root) return [];
4933 var left = [],
4934 right = [],
4935 leaves = [];
4936
4937 if (k === 0) {
4938 // don't cut tree, simply return all nodes as 1 single cluster
4939 if (root.left) getAllChildren(root.left, left);
4940 if (root.right) getAllChildren(root.right, right);
4941 leaves = left.concat(right);
4942 return [cy.collection(leaves)];
4943 } else if (k === 1) {
4944 // cut at root
4945 if (root.value) {
4946 // leaf node
4947 return [cy.collection(root.value)];
4948 } else {
4949 if (root.left) getAllChildren(root.left, left);
4950 if (root.right) getAllChildren(root.right, right);
4951 return [cy.collection(left), cy.collection(right)];
4952 }
4953 } else {
4954 if (root.value) {
4955 return [cy.collection(root.value)];
4956 } else {
4957 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
4958 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
4959 return left.concat(right);
4960 }
4961 }
4962};
4963/* eslint-enable */
4964
4965
4966var hierarchicalClustering = function hierarchicalClustering(options) {
4967 var cy = this.cy();
4968 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
4969
4970 var opts = setOptions$2(options);
4971 var attrs = opts.attributes;
4972
4973 var getDist = function getDist(n1, n2) {
4974 return clusteringDistance(opts.distance, attrs.length, function (i) {
4975 return attrs[i](n1);
4976 }, function (i) {
4977 return attrs[i](n2);
4978 }, n1, n2);
4979 }; // Begin hierarchical algorithm
4980
4981
4982 var clusters = [];
4983 var dists = []; // distances between each pair of clusters
4984
4985 var mins = []; // closest cluster for each cluster
4986
4987 var index = []; // hash of all clusters by key
4988 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
4989
4990 for (var n = 0; n < nodes.length; n++) {
4991 var cluster = {
4992 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
4993 key: n,
4994 index: n
4995 };
4996 clusters[n] = cluster;
4997 index[n] = cluster;
4998 dists[n] = [];
4999 mins[n] = 0;
5000 } // Calculate the distance between each pair of clusters
5001
5002
5003 for (var i = 0; i < clusters.length; i++) {
5004 for (var j = 0; j <= i; j++) {
5005 var dist = void 0;
5006
5007 if (opts.mode === 'dendrogram') {
5008 // modes store cluster values differently
5009 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5010 } else {
5011 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5012 }
5013
5014 dists[i][j] = dist;
5015 dists[j][i] = dist;
5016
5017 if (dist < dists[i][mins[i]]) {
5018 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5019 }
5020 }
5021 } // Find the closest pair of clusters and merge them into a single cluster.
5022 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5023
5024
5025 var merged = mergeClosest(clusters, index, dists, mins, opts);
5026
5027 while (merged) {
5028 merged = mergeClosest(clusters, index, dists, mins, opts);
5029 }
5030
5031 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5032 // in addition to returning the clusters.
5033
5034 if (opts.mode === 'dendrogram') {
5035 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5036 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5037 } else {
5038 // Regular mode simply returns the clusters
5039 retClusters = new Array(clusters.length);
5040 clusters.forEach(function (cluster, i) {
5041 // Clean up meta data used for clustering
5042 cluster.key = cluster.index = null;
5043 retClusters[i] = cy.collection(cluster.value);
5044 });
5045 }
5046
5047 return retClusters;
5048};
5049
5050var hierarchicalClustering$1 = {
5051 hierarchicalClustering: hierarchicalClustering,
5052 hca: hierarchicalClustering
5053};
5054
5055// Implemented by Zoe Xi @zoexi for GSOC 2016
5056var defaults$7 = defaults({
5057 distance: 'euclidean',
5058 // distance metric to compare attributes between two nodes
5059 preference: 'median',
5060 // suitability of a data point to serve as an exemplar
5061 damping: 0.8,
5062 // damping factor between [0.5, 1)
5063 maxIterations: 1000,
5064 // max number of iterations to run
5065 minIterations: 100,
5066 // min number of iterations to run in order for clustering to stop
5067 attributes: [// functions to quantify the similarity between any two points
5068 // e.g. node => node.data('weight')
5069 ]
5070});
5071
5072var setOptions$3 = function setOptions(options) {
5073 var dmp = options.damping;
5074 var pref = options.preference;
5075
5076 if (!(0.5 <= dmp && dmp < 1)) {
5077 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5078 }
5079
5080 var validPrefs = ['median', 'mean', 'min', 'max'];
5081
5082 if (!(validPrefs.some(function (v) {
5083 return v === pref;
5084 }) || number(pref))) {
5085 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5086 return "'".concat(p, "'");
5087 }).join(', '), "] or a number. Got: ").concat(pref));
5088 }
5089
5090 return defaults$7(options);
5091};
5092/* eslint-enable */
5093
5094
5095var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5096 var attr = function attr(n, i) {
5097 return attributes[i](n);
5098 }; // nb negative because similarity should have an inverse relationship to distance
5099
5100
5101 return -clusteringDistance(type, attributes.length, function (i) {
5102 return attr(n1, i);
5103 }, function (i) {
5104 return attr(n2, i);
5105 }, n1, n2);
5106};
5107
5108var getPreference = function getPreference(S, preference) {
5109 // larger preference = greater # of clusters
5110 var p = null;
5111
5112 if (preference === 'median') {
5113 p = median(S);
5114 } else if (preference === 'mean') {
5115 p = mean(S);
5116 } else if (preference === 'min') {
5117 p = min(S);
5118 } else if (preference === 'max') {
5119 p = max(S);
5120 } else {
5121 // Custom preference number, as set by user
5122 p = preference;
5123 }
5124
5125 return p;
5126};
5127
5128var findExemplars = function findExemplars(n, R, A) {
5129 var indices = [];
5130
5131 for (var i = 0; i < n; i++) {
5132 if (R[i * n + i] + A[i * n + i] > 0) {
5133 indices.push(i);
5134 }
5135 }
5136
5137 return indices;
5138};
5139
5140var assignClusters = function assignClusters(n, S, exemplars) {
5141 var clusters = [];
5142
5143 for (var i = 0; i < n; i++) {
5144 var index = -1;
5145 var max = -Infinity;
5146
5147 for (var ei = 0; ei < exemplars.length; ei++) {
5148 var e = exemplars[ei];
5149
5150 if (S[i * n + e] > max) {
5151 index = e;
5152 max = S[i * n + e];
5153 }
5154 }
5155
5156 if (index > 0) {
5157 clusters.push(index);
5158 }
5159 }
5160
5161 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5162 clusters[exemplars[_ei]] = exemplars[_ei];
5163 }
5164
5165 return clusters;
5166};
5167
5168var assign$2 = function assign(n, S, exemplars) {
5169 var clusters = assignClusters(n, S, exemplars);
5170
5171 for (var ei = 0; ei < exemplars.length; ei++) {
5172 var ii = [];
5173
5174 for (var c = 0; c < clusters.length; c++) {
5175 if (clusters[c] === exemplars[ei]) {
5176 ii.push(c);
5177 }
5178 }
5179
5180 var maxI = -1;
5181 var maxSum = -Infinity;
5182
5183 for (var i = 0; i < ii.length; i++) {
5184 var sum = 0;
5185
5186 for (var j = 0; j < ii.length; j++) {
5187 sum += S[ii[j] * n + ii[i]];
5188 }
5189
5190 if (sum > maxSum) {
5191 maxI = i;
5192 maxSum = sum;
5193 }
5194 }
5195
5196 exemplars[ei] = ii[maxI];
5197 }
5198
5199 clusters = assignClusters(n, S, exemplars);
5200 return clusters;
5201};
5202
5203var affinityPropagation = function affinityPropagation(options) {
5204 var cy = this.cy();
5205 var nodes = this.nodes();
5206 var opts = setOptions$3(options); // Map each node to its position in node array
5207
5208 var id2position = {};
5209
5210 for (var i = 0; i < nodes.length; i++) {
5211 id2position[nodes[i].id()] = i;
5212 } // Begin affinity propagation algorithm
5213
5214
5215 var n; // number of data points
5216
5217 var n2; // size of matrices
5218
5219 var S; // similarity matrix (1D array)
5220
5221 var p; // preference/suitability of a data point to serve as an exemplar
5222
5223 var R; // responsibility matrix (1D array)
5224
5225 var A; // availability matrix (1D array)
5226
5227 n = nodes.length;
5228 n2 = n * n; // Initialize and build S similarity matrix
5229
5230 S = new Array(n2);
5231
5232 for (var _i = 0; _i < n2; _i++) {
5233 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5234 }
5235
5236 for (var _i2 = 0; _i2 < n; _i2++) {
5237 for (var j = 0; j < n; j++) {
5238 if (_i2 !== j) {
5239 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5240 }
5241 }
5242 } // Place preferences on the diagonal of S
5243
5244
5245 p = getPreference(S, opts.preference);
5246
5247 for (var _i3 = 0; _i3 < n; _i3++) {
5248 S[_i3 * n + _i3] = p;
5249 } // Initialize R responsibility matrix
5250
5251
5252 R = new Array(n2);
5253
5254 for (var _i4 = 0; _i4 < n2; _i4++) {
5255 R[_i4] = 0.0;
5256 } // Initialize A availability matrix
5257
5258
5259 A = new Array(n2);
5260
5261 for (var _i5 = 0; _i5 < n2; _i5++) {
5262 A[_i5] = 0.0;
5263 }
5264
5265 var old = new Array(n);
5266 var Rp = new Array(n);
5267 var se = new Array(n);
5268
5269 for (var _i6 = 0; _i6 < n; _i6++) {
5270 old[_i6] = 0.0;
5271 Rp[_i6] = 0.0;
5272 se[_i6] = 0;
5273 }
5274
5275 var e = new Array(n * opts.minIterations);
5276
5277 for (var _i7 = 0; _i7 < e.length; _i7++) {
5278 e[_i7] = 0;
5279 }
5280
5281 var iter;
5282
5283 for (iter = 0; iter < opts.maxIterations; iter++) {
5284 // main algorithmic loop
5285 // Update R responsibility matrix
5286 for (var _i8 = 0; _i8 < n; _i8++) {
5287 var max = -Infinity,
5288 max2 = -Infinity,
5289 maxI = -1,
5290 AS = 0.0;
5291
5292 for (var _j = 0; _j < n; _j++) {
5293 old[_j] = R[_i8 * n + _j];
5294 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5295
5296 if (AS >= max) {
5297 max2 = max;
5298 max = AS;
5299 maxI = _j;
5300 } else if (AS > max2) {
5301 max2 = AS;
5302 }
5303 }
5304
5305 for (var _j2 = 0; _j2 < n; _j2++) {
5306 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5307 }
5308
5309 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5310 } // Update A availability matrix
5311
5312
5313 for (var _i9 = 0; _i9 < n; _i9++) {
5314 var sum = 0;
5315
5316 for (var _j3 = 0; _j3 < n; _j3++) {
5317 old[_j3] = A[_j3 * n + _i9];
5318 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5319 sum += Rp[_j3];
5320 }
5321
5322 sum -= Rp[_i9];
5323 Rp[_i9] = R[_i9 * n + _i9];
5324 sum += Rp[_i9];
5325
5326 for (var _j4 = 0; _j4 < n; _j4++) {
5327 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5328 }
5329
5330 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5331 } // Check for convergence
5332
5333
5334 var K = 0;
5335
5336 for (var _i10 = 0; _i10 < n; _i10++) {
5337 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5338 e[iter % opts.minIterations * n + _i10] = E;
5339 K += E;
5340 }
5341
5342 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5343 var _sum = 0;
5344
5345 for (var _i11 = 0; _i11 < n; _i11++) {
5346 se[_i11] = 0;
5347
5348 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5349 se[_i11] += e[_j5 * n + _i11];
5350 }
5351
5352 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5353 _sum++;
5354 }
5355 }
5356
5357 if (_sum === n) {
5358 // then we have convergence
5359 break;
5360 }
5361 }
5362 } // Identify exemplars (cluster centers)
5363
5364
5365 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5366
5367 var clusterIndices = assign$2(n, S, exemplarsIndices);
5368 var clusters = {};
5369
5370 for (var c = 0; c < exemplarsIndices.length; c++) {
5371 clusters[exemplarsIndices[c]] = [];
5372 }
5373
5374 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5375 var pos = id2position[nodes[_i12].id()];
5376
5377 var clusterIndex = clusterIndices[pos];
5378
5379 if (clusterIndex != null) {
5380 // the node may have not been assigned a cluster if no valid attributes were specified
5381 clusters[clusterIndex].push(nodes[_i12]);
5382 }
5383 }
5384
5385 var retClusters = new Array(exemplarsIndices.length);
5386
5387 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5388 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5389 }
5390
5391 return retClusters;
5392};
5393
5394var affinityPropagation$1 = {
5395 affinityPropagation: affinityPropagation,
5396 ap: affinityPropagation
5397};
5398
5399var hierholzerDefaults = defaults({
5400 root: undefined,
5401 directed: false
5402});
5403var elesfn$b = {
5404 hierholzer: function hierholzer(options) {
5405 if (!plainObject(options)) {
5406 var args = arguments;
5407 options = {
5408 root: args[0],
5409 directed: args[1]
5410 };
5411 }
5412
5413 var _hierholzerDefaults = hierholzerDefaults(options),
5414 root = _hierholzerDefaults.root,
5415 directed = _hierholzerDefaults.directed;
5416
5417 var eles = this;
5418 var dflag = false;
5419 var oddIn;
5420 var oddOut;
5421 var startVertex;
5422 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5423 var nodes = {};
5424 var edges = {};
5425
5426 if (directed) {
5427 eles.forEach(function (ele) {
5428 var id = ele.id();
5429
5430 if (ele.isNode()) {
5431 var ind = ele.indegree(true);
5432 var outd = ele.outdegree(true);
5433 var d1 = ind - outd;
5434 var d2 = outd - ind;
5435
5436 if (d1 == 1) {
5437 if (oddIn) dflag = true;else oddIn = id;
5438 } else if (d2 == 1) {
5439 if (oddOut) dflag = true;else oddOut = id;
5440 } else if (d2 > 1 || d1 > 1) {
5441 dflag = true;
5442 }
5443
5444 nodes[id] = [];
5445 ele.outgoers().forEach(function (e) {
5446 if (e.isEdge()) nodes[id].push(e.id());
5447 });
5448 } else {
5449 edges[id] = [undefined, ele.target().id()];
5450 }
5451 });
5452 } else {
5453 eles.forEach(function (ele) {
5454 var id = ele.id();
5455
5456 if (ele.isNode()) {
5457 var d = ele.degree(true);
5458
5459 if (d % 2) {
5460 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5461 }
5462
5463 nodes[id] = [];
5464 ele.connectedEdges().forEach(function (e) {
5465 return nodes[id].push(e.id());
5466 });
5467 } else {
5468 edges[id] = [ele.source().id(), ele.target().id()];
5469 }
5470 });
5471 }
5472
5473 var result = {
5474 found: false,
5475 trail: undefined
5476 };
5477 if (dflag) return result;else if (oddOut && oddIn) {
5478 if (directed) {
5479 if (startVertex && oddOut != startVertex) {
5480 return result;
5481 }
5482
5483 startVertex = oddOut;
5484 } else {
5485 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5486 return result;
5487 } else if (!startVertex) {
5488 startVertex = oddOut;
5489 }
5490 }
5491 } else {
5492 if (!startVertex) startVertex = eles[0].id();
5493 }
5494
5495 var walk = function walk(v) {
5496 var currentNode = v;
5497 var subtour = [v];
5498 var adj, adjTail, adjHead;
5499
5500 while (nodes[currentNode].length) {
5501 adj = nodes[currentNode].shift();
5502 adjTail = edges[adj][0];
5503 adjHead = edges[adj][1];
5504
5505 if (currentNode != adjHead) {
5506 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5507 return e != adj;
5508 });
5509 currentNode = adjHead;
5510 } else if (!directed && currentNode != adjTail) {
5511 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5512 return e != adj;
5513 });
5514 currentNode = adjTail;
5515 }
5516
5517 subtour.unshift(adj);
5518 subtour.unshift(currentNode);
5519 }
5520
5521 return subtour;
5522 };
5523
5524 var trail = [];
5525 var subtour = [];
5526 subtour = walk(startVertex);
5527
5528 while (subtour.length != 1) {
5529 if (nodes[subtour[0]].length == 0) {
5530 trail.unshift(eles.getElementById(subtour.shift()));
5531 trail.unshift(eles.getElementById(subtour.shift()));
5532 } else {
5533 subtour = walk(subtour.shift()).concat(subtour);
5534 }
5535 }
5536
5537 trail.unshift(eles.getElementById(subtour.shift())); // final node
5538
5539 for (var d in nodes) {
5540 if (nodes[d].length) {
5541 return result;
5542 }
5543 }
5544
5545 result.found = true;
5546 result.trail = this.spawn(trail);
5547 return result;
5548 }
5549};
5550
5551var elesfn$c = {};
5552[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].forEach(function (props) {
5553 extend(elesfn$c, props);
5554});
5555
5556/*!
5557Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5558Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5559Licensed under The MIT License (http://opensource.org/licenses/MIT)
5560*/
5561
5562/* promise states [Promises/A+ 2.1] */
5563var STATE_PENDING = 0;
5564/* [Promises/A+ 2.1.1] */
5565
5566var STATE_FULFILLED = 1;
5567/* [Promises/A+ 2.1.2] */
5568
5569var STATE_REJECTED = 2;
5570/* [Promises/A+ 2.1.3] */
5571
5572/* promise object constructor */
5573
5574var api = function api(executor) {
5575 /* optionally support non-constructor/plain-function call */
5576 if (!(this instanceof api)) return new api(executor);
5577 /* initialize object */
5578
5579 this.id = 'Thenable/1.0.7';
5580 this.state = STATE_PENDING;
5581 /* initial state */
5582
5583 this.fulfillValue = undefined;
5584 /* initial value */
5585
5586 /* [Promises/A+ 1.3, 2.1.2.2] */
5587
5588 this.rejectReason = undefined;
5589 /* initial reason */
5590
5591 /* [Promises/A+ 1.5, 2.1.3.2] */
5592
5593 this.onFulfilled = [];
5594 /* initial handlers */
5595
5596 this.onRejected = [];
5597 /* initial handlers */
5598
5599 /* provide optional information-hiding proxy */
5600
5601 this.proxy = {
5602 then: this.then.bind(this)
5603 };
5604 /* support optional executor function */
5605
5606 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5607};
5608/* promise API methods */
5609
5610
5611api.prototype = {
5612 /* promise resolving methods */
5613 fulfill: function fulfill(value) {
5614 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5615 },
5616 reject: function reject(value) {
5617 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5618 },
5619
5620 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5621 then: function then(onFulfilled, onRejected) {
5622 var curr = this;
5623 var next = new api();
5624 /* [Promises/A+ 2.2.7] */
5625
5626 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5627 /* [Promises/A+ 2.2.2/2.2.6] */
5628
5629 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5630 /* [Promises/A+ 2.2.3/2.2.6] */
5631
5632 execute(curr);
5633 return next.proxy;
5634 /* [Promises/A+ 2.2.7, 3.3] */
5635 }
5636};
5637/* deliver an action */
5638
5639var deliver = function deliver(curr, state, name, value) {
5640 if (curr.state === STATE_PENDING) {
5641 curr.state = state;
5642 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5643
5644 curr[name] = value;
5645 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5646
5647 execute(curr);
5648 }
5649
5650 return curr;
5651};
5652/* execute all handlers */
5653
5654
5655var execute = function execute(curr) {
5656 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5657};
5658/* execute particular set of handlers */
5659
5660
5661var execute_handlers = function execute_handlers(curr, name, value) {
5662 /* global setImmediate: true */
5663
5664 /* global setTimeout: true */
5665
5666 /* short-circuit processing */
5667 if (curr[name].length === 0) return;
5668 /* iterate over all handlers, exactly once */
5669
5670 var handlers = curr[name];
5671 curr[name] = [];
5672 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5673
5674 var func = function func() {
5675 for (var i = 0; i < handlers.length; i++) {
5676 handlers[i](value);
5677 }
5678 /* [Promises/A+ 2.2.5] */
5679
5680 };
5681 /* execute procedure asynchronously */
5682
5683 /* [Promises/A+ 2.2.4, 3.1] */
5684
5685
5686 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5687};
5688/* generate a resolver function */
5689
5690
5691var resolver = function resolver(cb, next, method) {
5692 return function (value) {
5693 if (typeof cb !== 'function')
5694 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5695 next[method].call(next, value);
5696 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5697 else {
5698 var result;
5699
5700 try {
5701 result = cb(value);
5702 }
5703 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5704 catch (e) {
5705 next.reject(e);
5706 /* [Promises/A+ 2.2.7.2] */
5707
5708 return;
5709 }
5710
5711 resolve(next, result);
5712 /* [Promises/A+ 2.2.7.1] */
5713 }
5714 };
5715};
5716/* "Promise Resolution Procedure" */
5717
5718/* [Promises/A+ 2.3] */
5719
5720
5721var resolve = function resolve(promise, x) {
5722 /* sanity check arguments */
5723
5724 /* [Promises/A+ 2.3.1] */
5725 if (promise === x || promise.proxy === x) {
5726 promise.reject(new TypeError('cannot resolve promise with itself'));
5727 return;
5728 }
5729 /* surgically check for a "then" method
5730 (mainly to just call the "getter" of "then" only once) */
5731
5732
5733 var then;
5734
5735 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
5736 try {
5737 then = x.then;
5738 }
5739 /* [Promises/A+ 2.3.3.1, 3.5] */
5740 catch (e) {
5741 promise.reject(e);
5742 /* [Promises/A+ 2.3.3.2] */
5743
5744 return;
5745 }
5746 }
5747 /* handle own Thenables [Promises/A+ 2.3.2]
5748 and similar "thenables" [Promises/A+ 2.3.3] */
5749
5750
5751 if (typeof then === 'function') {
5752 var resolved = false;
5753
5754 try {
5755 /* call retrieved "then" method */
5756
5757 /* [Promises/A+ 2.3.3.3] */
5758 then.call(x,
5759 /* resolvePromise */
5760
5761 /* [Promises/A+ 2.3.3.3.1] */
5762 function (y) {
5763 if (resolved) return;
5764 resolved = true;
5765 /* [Promises/A+ 2.3.3.3.3] */
5766
5767 if (y === x)
5768 /* [Promises/A+ 3.6] */
5769 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
5770 },
5771 /* rejectPromise */
5772
5773 /* [Promises/A+ 2.3.3.3.2] */
5774 function (r) {
5775 if (resolved) return;
5776 resolved = true;
5777 /* [Promises/A+ 2.3.3.3.3] */
5778
5779 promise.reject(r);
5780 });
5781 } catch (e) {
5782 if (!resolved)
5783 /* [Promises/A+ 2.3.3.3.3] */
5784 promise.reject(e);
5785 /* [Promises/A+ 2.3.3.3.4] */
5786 }
5787
5788 return;
5789 }
5790 /* handle other values */
5791
5792
5793 promise.fulfill(x);
5794 /* [Promises/A+ 2.3.4, 2.3.3.4] */
5795}; // so we always have Promise.all()
5796
5797
5798api.all = function (ps) {
5799 return new api(function (resolveAll, rejectAll) {
5800 var vals = new Array(ps.length);
5801 var doneCount = 0;
5802
5803 var fulfill = function fulfill(i, val) {
5804 vals[i] = val;
5805 doneCount++;
5806
5807 if (doneCount === ps.length) {
5808 resolveAll(vals);
5809 }
5810 };
5811
5812 for (var i = 0; i < ps.length; i++) {
5813 (function (i) {
5814 var p = ps[i];
5815 var isPromise = p != null && p.then != null;
5816
5817 if (isPromise) {
5818 p.then(function (val) {
5819 fulfill(i, val);
5820 }, function (err) {
5821 rejectAll(err);
5822 });
5823 } else {
5824 var val = p;
5825 fulfill(i, val);
5826 }
5827 })(i);
5828 }
5829 });
5830};
5831
5832api.resolve = function (val) {
5833 return new api(function (resolve, reject) {
5834 resolve(val);
5835 });
5836};
5837
5838api.reject = function (val) {
5839 return new api(function (resolve, reject) {
5840 reject(val);
5841 });
5842};
5843
5844var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
5845
5846var Animation = function Animation(target, opts, opts2) {
5847 var isCore = core(target);
5848 var isEle = !isCore;
5849
5850 var _p = this._private = extend({
5851 duration: 1000
5852 }, opts, opts2);
5853
5854 _p.target = target;
5855 _p.style = _p.style || _p.css;
5856 _p.started = false;
5857 _p.playing = false;
5858 _p.hooked = false;
5859 _p.applying = false;
5860 _p.progress = 0;
5861 _p.completes = [];
5862 _p.frames = [];
5863
5864 if (_p.complete && fn(_p.complete)) {
5865 _p.completes.push(_p.complete);
5866 }
5867
5868 if (isEle) {
5869 var pos = target.position();
5870 _p.startPosition = _p.startPosition || {
5871 x: pos.x,
5872 y: pos.y
5873 };
5874 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
5875 }
5876
5877 if (isCore) {
5878 var pan = target.pan();
5879 _p.startPan = {
5880 x: pan.x,
5881 y: pan.y
5882 };
5883 _p.startZoom = target.zoom();
5884 } // for future timeline/animations impl
5885
5886
5887 this.length = 1;
5888 this[0] = this;
5889};
5890
5891var anifn = Animation.prototype;
5892extend(anifn, {
5893 instanceString: function instanceString() {
5894 return 'animation';
5895 },
5896 hook: function hook() {
5897 var _p = this._private;
5898
5899 if (!_p.hooked) {
5900 // add to target's animation queue
5901 var q;
5902 var tAni = _p.target._private.animation;
5903
5904 if (_p.queue) {
5905 q = tAni.queue;
5906 } else {
5907 q = tAni.current;
5908 }
5909
5910 q.push(this); // add to the animation loop pool
5911
5912 if (elementOrCollection(_p.target)) {
5913 _p.target.cy().addToAnimationPool(_p.target);
5914 }
5915
5916 _p.hooked = true;
5917 }
5918
5919 return this;
5920 },
5921 play: function play() {
5922 var _p = this._private; // autorewind
5923
5924 if (_p.progress === 1) {
5925 _p.progress = 0;
5926 }
5927
5928 _p.playing = true;
5929 _p.started = false; // needs to be started by animation loop
5930
5931 _p.stopped = false;
5932 this.hook(); // the animation loop will start the animation...
5933
5934 return this;
5935 },
5936 playing: function playing() {
5937 return this._private.playing;
5938 },
5939 apply: function apply() {
5940 var _p = this._private;
5941 _p.applying = true;
5942 _p.started = false; // needs to be started by animation loop
5943
5944 _p.stopped = false;
5945 this.hook(); // the animation loop will apply the animation at this progress
5946
5947 return this;
5948 },
5949 applying: function applying() {
5950 return this._private.applying;
5951 },
5952 pause: function pause() {
5953 var _p = this._private;
5954 _p.playing = false;
5955 _p.started = false;
5956 return this;
5957 },
5958 stop: function stop() {
5959 var _p = this._private;
5960 _p.playing = false;
5961 _p.started = false;
5962 _p.stopped = true; // to be removed from animation queues
5963
5964 return this;
5965 },
5966 rewind: function rewind() {
5967 return this.progress(0);
5968 },
5969 fastforward: function fastforward() {
5970 return this.progress(1);
5971 },
5972 time: function time(t) {
5973 var _p = this._private;
5974
5975 if (t === undefined) {
5976 return _p.progress * _p.duration;
5977 } else {
5978 return this.progress(t / _p.duration);
5979 }
5980 },
5981 progress: function progress(p) {
5982 var _p = this._private;
5983 var wasPlaying = _p.playing;
5984
5985 if (p === undefined) {
5986 return _p.progress;
5987 } else {
5988 if (wasPlaying) {
5989 this.pause();
5990 }
5991
5992 _p.progress = p;
5993 _p.started = false;
5994
5995 if (wasPlaying) {
5996 this.play();
5997 }
5998 }
5999
6000 return this;
6001 },
6002 completed: function completed() {
6003 return this._private.progress === 1;
6004 },
6005 reverse: function reverse() {
6006 var _p = this._private;
6007 var wasPlaying = _p.playing;
6008
6009 if (wasPlaying) {
6010 this.pause();
6011 }
6012
6013 _p.progress = 1 - _p.progress;
6014 _p.started = false;
6015
6016 var swap = function swap(a, b) {
6017 var _pa = _p[a];
6018
6019 if (_pa == null) {
6020 return;
6021 }
6022
6023 _p[a] = _p[b];
6024 _p[b] = _pa;
6025 };
6026
6027 swap('zoom', 'startZoom');
6028 swap('pan', 'startPan');
6029 swap('position', 'startPosition'); // swap styles
6030
6031 if (_p.style) {
6032 for (var i = 0; i < _p.style.length; i++) {
6033 var prop = _p.style[i];
6034 var name = prop.name;
6035 var startStyleProp = _p.startStyle[name];
6036 _p.startStyle[name] = prop;
6037 _p.style[i] = startStyleProp;
6038 }
6039 }
6040
6041 if (wasPlaying) {
6042 this.play();
6043 }
6044
6045 return this;
6046 },
6047 promise: function promise(type) {
6048 var _p = this._private;
6049 var arr;
6050
6051 switch (type) {
6052 case 'frame':
6053 arr = _p.frames;
6054 break;
6055
6056 default:
6057 case 'complete':
6058 case 'completed':
6059 arr = _p.completes;
6060 }
6061
6062 return new Promise$1(function (resolve, reject) {
6063 arr.push(function () {
6064 resolve();
6065 });
6066 });
6067 }
6068});
6069anifn.complete = anifn.completed;
6070anifn.run = anifn.play;
6071anifn.running = anifn.playing;
6072
6073var define = {
6074 animated: function animated() {
6075 return function animatedImpl() {
6076 var self = this;
6077 var selfIsArrayLike = self.length !== undefined;
6078 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6079
6080 var cy = this._private.cy || this;
6081
6082 if (!cy.styleEnabled()) {
6083 return false;
6084 }
6085
6086 var ele = all[0];
6087
6088 if (ele) {
6089 return ele._private.animation.current.length > 0;
6090 }
6091 };
6092 },
6093 // animated
6094 clearQueue: function clearQueue() {
6095 return function clearQueueImpl() {
6096 var self = this;
6097 var selfIsArrayLike = self.length !== undefined;
6098 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6099
6100 var cy = this._private.cy || this;
6101
6102 if (!cy.styleEnabled()) {
6103 return this;
6104 }
6105
6106 for (var i = 0; i < all.length; i++) {
6107 var ele = all[i];
6108 ele._private.animation.queue = [];
6109 }
6110
6111 return this;
6112 };
6113 },
6114 // clearQueue
6115 delay: function delay() {
6116 return function delayImpl(time, complete) {
6117 var cy = this._private.cy || this;
6118
6119 if (!cy.styleEnabled()) {
6120 return this;
6121 }
6122
6123 return this.animate({
6124 delay: time,
6125 duration: time,
6126 complete: complete
6127 });
6128 };
6129 },
6130 // delay
6131 delayAnimation: function delayAnimation() {
6132 return function delayAnimationImpl(time, complete) {
6133 var cy = this._private.cy || this;
6134
6135 if (!cy.styleEnabled()) {
6136 return this;
6137 }
6138
6139 return this.animation({
6140 delay: time,
6141 duration: time,
6142 complete: complete
6143 });
6144 };
6145 },
6146 // delay
6147 animation: function animation() {
6148 return function animationImpl(properties, params) {
6149 var self = this;
6150 var selfIsArrayLike = self.length !== undefined;
6151 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6152
6153 var cy = this._private.cy || this;
6154 var isCore = !selfIsArrayLike;
6155 var isEles = !isCore;
6156
6157 if (!cy.styleEnabled()) {
6158 return this;
6159 }
6160
6161 var style = cy.style();
6162 properties = extend({}, properties, params);
6163 var propertiesEmpty = Object.keys(properties).length === 0;
6164
6165 if (propertiesEmpty) {
6166 return new Animation(all[0], properties); // nothing to animate
6167 }
6168
6169 if (properties.duration === undefined) {
6170 properties.duration = 400;
6171 }
6172
6173 switch (properties.duration) {
6174 case 'slow':
6175 properties.duration = 600;
6176 break;
6177
6178 case 'fast':
6179 properties.duration = 200;
6180 break;
6181 }
6182
6183 if (isEles) {
6184 properties.style = style.getPropsList(properties.style || properties.css);
6185 properties.css = undefined;
6186 }
6187
6188 if (isEles && properties.renderedPosition != null) {
6189 var rpos = properties.renderedPosition;
6190 var pan = cy.pan();
6191 var zoom = cy.zoom();
6192 properties.position = renderedToModelPosition(rpos, zoom, pan);
6193 } // override pan w/ panBy if set
6194
6195
6196 if (isCore && properties.panBy != null) {
6197 var panBy = properties.panBy;
6198 var cyPan = cy.pan();
6199 properties.pan = {
6200 x: cyPan.x + panBy.x,
6201 y: cyPan.y + panBy.y
6202 };
6203 } // override pan w/ center if set
6204
6205
6206 var center = properties.center || properties.centre;
6207
6208 if (isCore && center != null) {
6209 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6210
6211 if (centerPan != null) {
6212 properties.pan = centerPan;
6213 }
6214 } // override pan & zoom w/ fit if set
6215
6216
6217 if (isCore && properties.fit != null) {
6218 var fit = properties.fit;
6219 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6220
6221 if (fitVp != null) {
6222 properties.pan = fitVp.pan;
6223 properties.zoom = fitVp.zoom;
6224 }
6225 } // override zoom (& potentially pan) w/ zoom obj if set
6226
6227
6228 if (isCore && plainObject(properties.zoom)) {
6229 var vp = cy.getZoomedViewport(properties.zoom);
6230
6231 if (vp != null) {
6232 if (vp.zoomed) {
6233 properties.zoom = vp.zoom;
6234 }
6235
6236 if (vp.panned) {
6237 properties.pan = vp.pan;
6238 }
6239 }
6240 }
6241
6242 return new Animation(all[0], properties);
6243 };
6244 },
6245 // animate
6246 animate: function animate() {
6247 return function animateImpl(properties, params) {
6248 var self = this;
6249 var selfIsArrayLike = self.length !== undefined;
6250 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6251
6252 var cy = this._private.cy || this;
6253
6254 if (!cy.styleEnabled()) {
6255 return this;
6256 }
6257
6258 if (params) {
6259 properties = extend({}, properties, params);
6260 } // manually hook and run the animation
6261
6262
6263 for (var i = 0; i < all.length; i++) {
6264 var ele = all[i];
6265 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6266 var ani = ele.animation(properties, queue ? {
6267 queue: true
6268 } : undefined);
6269 ani.play();
6270 }
6271
6272 return this; // chaining
6273 };
6274 },
6275 // animate
6276 stop: function stop() {
6277 return function stopImpl(clearQueue, jumpToEnd) {
6278 var self = this;
6279 var selfIsArrayLike = self.length !== undefined;
6280 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6281
6282 var cy = this._private.cy || this;
6283
6284 if (!cy.styleEnabled()) {
6285 return this;
6286 }
6287
6288 for (var i = 0; i < all.length; i++) {
6289 var ele = all[i];
6290 var _p = ele._private;
6291 var anis = _p.animation.current;
6292
6293 for (var j = 0; j < anis.length; j++) {
6294 var ani = anis[j];
6295 var ani_p = ani._private;
6296
6297 if (jumpToEnd) {
6298 // next iteration of the animation loop, the animation
6299 // will go straight to the end and be removed
6300 ani_p.duration = 0;
6301 }
6302 } // clear the queue of future animations
6303
6304
6305 if (clearQueue) {
6306 _p.animation.queue = [];
6307 }
6308
6309 if (!jumpToEnd) {
6310 _p.animation.current = [];
6311 }
6312 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6313
6314
6315 cy.notify('draw');
6316 return this;
6317 };
6318 } // stop
6319
6320}; // define
6321
6322var define$1 = {
6323 // access data field
6324 data: function data(params) {
6325 var defaults = {
6326 field: 'data',
6327 bindingEvent: 'data',
6328 allowBinding: false,
6329 allowSetting: false,
6330 allowGetting: false,
6331 settingEvent: 'data',
6332 settingTriggersEvent: false,
6333 triggerFnName: 'trigger',
6334 immutableKeys: {},
6335 // key => true if immutable
6336 updateStyle: false,
6337 beforeGet: function beforeGet(self) {},
6338 beforeSet: function beforeSet(self, obj) {},
6339 onSet: function onSet(self) {},
6340 canSet: function canSet(self) {
6341 return true;
6342 }
6343 };
6344 params = extend({}, defaults, params);
6345 return function dataImpl(name, value) {
6346 var p = params;
6347 var self = this;
6348 var selfIsArrayLike = self.length !== undefined;
6349 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6350
6351 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6352
6353 if (string(name)) {
6354 // set or get property
6355 // .data('foo')
6356 if (p.allowGetting && value === undefined) {
6357 // get
6358 var ret;
6359
6360 if (single) {
6361 p.beforeGet(single);
6362 ret = single._private[p.field][name];
6363 }
6364
6365 return ret; // .data('foo', 'bar')
6366 } else if (p.allowSetting && value !== undefined) {
6367 // set
6368 var valid = !p.immutableKeys[name];
6369
6370 if (valid) {
6371 var change = _defineProperty({}, name, value);
6372
6373 p.beforeSet(self, change);
6374
6375 for (var i = 0, l = all.length; i < l; i++) {
6376 var ele = all[i];
6377
6378 if (p.canSet(ele)) {
6379 ele._private[p.field][name] = value;
6380 }
6381 } // update mappers if asked
6382
6383
6384 if (p.updateStyle) {
6385 self.updateStyle();
6386 } // call onSet callback
6387
6388
6389 p.onSet(self);
6390
6391 if (p.settingTriggersEvent) {
6392 self[p.triggerFnName](p.settingEvent);
6393 }
6394 }
6395 } // .data({ 'foo': 'bar' })
6396
6397 } else if (p.allowSetting && plainObject(name)) {
6398 // extend
6399 var obj = name;
6400 var k, v;
6401 var keys = Object.keys(obj);
6402 p.beforeSet(self, obj);
6403
6404 for (var _i = 0; _i < keys.length; _i++) {
6405 k = keys[_i];
6406 v = obj[k];
6407
6408 var _valid = !p.immutableKeys[k];
6409
6410 if (_valid) {
6411 for (var j = 0; j < all.length; j++) {
6412 var _ele = all[j];
6413
6414 if (p.canSet(_ele)) {
6415 _ele._private[p.field][k] = v;
6416 }
6417 }
6418 }
6419 } // update mappers if asked
6420
6421
6422 if (p.updateStyle) {
6423 self.updateStyle();
6424 } // call onSet callback
6425
6426
6427 p.onSet(self);
6428
6429 if (p.settingTriggersEvent) {
6430 self[p.triggerFnName](p.settingEvent);
6431 } // .data(function(){ ... })
6432
6433 } else if (p.allowBinding && fn(name)) {
6434 // bind to event
6435 var fn$1 = name;
6436 self.on(p.bindingEvent, fn$1); // .data()
6437 } else if (p.allowGetting && name === undefined) {
6438 // get whole object
6439 var _ret;
6440
6441 if (single) {
6442 p.beforeGet(single);
6443 _ret = single._private[p.field];
6444 }
6445
6446 return _ret;
6447 }
6448
6449 return self; // maintain chainability
6450 }; // function
6451 },
6452 // data
6453 // remove data field
6454 removeData: function removeData(params) {
6455 var defaults = {
6456 field: 'data',
6457 event: 'data',
6458 triggerFnName: 'trigger',
6459 triggerEvent: false,
6460 immutableKeys: {} // key => true if immutable
6461
6462 };
6463 params = extend({}, defaults, params);
6464 return function removeDataImpl(names) {
6465 var p = params;
6466 var self = this;
6467 var selfIsArrayLike = self.length !== undefined;
6468 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6469 // .removeData('foo bar')
6470
6471 if (string(names)) {
6472 // then get the list of keys, and delete them
6473 var keys = names.split(/\s+/);
6474 var l = keys.length;
6475
6476 for (var i = 0; i < l; i++) {
6477 // delete each non-empty key
6478 var key = keys[i];
6479
6480 if (emptyString(key)) {
6481 continue;
6482 }
6483
6484 var valid = !p.immutableKeys[key]; // not valid if immutable
6485
6486 if (valid) {
6487 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6488 all[i_a]._private[p.field][key] = undefined;
6489 }
6490 }
6491 }
6492
6493 if (p.triggerEvent) {
6494 self[p.triggerFnName](p.event);
6495 } // .removeData()
6496
6497 } else if (names === undefined) {
6498 // then delete all keys
6499 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6500 var _privateFields = all[_i_a]._private[p.field];
6501
6502 var _keys = Object.keys(_privateFields);
6503
6504 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6505 var _key = _keys[_i2];
6506 var validKeyToDelete = !p.immutableKeys[_key];
6507
6508 if (validKeyToDelete) {
6509 _privateFields[_key] = undefined;
6510 }
6511 }
6512 }
6513
6514 if (p.triggerEvent) {
6515 self[p.triggerFnName](p.event);
6516 }
6517 }
6518
6519 return self; // maintain chaining
6520 }; // function
6521 } // removeData
6522
6523}; // define
6524
6525var define$2 = {
6526 eventAliasesOn: function eventAliasesOn(proto) {
6527 var p = proto;
6528 p.addListener = p.listen = p.bind = p.on;
6529 p.unlisten = p.unbind = p.off = p.removeListener;
6530 p.trigger = p.emit; // this is just a wrapper alias of .on()
6531
6532 p.pon = p.promiseOn = function (events, selector) {
6533 var self = this;
6534 var args = Array.prototype.slice.call(arguments, 0);
6535 return new Promise$1(function (resolve, reject) {
6536 var callback = function callback(e) {
6537 self.off.apply(self, offArgs);
6538 resolve(e);
6539 };
6540
6541 var onArgs = args.concat([callback]);
6542 var offArgs = onArgs.concat([]);
6543 self.on.apply(self, onArgs);
6544 });
6545 };
6546 }
6547}; // define
6548
6549// use this module to cherry pick functions into your prototype
6550var define$3 = {};
6551[define, define$1, define$2].forEach(function (m) {
6552 extend(define$3, m);
6553});
6554
6555var elesfn$d = {
6556 animate: define$3.animate(),
6557 animation: define$3.animation(),
6558 animated: define$3.animated(),
6559 clearQueue: define$3.clearQueue(),
6560 delay: define$3.delay(),
6561 delayAnimation: define$3.delayAnimation(),
6562 stop: define$3.stop()
6563};
6564
6565var elesfn$e = {
6566 classes: function classes(_classes) {
6567 var self = this;
6568
6569 if (_classes === undefined) {
6570 var ret = [];
6571
6572 self[0]._private.classes.forEach(function (cls) {
6573 return ret.push(cls);
6574 });
6575
6576 return ret;
6577 } else if (!array(_classes)) {
6578 // extract classes from string
6579 _classes = (_classes || '').match(/\S+/g) || [];
6580 }
6581
6582 var changed = [];
6583 var classesSet = new Set$1(_classes); // check and update each ele
6584
6585 for (var j = 0; j < self.length; j++) {
6586 var ele = self[j];
6587 var _p = ele._private;
6588 var eleClasses = _p.classes;
6589 var changedEle = false; // check if ele has all of the passed classes
6590
6591 for (var i = 0; i < _classes.length; i++) {
6592 var cls = _classes[i];
6593 var eleHasClass = eleClasses.has(cls);
6594
6595 if (!eleHasClass) {
6596 changedEle = true;
6597 break;
6598 }
6599 } // check if ele has classes outside of those passed
6600
6601
6602 if (!changedEle) {
6603 changedEle = eleClasses.size !== _classes.length;
6604 }
6605
6606 if (changedEle) {
6607 _p.classes = classesSet;
6608 changed.push(ele);
6609 }
6610 } // trigger update style on those eles that had class changes
6611
6612
6613 if (changed.length > 0) {
6614 this.spawn(changed).updateStyle().emit('class');
6615 }
6616
6617 return self;
6618 },
6619 addClass: function addClass(classes) {
6620 return this.toggleClass(classes, true);
6621 },
6622 hasClass: function hasClass(className) {
6623 var ele = this[0];
6624 return ele != null && ele._private.classes.has(className);
6625 },
6626 toggleClass: function toggleClass(classes, toggle) {
6627 if (!array(classes)) {
6628 // extract classes from string
6629 classes = classes.match(/\S+/g) || [];
6630 }
6631
6632 var self = this;
6633 var toggleUndefd = toggle === undefined;
6634 var changed = []; // eles who had classes changed
6635
6636 for (var i = 0, il = self.length; i < il; i++) {
6637 var ele = self[i];
6638 var eleClasses = ele._private.classes;
6639 var changedEle = false;
6640
6641 for (var j = 0; j < classes.length; j++) {
6642 var cls = classes[j];
6643 var hasClass = eleClasses.has(cls);
6644 var changedNow = false;
6645
6646 if (toggle || toggleUndefd && !hasClass) {
6647 eleClasses.add(cls);
6648 changedNow = true;
6649 } else if (!toggle || toggleUndefd && hasClass) {
6650 eleClasses["delete"](cls);
6651 changedNow = true;
6652 }
6653
6654 if (!changedEle && changedNow) {
6655 changed.push(ele);
6656 changedEle = true;
6657 }
6658 } // for j classes
6659
6660 } // for i eles
6661 // trigger update style on those eles that had class changes
6662
6663
6664 if (changed.length > 0) {
6665 this.spawn(changed).updateStyle().emit('class');
6666 }
6667
6668 return self;
6669 },
6670 removeClass: function removeClass(classes) {
6671 return this.toggleClass(classes, false);
6672 },
6673 flashClass: function flashClass(classes, duration) {
6674 var self = this;
6675
6676 if (duration == null) {
6677 duration = 250;
6678 } else if (duration === 0) {
6679 return self; // nothing to do really
6680 }
6681
6682 self.addClass(classes);
6683 setTimeout(function () {
6684 self.removeClass(classes);
6685 }, duration);
6686 return self;
6687 }
6688};
6689elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6690
6691var tokens = {
6692 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6693 // chars we need to escape in let names, etc
6694 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6695 // binary comparison op (used in data selectors)
6696 boolOp: '\\?|\\!|\\^',
6697 // boolean (unary) operators (used in data selectors)
6698 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6699 // string literals (used in data selectors) -- doublequotes | singlequotes
6700 number: number$1,
6701 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6702 meta: 'degree|indegree|outdegree',
6703 // allowed metadata fields (i.e. allowed functions to use from Collection)
6704 separator: '\\s*,\\s*',
6705 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6706 descendant: '\\s+',
6707 child: '\\s+>\\s+',
6708 subject: '\\$',
6709 group: 'node|edge|\\*',
6710 directedEdge: '\\s+->\\s+',
6711 undirectedEdge: '\\s+<->\\s+'
6712};
6713tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
6714
6715tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
6716
6717tokens.className = tokens.variable; // a class name (follows variable conventions)
6718
6719tokens.id = tokens.variable; // an element id (follows variable conventions)
6720
6721(function () {
6722 var ops, op, i; // add @ variants to comparatorOp
6723
6724 ops = tokens.comparatorOp.split('|');
6725
6726 for (i = 0; i < ops.length; i++) {
6727 op = ops[i];
6728 tokens.comparatorOp += '|@' + op;
6729 } // add ! variants to comparatorOp
6730
6731
6732 ops = tokens.comparatorOp.split('|');
6733
6734 for (i = 0; i < ops.length; i++) {
6735 op = ops[i];
6736
6737 if (op.indexOf('!') >= 0) {
6738 continue;
6739 } // skip ops that explicitly contain !
6740
6741
6742 if (op === '=') {
6743 continue;
6744 } // skip = b/c != is explicitly defined
6745
6746
6747 tokens.comparatorOp += '|\\!' + op;
6748 }
6749})();
6750
6751/**
6752 * Make a new query object
6753 *
6754 * @prop type {Type} The type enum (int) of the query
6755 * @prop checks List of checks to make against an ele to test for a match
6756 */
6757var newQuery = function newQuery() {
6758 return {
6759 checks: []
6760 };
6761};
6762
6763/**
6764 * A check type enum-like object. Uses integer values for fast match() lookup.
6765 * The ordering does not matter as long as the ints are unique.
6766 */
6767var Type = {
6768 /** E.g. node */
6769 GROUP: 0,
6770
6771 /** A collection of elements */
6772 COLLECTION: 1,
6773
6774 /** A filter(ele) function */
6775 FILTER: 2,
6776
6777 /** E.g. [foo > 1] */
6778 DATA_COMPARE: 3,
6779
6780 /** E.g. [foo] */
6781 DATA_EXIST: 4,
6782
6783 /** E.g. [?foo] */
6784 DATA_BOOL: 5,
6785
6786 /** E.g. [[degree > 2]] */
6787 META_COMPARE: 6,
6788
6789 /** E.g. :selected */
6790 STATE: 7,
6791
6792 /** E.g. #foo */
6793 ID: 8,
6794
6795 /** E.g. .foo */
6796 CLASS: 9,
6797
6798 /** E.g. #foo <-> #bar */
6799 UNDIRECTED_EDGE: 10,
6800
6801 /** E.g. #foo -> #bar */
6802 DIRECTED_EDGE: 11,
6803
6804 /** E.g. $#foo -> #bar */
6805 NODE_SOURCE: 12,
6806
6807 /** E.g. #foo -> $#bar */
6808 NODE_TARGET: 13,
6809
6810 /** E.g. $#foo <-> #bar */
6811 NODE_NEIGHBOR: 14,
6812
6813 /** E.g. #foo > #bar */
6814 CHILD: 15,
6815
6816 /** E.g. #foo #bar */
6817 DESCENDANT: 16,
6818
6819 /** E.g. $#foo > #bar */
6820 PARENT: 17,
6821
6822 /** E.g. $#foo #bar */
6823 ANCESTOR: 18,
6824
6825 /** E.g. #foo > $bar > #baz */
6826 COMPOUND_SPLIT: 19,
6827
6828 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
6829 TRUE: 20
6830};
6831
6832var stateSelectors = [{
6833 selector: ':selected',
6834 matches: function matches(ele) {
6835 return ele.selected();
6836 }
6837}, {
6838 selector: ':unselected',
6839 matches: function matches(ele) {
6840 return !ele.selected();
6841 }
6842}, {
6843 selector: ':selectable',
6844 matches: function matches(ele) {
6845 return ele.selectable();
6846 }
6847}, {
6848 selector: ':unselectable',
6849 matches: function matches(ele) {
6850 return !ele.selectable();
6851 }
6852}, {
6853 selector: ':locked',
6854 matches: function matches(ele) {
6855 return ele.locked();
6856 }
6857}, {
6858 selector: ':unlocked',
6859 matches: function matches(ele) {
6860 return !ele.locked();
6861 }
6862}, {
6863 selector: ':visible',
6864 matches: function matches(ele) {
6865 return ele.visible();
6866 }
6867}, {
6868 selector: ':hidden',
6869 matches: function matches(ele) {
6870 return !ele.visible();
6871 }
6872}, {
6873 selector: ':transparent',
6874 matches: function matches(ele) {
6875 return ele.transparent();
6876 }
6877}, {
6878 selector: ':grabbed',
6879 matches: function matches(ele) {
6880 return ele.grabbed();
6881 }
6882}, {
6883 selector: ':free',
6884 matches: function matches(ele) {
6885 return !ele.grabbed();
6886 }
6887}, {
6888 selector: ':removed',
6889 matches: function matches(ele) {
6890 return ele.removed();
6891 }
6892}, {
6893 selector: ':inside',
6894 matches: function matches(ele) {
6895 return !ele.removed();
6896 }
6897}, {
6898 selector: ':grabbable',
6899 matches: function matches(ele) {
6900 return ele.grabbable();
6901 }
6902}, {
6903 selector: ':ungrabbable',
6904 matches: function matches(ele) {
6905 return !ele.grabbable();
6906 }
6907}, {
6908 selector: ':animated',
6909 matches: function matches(ele) {
6910 return ele.animated();
6911 }
6912}, {
6913 selector: ':unanimated',
6914 matches: function matches(ele) {
6915 return !ele.animated();
6916 }
6917}, {
6918 selector: ':parent',
6919 matches: function matches(ele) {
6920 return ele.isParent();
6921 }
6922}, {
6923 selector: ':childless',
6924 matches: function matches(ele) {
6925 return ele.isChildless();
6926 }
6927}, {
6928 selector: ':child',
6929 matches: function matches(ele) {
6930 return ele.isChild();
6931 }
6932}, {
6933 selector: ':orphan',
6934 matches: function matches(ele) {
6935 return ele.isOrphan();
6936 }
6937}, {
6938 selector: ':nonorphan',
6939 matches: function matches(ele) {
6940 return ele.isChild();
6941 }
6942}, {
6943 selector: ':compound',
6944 matches: function matches(ele) {
6945 if (ele.isNode()) {
6946 return ele.isParent();
6947 } else {
6948 return ele.source().isParent() || ele.target().isParent();
6949 }
6950 }
6951}, {
6952 selector: ':loop',
6953 matches: function matches(ele) {
6954 return ele.isLoop();
6955 }
6956}, {
6957 selector: ':simple',
6958 matches: function matches(ele) {
6959 return ele.isSimple();
6960 }
6961}, {
6962 selector: ':active',
6963 matches: function matches(ele) {
6964 return ele.active();
6965 }
6966}, {
6967 selector: ':inactive',
6968 matches: function matches(ele) {
6969 return !ele.active();
6970 }
6971}, {
6972 selector: ':backgrounding',
6973 matches: function matches(ele) {
6974 return ele.backgrounding();
6975 }
6976}, {
6977 selector: ':nonbackgrounding',
6978 matches: function matches(ele) {
6979 return !ele.backgrounding();
6980 }
6981}].sort(function (a, b) {
6982 // n.b. selectors that are starting substrings of others must have the longer ones first
6983 return descending(a.selector, b.selector);
6984});
6985
6986var lookup = function () {
6987 var selToFn = {};
6988 var s;
6989
6990 for (var i = 0; i < stateSelectors.length; i++) {
6991 s = stateSelectors[i];
6992 selToFn[s.selector] = s.matches;
6993 }
6994
6995 return selToFn;
6996}();
6997
6998var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
6999 return lookup[sel](ele);
7000};
7001var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7002 return s.selector;
7003}).join('|') + ')';
7004
7005// so that values get compared properly in Selector.filter()
7006
7007var cleanMetaChars = function cleanMetaChars(str) {
7008 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7009 return $1;
7010 });
7011};
7012
7013var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7014 selector[selector.length - 1] = replacementQuery;
7015}; // NOTE: add new expression syntax here to have it recognised by the parser;
7016// - a query contains all adjacent (i.e. no separator in between) expressions;
7017// - the current query is stored in selector[i]
7018// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7019
7020
7021var exprs = [{
7022 name: 'group',
7023 // just used for identifying when debugging
7024 query: true,
7025 regex: '(' + tokens.group + ')',
7026 populate: function populate(selector, query, _ref) {
7027 var _ref2 = _slicedToArray(_ref, 1),
7028 group = _ref2[0];
7029
7030 query.checks.push({
7031 type: Type.GROUP,
7032 value: group === '*' ? group : group + 's'
7033 });
7034 }
7035}, {
7036 name: 'state',
7037 query: true,
7038 regex: stateSelectorRegex,
7039 populate: function populate(selector, query, _ref3) {
7040 var _ref4 = _slicedToArray(_ref3, 1),
7041 state = _ref4[0];
7042
7043 query.checks.push({
7044 type: Type.STATE,
7045 value: state
7046 });
7047 }
7048}, {
7049 name: 'id',
7050 query: true,
7051 regex: '\\#(' + tokens.id + ')',
7052 populate: function populate(selector, query, _ref5) {
7053 var _ref6 = _slicedToArray(_ref5, 1),
7054 id = _ref6[0];
7055
7056 query.checks.push({
7057 type: Type.ID,
7058 value: cleanMetaChars(id)
7059 });
7060 }
7061}, {
7062 name: 'className',
7063 query: true,
7064 regex: '\\.(' + tokens.className + ')',
7065 populate: function populate(selector, query, _ref7) {
7066 var _ref8 = _slicedToArray(_ref7, 1),
7067 className = _ref8[0];
7068
7069 query.checks.push({
7070 type: Type.CLASS,
7071 value: cleanMetaChars(className)
7072 });
7073 }
7074}, {
7075 name: 'dataExists',
7076 query: true,
7077 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7078 populate: function populate(selector, query, _ref9) {
7079 var _ref10 = _slicedToArray(_ref9, 1),
7080 variable = _ref10[0];
7081
7082 query.checks.push({
7083 type: Type.DATA_EXIST,
7084 field: cleanMetaChars(variable)
7085 });
7086 }
7087}, {
7088 name: 'dataCompare',
7089 query: true,
7090 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7091 populate: function populate(selector, query, _ref11) {
7092 var _ref12 = _slicedToArray(_ref11, 3),
7093 variable = _ref12[0],
7094 comparatorOp = _ref12[1],
7095 value = _ref12[2];
7096
7097 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7098
7099 if (valueIsString) {
7100 value = value.substring(1, value.length - 1);
7101 } else {
7102 value = parseFloat(value);
7103 }
7104
7105 query.checks.push({
7106 type: Type.DATA_COMPARE,
7107 field: cleanMetaChars(variable),
7108 operator: comparatorOp,
7109 value: value
7110 });
7111 }
7112}, {
7113 name: 'dataBool',
7114 query: true,
7115 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7116 populate: function populate(selector, query, _ref13) {
7117 var _ref14 = _slicedToArray(_ref13, 2),
7118 boolOp = _ref14[0],
7119 variable = _ref14[1];
7120
7121 query.checks.push({
7122 type: Type.DATA_BOOL,
7123 field: cleanMetaChars(variable),
7124 operator: boolOp
7125 });
7126 }
7127}, {
7128 name: 'metaCompare',
7129 query: true,
7130 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7131 populate: function populate(selector, query, _ref15) {
7132 var _ref16 = _slicedToArray(_ref15, 3),
7133 meta = _ref16[0],
7134 comparatorOp = _ref16[1],
7135 number = _ref16[2];
7136
7137 query.checks.push({
7138 type: Type.META_COMPARE,
7139 field: cleanMetaChars(meta),
7140 operator: comparatorOp,
7141 value: parseFloat(number)
7142 });
7143 }
7144}, {
7145 name: 'nextQuery',
7146 separator: true,
7147 regex: tokens.separator,
7148 populate: function populate(selector, query) {
7149 var currentSubject = selector.currentSubject;
7150 var edgeCount = selector.edgeCount;
7151 var compoundCount = selector.compoundCount;
7152 var lastQ = selector[selector.length - 1];
7153
7154 if (currentSubject != null) {
7155 lastQ.subject = currentSubject;
7156 selector.currentSubject = null;
7157 }
7158
7159 lastQ.edgeCount = edgeCount;
7160 lastQ.compoundCount = compoundCount;
7161 selector.edgeCount = 0;
7162 selector.compoundCount = 0; // go on to next query
7163
7164 var nextQuery = selector[selector.length++] = newQuery();
7165 return nextQuery; // this is the new query to be filled by the following exprs
7166 }
7167}, {
7168 name: 'directedEdge',
7169 separator: true,
7170 regex: tokens.directedEdge,
7171 populate: function populate(selector, query) {
7172 if (selector.currentSubject == null) {
7173 // undirected edge
7174 var edgeQuery = newQuery();
7175 var source = query;
7176 var target = newQuery();
7177 edgeQuery.checks.push({
7178 type: Type.DIRECTED_EDGE,
7179 source: source,
7180 target: target
7181 }); // the query in the selector should be the edge rather than the source
7182
7183 replaceLastQuery(selector, query, edgeQuery);
7184 selector.edgeCount++; // we're now populating the target query with expressions that follow
7185
7186 return target;
7187 } else {
7188 // source/target
7189 var srcTgtQ = newQuery();
7190 var _source = query;
7191
7192 var _target = newQuery();
7193
7194 srcTgtQ.checks.push({
7195 type: Type.NODE_SOURCE,
7196 source: _source,
7197 target: _target
7198 }); // the query in the selector should be the neighbourhood rather than the node
7199
7200 replaceLastQuery(selector, query, srcTgtQ);
7201 selector.edgeCount++;
7202 return _target; // now populating the target with the following expressions
7203 }
7204 }
7205}, {
7206 name: 'undirectedEdge',
7207 separator: true,
7208 regex: tokens.undirectedEdge,
7209 populate: function populate(selector, query) {
7210 if (selector.currentSubject == null) {
7211 // undirected edge
7212 var edgeQuery = newQuery();
7213 var source = query;
7214 var target = newQuery();
7215 edgeQuery.checks.push({
7216 type: Type.UNDIRECTED_EDGE,
7217 nodes: [source, target]
7218 }); // the query in the selector should be the edge rather than the source
7219
7220 replaceLastQuery(selector, query, edgeQuery);
7221 selector.edgeCount++; // we're now populating the target query with expressions that follow
7222
7223 return target;
7224 } else {
7225 // neighbourhood
7226 var nhoodQ = newQuery();
7227 var node = query;
7228 var neighbor = newQuery();
7229 nhoodQ.checks.push({
7230 type: Type.NODE_NEIGHBOR,
7231 node: node,
7232 neighbor: neighbor
7233 }); // the query in the selector should be the neighbourhood rather than the node
7234
7235 replaceLastQuery(selector, query, nhoodQ);
7236 return neighbor; // now populating the neighbor with following expressions
7237 }
7238 }
7239}, {
7240 name: 'child',
7241 separator: true,
7242 regex: tokens.child,
7243 populate: function populate(selector, query) {
7244 if (selector.currentSubject == null) {
7245 // default: child query
7246 var parentChildQuery = newQuery();
7247 var child = newQuery();
7248 var parent = selector[selector.length - 1];
7249 parentChildQuery.checks.push({
7250 type: Type.CHILD,
7251 parent: parent,
7252 child: child
7253 }); // the query in the selector should be the '>' itself
7254
7255 replaceLastQuery(selector, query, parentChildQuery);
7256 selector.compoundCount++; // we're now populating the child query with expressions that follow
7257
7258 return child;
7259 } else if (selector.currentSubject === query) {
7260 // compound split query
7261 var compound = newQuery();
7262 var left = selector[selector.length - 1];
7263 var right = newQuery();
7264 var subject = newQuery();
7265
7266 var _child = newQuery();
7267
7268 var _parent = newQuery(); // set up the root compound q
7269
7270
7271 compound.checks.push({
7272 type: Type.COMPOUND_SPLIT,
7273 left: left,
7274 right: right,
7275 subject: subject
7276 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7277
7278 subject.checks = query.checks; // take the checks from the left
7279
7280 query.checks = [{
7281 type: Type.TRUE
7282 }]; // checks under left refs the subject implicitly
7283 // set up the right q
7284
7285 _parent.checks.push({
7286 type: Type.TRUE
7287 }); // parent implicitly refs the subject
7288
7289
7290 right.checks.push({
7291 type: Type.PARENT,
7292 // type is swapped on right side queries
7293 parent: _parent,
7294 child: _child // empty for now
7295
7296 });
7297 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7298
7299 selector.currentSubject = subject;
7300 selector.compoundCount++;
7301 return _child; // now populating the right side's child
7302 } else {
7303 // parent query
7304 // info for parent query
7305 var _parent2 = newQuery();
7306
7307 var _child2 = newQuery();
7308
7309 var pcQChecks = [{
7310 type: Type.PARENT,
7311 parent: _parent2,
7312 child: _child2
7313 }]; // the parent-child query takes the place of the query previously being populated
7314
7315 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7316
7317 query.checks = pcQChecks; // pc query takes over
7318
7319 selector.compoundCount++;
7320 return _child2; // we're now populating the child
7321 }
7322 }
7323}, {
7324 name: 'descendant',
7325 separator: true,
7326 regex: tokens.descendant,
7327 populate: function populate(selector, query) {
7328 if (selector.currentSubject == null) {
7329 // default: descendant query
7330 var ancChQuery = newQuery();
7331 var descendant = newQuery();
7332 var ancestor = selector[selector.length - 1];
7333 ancChQuery.checks.push({
7334 type: Type.DESCENDANT,
7335 ancestor: ancestor,
7336 descendant: descendant
7337 }); // the query in the selector should be the '>' itself
7338
7339 replaceLastQuery(selector, query, ancChQuery);
7340 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7341
7342 return descendant;
7343 } else if (selector.currentSubject === query) {
7344 // compound split query
7345 var compound = newQuery();
7346 var left = selector[selector.length - 1];
7347 var right = newQuery();
7348 var subject = newQuery();
7349
7350 var _descendant = newQuery();
7351
7352 var _ancestor = newQuery(); // set up the root compound q
7353
7354
7355 compound.checks.push({
7356 type: Type.COMPOUND_SPLIT,
7357 left: left,
7358 right: right,
7359 subject: subject
7360 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7361
7362 subject.checks = query.checks; // take the checks from the left
7363
7364 query.checks = [{
7365 type: Type.TRUE
7366 }]; // checks under left refs the subject implicitly
7367 // set up the right q
7368
7369 _ancestor.checks.push({
7370 type: Type.TRUE
7371 }); // ancestor implicitly refs the subject
7372
7373
7374 right.checks.push({
7375 type: Type.ANCESTOR,
7376 // type is swapped on right side queries
7377 ancestor: _ancestor,
7378 descendant: _descendant // empty for now
7379
7380 });
7381 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7382
7383 selector.currentSubject = subject;
7384 selector.compoundCount++;
7385 return _descendant; // now populating the right side's descendant
7386 } else {
7387 // ancestor query
7388 // info for parent query
7389 var _ancestor2 = newQuery();
7390
7391 var _descendant2 = newQuery();
7392
7393 var adQChecks = [{
7394 type: Type.ANCESTOR,
7395 ancestor: _ancestor2,
7396 descendant: _descendant2
7397 }]; // the parent-child query takes the place of the query previously being populated
7398
7399 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7400
7401 query.checks = adQChecks; // pc query takes over
7402
7403 selector.compoundCount++;
7404 return _descendant2; // we're now populating the child
7405 }
7406 }
7407}, {
7408 name: 'subject',
7409 modifier: true,
7410 regex: tokens.subject,
7411 populate: function populate(selector, query) {
7412 if (selector.currentSubject != null && selector.currentSubject !== query) {
7413 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7414 return false;
7415 }
7416
7417 selector.currentSubject = query;
7418 var topQ = selector[selector.length - 1];
7419 var topChk = topQ.checks[0];
7420 var topType = topChk == null ? null : topChk.type;
7421
7422 if (topType === Type.DIRECTED_EDGE) {
7423 // directed edge with subject on the target
7424 // change to target node check
7425 topChk.type = Type.NODE_TARGET;
7426 } else if (topType === Type.UNDIRECTED_EDGE) {
7427 // undirected edge with subject on the second node
7428 // change to neighbor check
7429 topChk.type = Type.NODE_NEIGHBOR;
7430 topChk.node = topChk.nodes[1]; // second node is subject
7431
7432 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7433
7434 topChk.nodes = null;
7435 }
7436 }
7437}];
7438exprs.forEach(function (e) {
7439 return e.regexObj = new RegExp('^' + e.regex);
7440});
7441
7442/**
7443 * Of all the expressions, find the first match in the remaining text.
7444 * @param {string} remaining The remaining text to parse
7445 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7446 */
7447
7448var consumeExpr = function consumeExpr(remaining) {
7449 var expr;
7450 var match;
7451 var name;
7452
7453 for (var j = 0; j < exprs.length; j++) {
7454 var e = exprs[j];
7455 var n = e.name;
7456 var m = remaining.match(e.regexObj);
7457
7458 if (m != null) {
7459 match = m;
7460 expr = e;
7461 name = n;
7462 var consumed = m[0];
7463 remaining = remaining.substring(consumed.length);
7464 break; // we've consumed one expr, so we can return now
7465 }
7466 }
7467
7468 return {
7469 expr: expr,
7470 match: match,
7471 name: name,
7472 remaining: remaining
7473 };
7474};
7475/**
7476 * Consume all the leading whitespace
7477 * @param {string} remaining The text to consume
7478 * @returns The text with the leading whitespace removed
7479 */
7480
7481
7482var consumeWhitespace = function consumeWhitespace(remaining) {
7483 var match = remaining.match(/^\s+/);
7484
7485 if (match) {
7486 var consumed = match[0];
7487 remaining = remaining.substring(consumed.length);
7488 }
7489
7490 return remaining;
7491};
7492/**
7493 * Parse the string and store the parsed representation in the Selector.
7494 * @param {string} selector The selector string
7495 * @returns `true` if the selector was successfully parsed, `false` otherwise
7496 */
7497
7498
7499var parse = function parse(selector) {
7500 var self = this;
7501 var remaining = self.inputText = selector;
7502 var currentQuery = self[0] = newQuery();
7503 self.length = 1;
7504 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7505
7506 for (;;) {
7507 var exprInfo = consumeExpr(remaining);
7508
7509 if (exprInfo.expr == null) {
7510 warn('The selector `' + selector + '`is invalid');
7511 return false;
7512 } else {
7513 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7514
7515 var ret = exprInfo.expr.populate(self, currentQuery, args);
7516
7517 if (ret === false) {
7518 return false; // exit if population failed
7519 } else if (ret != null) {
7520 currentQuery = ret; // change the current query to be filled if the expr specifies
7521 }
7522 }
7523
7524 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7525
7526 if (remaining.match(/^\s*$/)) {
7527 break;
7528 }
7529 }
7530
7531 var lastQ = self[self.length - 1];
7532
7533 if (self.currentSubject != null) {
7534 lastQ.subject = self.currentSubject;
7535 }
7536
7537 lastQ.edgeCount = self.edgeCount;
7538 lastQ.compoundCount = self.compoundCount;
7539
7540 for (var i = 0; i < self.length; i++) {
7541 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7542
7543 if (q.compoundCount > 0 && q.edgeCount > 0) {
7544 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7545 return false;
7546 }
7547
7548 if (q.edgeCount > 1) {
7549 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7550 return false;
7551 } else if (q.edgeCount === 1) {
7552 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.');
7553 }
7554 }
7555
7556 return true; // success
7557};
7558/**
7559 * Get the selector represented as a string. This value uses default formatting,
7560 * so things like spacing may differ from the input text passed to the constructor.
7561 * @returns {string} The selector string
7562 */
7563
7564
7565var toString = function toString() {
7566 if (this.toStringCache != null) {
7567 return this.toStringCache;
7568 }
7569
7570 var clean = function clean(obj) {
7571 if (obj == null) {
7572 return '';
7573 } else {
7574 return obj;
7575 }
7576 };
7577
7578 var cleanVal = function cleanVal(val) {
7579 if (string(val)) {
7580 return '"' + val + '"';
7581 } else {
7582 return clean(val);
7583 }
7584 };
7585
7586 var space = function space(val) {
7587 return ' ' + val + ' ';
7588 };
7589
7590 var checkToString = function checkToString(check, subject) {
7591 var type = check.type,
7592 value = check.value;
7593
7594 switch (type) {
7595 case Type.GROUP:
7596 {
7597 var group = clean(value);
7598 return group.substring(0, group.length - 1);
7599 }
7600
7601 case Type.DATA_COMPARE:
7602 {
7603 var field = check.field,
7604 operator = check.operator;
7605 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7606 }
7607
7608 case Type.DATA_BOOL:
7609 {
7610 var _operator = check.operator,
7611 _field = check.field;
7612 return '[' + clean(_operator) + _field + ']';
7613 }
7614
7615 case Type.DATA_EXIST:
7616 {
7617 var _field2 = check.field;
7618 return '[' + _field2 + ']';
7619 }
7620
7621 case Type.META_COMPARE:
7622 {
7623 var _operator2 = check.operator,
7624 _field3 = check.field;
7625 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7626 }
7627
7628 case Type.STATE:
7629 {
7630 return value;
7631 }
7632
7633 case Type.ID:
7634 {
7635 return '#' + value;
7636 }
7637
7638 case Type.CLASS:
7639 {
7640 return '.' + value;
7641 }
7642
7643 case Type.PARENT:
7644 case Type.CHILD:
7645 {
7646 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7647 }
7648
7649 case Type.ANCESTOR:
7650 case Type.DESCENDANT:
7651 {
7652 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7653 }
7654
7655 case Type.COMPOUND_SPLIT:
7656 {
7657 var lhs = queryToString(check.left, subject);
7658 var sub = queryToString(check.subject, subject);
7659 var rhs = queryToString(check.right, subject);
7660 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7661 }
7662
7663 case Type.TRUE:
7664 {
7665 return '';
7666 }
7667 }
7668 };
7669
7670 var queryToString = function queryToString(query, subject) {
7671 return query.checks.reduce(function (str, chk, i) {
7672 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7673 }, '');
7674 };
7675
7676 var str = '';
7677
7678 for (var i = 0; i < this.length; i++) {
7679 var query = this[i];
7680 str += queryToString(query, query.subject);
7681
7682 if (this.length > 1 && i < this.length - 1) {
7683 str += ', ';
7684 }
7685 }
7686
7687 this.toStringCache = str;
7688 return str;
7689};
7690var parse$1 = {
7691 parse: parse,
7692 toString: toString
7693};
7694
7695var valCmp = function valCmp(fieldVal, operator, value) {
7696 var matches;
7697 var isFieldStr = string(fieldVal);
7698 var isFieldNum = number(fieldVal);
7699 var isValStr = string(value);
7700 var fieldStr, valStr;
7701 var caseInsensitive = false;
7702 var notExpr = false;
7703 var isIneqCmp = false;
7704
7705 if (operator.indexOf('!') >= 0) {
7706 operator = operator.replace('!', '');
7707 notExpr = true;
7708 }
7709
7710 if (operator.indexOf('@') >= 0) {
7711 operator = operator.replace('@', '');
7712 caseInsensitive = true;
7713 }
7714
7715 if (isFieldStr || isValStr || caseInsensitive) {
7716 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
7717 valStr = '' + value;
7718 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
7719 // even if we're comparing numbers
7720
7721
7722 if (caseInsensitive) {
7723 fieldVal = fieldStr = fieldStr.toLowerCase();
7724 value = valStr = valStr.toLowerCase();
7725 }
7726
7727 switch (operator) {
7728 case '*=':
7729 matches = fieldStr.indexOf(valStr) >= 0;
7730 break;
7731
7732 case '$=':
7733 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
7734 break;
7735
7736 case '^=':
7737 matches = fieldStr.indexOf(valStr) === 0;
7738 break;
7739
7740 case '=':
7741 matches = fieldVal === value;
7742 break;
7743
7744 case '>':
7745 isIneqCmp = true;
7746 matches = fieldVal > value;
7747 break;
7748
7749 case '>=':
7750 isIneqCmp = true;
7751 matches = fieldVal >= value;
7752 break;
7753
7754 case '<':
7755 isIneqCmp = true;
7756 matches = fieldVal < value;
7757 break;
7758
7759 case '<=':
7760 isIneqCmp = true;
7761 matches = fieldVal <= value;
7762 break;
7763
7764 default:
7765 matches = false;
7766 break;
7767 } // apply the not op, but null vals for inequalities should always stay non-matching
7768
7769
7770 if (notExpr && (fieldVal != null || !isIneqCmp)) {
7771 matches = !matches;
7772 }
7773
7774 return matches;
7775};
7776var boolCmp = function boolCmp(fieldVal, operator) {
7777 switch (operator) {
7778 case '?':
7779 return fieldVal ? true : false;
7780
7781 case '!':
7782 return fieldVal ? false : true;
7783
7784 case '^':
7785 return fieldVal === undefined;
7786 }
7787};
7788var existCmp = function existCmp(fieldVal) {
7789 return fieldVal !== undefined;
7790};
7791var data = function data(ele, field) {
7792 return ele.data(field);
7793};
7794var meta = function meta(ele, field) {
7795 return ele[field]();
7796};
7797
7798/** A lookup of `match(check, ele)` functions by `Type` int */
7799
7800var match = [];
7801/**
7802 * Returns whether the query matches for the element
7803 * @param query The `{ type, value, ... }` query object
7804 * @param ele The element to compare against
7805*/
7806
7807var matches = function matches(query, ele) {
7808 return query.checks.every(function (chk) {
7809 return match[chk.type](chk, ele);
7810 });
7811};
7812
7813match[Type.GROUP] = function (check, ele) {
7814 var group = check.value;
7815 return group === '*' || group === ele.group();
7816};
7817
7818match[Type.STATE] = function (check, ele) {
7819 var stateSelector = check.value;
7820 return stateSelectorMatches(stateSelector, ele);
7821};
7822
7823match[Type.ID] = function (check, ele) {
7824 var id = check.value;
7825 return ele.id() === id;
7826};
7827
7828match[Type.CLASS] = function (check, ele) {
7829 var cls = check.value;
7830 return ele.hasClass(cls);
7831};
7832
7833match[Type.META_COMPARE] = function (check, ele) {
7834 var field = check.field,
7835 operator = check.operator,
7836 value = check.value;
7837 return valCmp(meta(ele, field), operator, value);
7838};
7839
7840match[Type.DATA_COMPARE] = function (check, ele) {
7841 var field = check.field,
7842 operator = check.operator,
7843 value = check.value;
7844 return valCmp(data(ele, field), operator, value);
7845};
7846
7847match[Type.DATA_BOOL] = function (check, ele) {
7848 var field = check.field,
7849 operator = check.operator;
7850 return boolCmp(data(ele, field), operator);
7851};
7852
7853match[Type.DATA_EXIST] = function (check, ele) {
7854 var field = check.field,
7855 operator = check.operator;
7856 return existCmp(data(ele, field));
7857};
7858
7859match[Type.UNDIRECTED_EDGE] = function (check, ele) {
7860 var qA = check.nodes[0];
7861 var qB = check.nodes[1];
7862 var src = ele.source();
7863 var tgt = ele.target();
7864 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
7865};
7866
7867match[Type.NODE_NEIGHBOR] = function (check, ele) {
7868 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
7869 return n.isNode() && matches(check.neighbor, n);
7870 });
7871};
7872
7873match[Type.DIRECTED_EDGE] = function (check, ele) {
7874 return matches(check.source, ele.source()) && matches(check.target, ele.target());
7875};
7876
7877match[Type.NODE_SOURCE] = function (check, ele) {
7878 return matches(check.source, ele) && ele.outgoers().some(function (n) {
7879 return n.isNode() && matches(check.target, n);
7880 });
7881};
7882
7883match[Type.NODE_TARGET] = function (check, ele) {
7884 return matches(check.target, ele) && ele.incomers().some(function (n) {
7885 return n.isNode() && matches(check.source, n);
7886 });
7887};
7888
7889match[Type.CHILD] = function (check, ele) {
7890 return matches(check.child, ele) && matches(check.parent, ele.parent());
7891};
7892
7893match[Type.PARENT] = function (check, ele) {
7894 return matches(check.parent, ele) && ele.children().some(function (c) {
7895 return matches(check.child, c);
7896 });
7897};
7898
7899match[Type.DESCENDANT] = function (check, ele) {
7900 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
7901 return matches(check.ancestor, a);
7902 });
7903};
7904
7905match[Type.ANCESTOR] = function (check, ele) {
7906 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
7907 return matches(check.descendant, d);
7908 });
7909};
7910
7911match[Type.COMPOUND_SPLIT] = function (check, ele) {
7912 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
7913};
7914
7915match[Type.TRUE] = function () {
7916 return true;
7917};
7918
7919match[Type.COLLECTION] = function (check, ele) {
7920 var collection = check.value;
7921 return collection.has(ele);
7922};
7923
7924match[Type.FILTER] = function (check, ele) {
7925 var filter = check.value;
7926 return filter(ele);
7927};
7928
7929var filter = function filter(collection) {
7930 var self = this; // for 1 id #foo queries, just get the element
7931
7932 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
7933 return collection.getElementById(self[0].checks[0].value).collection();
7934 }
7935
7936 var selectorFunction = function selectorFunction(element) {
7937 for (var j = 0; j < self.length; j++) {
7938 var query = self[j];
7939
7940 if (matches(query, element)) {
7941 return true;
7942 }
7943 }
7944
7945 return false;
7946 };
7947
7948 if (self.text() == null) {
7949 selectorFunction = function selectorFunction() {
7950 return true;
7951 };
7952 }
7953
7954 return collection.filter(selectorFunction);
7955}; // filter
7956// does selector match a single element?
7957
7958
7959var matches$1 = function matches$1(ele) {
7960 var self = this;
7961
7962 for (var j = 0; j < self.length; j++) {
7963 var query = self[j];
7964
7965 if (matches(query, ele)) {
7966 return true;
7967 }
7968 }
7969
7970 return false;
7971}; // matches
7972
7973
7974var matching = {
7975 matches: matches$1,
7976 filter: filter
7977};
7978
7979var Selector = function Selector(selector) {
7980 this.inputText = selector;
7981 this.currentSubject = null;
7982 this.compoundCount = 0;
7983 this.edgeCount = 0;
7984 this.length = 0;
7985
7986 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
7987 this.addQuery({
7988 checks: [{
7989 type: Type.COLLECTION,
7990 value: selector.collection()
7991 }]
7992 });
7993 } else if (fn(selector)) {
7994 this.addQuery({
7995 checks: [{
7996 type: Type.FILTER,
7997 value: selector
7998 }]
7999 });
8000 } else if (string(selector)) {
8001 if (!this.parse(selector)) {
8002 this.invalid = true;
8003 }
8004 } else {
8005 error('A selector must be created from a string; found ');
8006 }
8007};
8008
8009var selfn = Selector.prototype;
8010[parse$1, matching].forEach(function (p) {
8011 return extend(selfn, p);
8012});
8013
8014selfn.text = function () {
8015 return this.inputText;
8016};
8017
8018selfn.size = function () {
8019 return this.length;
8020};
8021
8022selfn.eq = function (i) {
8023 return this[i];
8024};
8025
8026selfn.sameText = function (otherSel) {
8027 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8028};
8029
8030selfn.addQuery = function (q) {
8031 this[this.length++] = q;
8032};
8033
8034selfn.selector = selfn.toString;
8035
8036var elesfn$f = {
8037 allAre: function allAre(selector) {
8038 var selObj = new Selector(selector);
8039 return this.every(function (ele) {
8040 return selObj.matches(ele);
8041 });
8042 },
8043 is: function is(selector) {
8044 var selObj = new Selector(selector);
8045 return this.some(function (ele) {
8046 return selObj.matches(ele);
8047 });
8048 },
8049 some: function some(fn, thisArg) {
8050 for (var i = 0; i < this.length; i++) {
8051 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8052
8053 if (ret) {
8054 return true;
8055 }
8056 }
8057
8058 return false;
8059 },
8060 every: function every(fn, thisArg) {
8061 for (var i = 0; i < this.length; i++) {
8062 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8063
8064 if (!ret) {
8065 return false;
8066 }
8067 }
8068
8069 return true;
8070 },
8071 same: function same(collection) {
8072 // cheap collection ref check
8073 if (this === collection) {
8074 return true;
8075 }
8076
8077 collection = this.cy().collection(collection);
8078 var thisLength = this.length;
8079 var collectionLength = collection.length; // cheap length check
8080
8081 if (thisLength !== collectionLength) {
8082 return false;
8083 } // cheap element ref check
8084
8085
8086 if (thisLength === 1) {
8087 return this[0] === collection[0];
8088 }
8089
8090 return this.every(function (ele) {
8091 return collection.hasElementWithId(ele.id());
8092 });
8093 },
8094 anySame: function anySame(collection) {
8095 collection = this.cy().collection(collection);
8096 return this.some(function (ele) {
8097 return collection.hasElementWithId(ele.id());
8098 });
8099 },
8100 allAreNeighbors: function allAreNeighbors(collection) {
8101 collection = this.cy().collection(collection);
8102 var nhood = this.neighborhood();
8103 return collection.every(function (ele) {
8104 return nhood.hasElementWithId(ele.id());
8105 });
8106 },
8107 contains: function contains(collection) {
8108 collection = this.cy().collection(collection);
8109 var self = this;
8110 return collection.every(function (ele) {
8111 return self.hasElementWithId(ele.id());
8112 });
8113 }
8114};
8115elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8116elesfn$f.has = elesfn$f.contains;
8117elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8118
8119var cache = function cache(fn, name) {
8120 return function traversalCache(arg1, arg2, arg3, arg4) {
8121 var selectorOrEles = arg1;
8122 var eles = this;
8123 var key;
8124
8125 if (selectorOrEles == null) {
8126 key = '';
8127 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8128 key = selectorOrEles.id();
8129 }
8130
8131 if (eles.length === 1 && key) {
8132 var _p = eles[0]._private;
8133 var tch = _p.traversalCache = _p.traversalCache || {};
8134 var ch = tch[name] = tch[name] || [];
8135 var hash = hashString(key);
8136 var cacheHit = ch[hash];
8137
8138 if (cacheHit) {
8139 return cacheHit;
8140 } else {
8141 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8142 }
8143 } else {
8144 return fn.call(eles, arg1, arg2, arg3, arg4);
8145 }
8146 };
8147};
8148
8149var elesfn$g = {
8150 parent: function parent(selector) {
8151 var parents = []; // optimisation for single ele call
8152
8153 if (this.length === 1) {
8154 var parent = this[0]._private.parent;
8155
8156 if (parent) {
8157 return parent;
8158 }
8159 }
8160
8161 for (var i = 0; i < this.length; i++) {
8162 var ele = this[i];
8163 var _parent = ele._private.parent;
8164
8165 if (_parent) {
8166 parents.push(_parent);
8167 }
8168 }
8169
8170 return this.spawn(parents, {
8171 unique: true
8172 }).filter(selector);
8173 },
8174 parents: function parents(selector) {
8175 var parents = [];
8176 var eles = this.parent();
8177
8178 while (eles.nonempty()) {
8179 for (var i = 0; i < eles.length; i++) {
8180 var ele = eles[i];
8181 parents.push(ele);
8182 }
8183
8184 eles = eles.parent();
8185 }
8186
8187 return this.spawn(parents, {
8188 unique: true
8189 }).filter(selector);
8190 },
8191 commonAncestors: function commonAncestors(selector) {
8192 var ancestors;
8193
8194 for (var i = 0; i < this.length; i++) {
8195 var ele = this[i];
8196 var parents = ele.parents();
8197 ancestors = ancestors || parents;
8198 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8199 }
8200
8201 return ancestors.filter(selector);
8202 },
8203 orphans: function orphans(selector) {
8204 return this.stdFilter(function (ele) {
8205 return ele.isOrphan();
8206 }).filter(selector);
8207 },
8208 nonorphans: function nonorphans(selector) {
8209 return this.stdFilter(function (ele) {
8210 return ele.isChild();
8211 }).filter(selector);
8212 },
8213 children: cache(function (selector) {
8214 var children = [];
8215
8216 for (var i = 0; i < this.length; i++) {
8217 var ele = this[i];
8218 var eleChildren = ele._private.children;
8219
8220 for (var j = 0; j < eleChildren.length; j++) {
8221 children.push(eleChildren[j]);
8222 }
8223 }
8224
8225 return this.spawn(children, {
8226 unique: true
8227 }).filter(selector);
8228 }, 'children'),
8229 siblings: function siblings(selector) {
8230 return this.parent().children().not(this).filter(selector);
8231 },
8232 isParent: function isParent() {
8233 var ele = this[0];
8234
8235 if (ele) {
8236 return ele.isNode() && ele._private.children.length !== 0;
8237 }
8238 },
8239 isChildless: function isChildless() {
8240 var ele = this[0];
8241
8242 if (ele) {
8243 return ele.isNode() && ele._private.children.length === 0;
8244 }
8245 },
8246 isChild: function isChild() {
8247 var ele = this[0];
8248
8249 if (ele) {
8250 return ele.isNode() && ele._private.parent != null;
8251 }
8252 },
8253 isOrphan: function isOrphan() {
8254 var ele = this[0];
8255
8256 if (ele) {
8257 return ele.isNode() && ele._private.parent == null;
8258 }
8259 },
8260 descendants: function descendants(selector) {
8261 var elements = [];
8262
8263 function add(eles) {
8264 for (var i = 0; i < eles.length; i++) {
8265 var ele = eles[i];
8266 elements.push(ele);
8267
8268 if (ele.children().nonempty()) {
8269 add(ele.children());
8270 }
8271 }
8272 }
8273
8274 add(this.children());
8275 return this.spawn(elements, {
8276 unique: true
8277 }).filter(selector);
8278 }
8279};
8280
8281function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8282 var q = [];
8283 var did = new Set$1();
8284 var cy = eles.cy();
8285 var hasCompounds = cy.hasCompoundNodes();
8286
8287 for (var i = 0; i < eles.length; i++) {
8288 var ele = eles[i];
8289
8290 if (includeSelf) {
8291 q.push(ele);
8292 } else if (hasCompounds) {
8293 recursiveStep(q, did, ele);
8294 }
8295 }
8296
8297 while (q.length > 0) {
8298 var _ele = q.shift();
8299
8300 fn(_ele);
8301 did.add(_ele.id());
8302
8303 if (hasCompounds) {
8304 recursiveStep(q, did, _ele);
8305 }
8306 }
8307
8308 return eles;
8309}
8310
8311function addChildren(q, did, ele) {
8312 if (ele.isParent()) {
8313 var children = ele._private.children;
8314
8315 for (var i = 0; i < children.length; i++) {
8316 var child = children[i];
8317
8318 if (!did.has(child.id())) {
8319 q.push(child);
8320 }
8321 }
8322 }
8323} // very efficient version of eles.add( eles.descendants() ).forEach()
8324// for internal use
8325
8326
8327elesfn$g.forEachDown = function (fn) {
8328 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8329 return forEachCompound(this, fn, includeSelf, addChildren);
8330};
8331
8332function addParent(q, did, ele) {
8333 if (ele.isChild()) {
8334 var parent = ele._private.parent;
8335
8336 if (!did.has(parent.id())) {
8337 q.push(parent);
8338 }
8339 }
8340}
8341
8342elesfn$g.forEachUp = function (fn) {
8343 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8344 return forEachCompound(this, fn, includeSelf, addParent);
8345};
8346
8347function addParentAndChildren(q, did, ele) {
8348 addParent(q, did, ele);
8349 addChildren(q, did, ele);
8350}
8351
8352elesfn$g.forEachUpAndDown = function (fn) {
8353 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8354 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8355}; // aliases
8356
8357
8358elesfn$g.ancestors = elesfn$g.parents;
8359
8360var fn$1, elesfn$h;
8361fn$1 = elesfn$h = {
8362 data: define$3.data({
8363 field: 'data',
8364 bindingEvent: 'data',
8365 allowBinding: true,
8366 allowSetting: true,
8367 settingEvent: 'data',
8368 settingTriggersEvent: true,
8369 triggerFnName: 'trigger',
8370 allowGetting: true,
8371 immutableKeys: {
8372 'id': true,
8373 'source': true,
8374 'target': true,
8375 'parent': true
8376 },
8377 updateStyle: true
8378 }),
8379 removeData: define$3.removeData({
8380 field: 'data',
8381 event: 'data',
8382 triggerFnName: 'trigger',
8383 triggerEvent: true,
8384 immutableKeys: {
8385 'id': true,
8386 'source': true,
8387 'target': true,
8388 'parent': true
8389 },
8390 updateStyle: true
8391 }),
8392 scratch: define$3.data({
8393 field: 'scratch',
8394 bindingEvent: 'scratch',
8395 allowBinding: true,
8396 allowSetting: true,
8397 settingEvent: 'scratch',
8398 settingTriggersEvent: true,
8399 triggerFnName: 'trigger',
8400 allowGetting: true,
8401 updateStyle: true
8402 }),
8403 removeScratch: define$3.removeData({
8404 field: 'scratch',
8405 event: 'scratch',
8406 triggerFnName: 'trigger',
8407 triggerEvent: true,
8408 updateStyle: true
8409 }),
8410 rscratch: define$3.data({
8411 field: 'rscratch',
8412 allowBinding: false,
8413 allowSetting: true,
8414 settingTriggersEvent: false,
8415 allowGetting: true
8416 }),
8417 removeRscratch: define$3.removeData({
8418 field: 'rscratch',
8419 triggerEvent: false
8420 }),
8421 id: function id() {
8422 var ele = this[0];
8423
8424 if (ele) {
8425 return ele._private.data.id;
8426 }
8427 }
8428}; // aliases
8429
8430fn$1.attr = fn$1.data;
8431fn$1.removeAttr = fn$1.removeData;
8432var data$1 = elesfn$h;
8433
8434var elesfn$i = {};
8435
8436function defineDegreeFunction(callback) {
8437 return function (includeLoops) {
8438 var self = this;
8439
8440 if (includeLoops === undefined) {
8441 includeLoops = true;
8442 }
8443
8444 if (self.length === 0) {
8445 return;
8446 }
8447
8448 if (self.isNode() && !self.removed()) {
8449 var degree = 0;
8450 var node = self[0];
8451 var connectedEdges = node._private.edges;
8452
8453 for (var i = 0; i < connectedEdges.length; i++) {
8454 var edge = connectedEdges[i];
8455
8456 if (!includeLoops && edge.isLoop()) {
8457 continue;
8458 }
8459
8460 degree += callback(node, edge);
8461 }
8462
8463 return degree;
8464 } else {
8465 return;
8466 }
8467 };
8468}
8469
8470extend(elesfn$i, {
8471 degree: defineDegreeFunction(function (node, edge) {
8472 if (edge.source().same(edge.target())) {
8473 return 2;
8474 } else {
8475 return 1;
8476 }
8477 }),
8478 indegree: defineDegreeFunction(function (node, edge) {
8479 if (edge.target().same(node)) {
8480 return 1;
8481 } else {
8482 return 0;
8483 }
8484 }),
8485 outdegree: defineDegreeFunction(function (node, edge) {
8486 if (edge.source().same(node)) {
8487 return 1;
8488 } else {
8489 return 0;
8490 }
8491 })
8492});
8493
8494function defineDegreeBoundsFunction(degreeFn, callback) {
8495 return function (includeLoops) {
8496 var ret;
8497 var nodes = this.nodes();
8498
8499 for (var i = 0; i < nodes.length; i++) {
8500 var ele = nodes[i];
8501 var degree = ele[degreeFn](includeLoops);
8502
8503 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8504 ret = degree;
8505 }
8506 }
8507
8508 return ret;
8509 };
8510}
8511
8512extend(elesfn$i, {
8513 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8514 return degree < min;
8515 }),
8516 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8517 return degree > max;
8518 }),
8519 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8520 return degree < min;
8521 }),
8522 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8523 return degree > max;
8524 }),
8525 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8526 return degree < min;
8527 }),
8528 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8529 return degree > max;
8530 })
8531});
8532extend(elesfn$i, {
8533 totalDegree: function totalDegree(includeLoops) {
8534 var total = 0;
8535 var nodes = this.nodes();
8536
8537 for (var i = 0; i < nodes.length; i++) {
8538 total += nodes[i].degree(includeLoops);
8539 }
8540
8541 return total;
8542 }
8543});
8544
8545var fn$2, elesfn$j;
8546
8547var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8548 for (var i = 0; i < eles.length; i++) {
8549 var ele = eles[i];
8550
8551 if (!ele.locked()) {
8552 var oldPos = ele._private.position;
8553 var delta = {
8554 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8555 y: newPos.y != null ? newPos.y - oldPos.y : 0
8556 };
8557
8558 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8559 ele.children().shift(delta, silent);
8560 }
8561
8562 ele.shiftCachedBoundingBox(delta);
8563 }
8564 }
8565};
8566
8567var positionDef = {
8568 field: 'position',
8569 bindingEvent: 'position',
8570 allowBinding: true,
8571 allowSetting: true,
8572 settingEvent: 'position',
8573 settingTriggersEvent: true,
8574 triggerFnName: 'emitAndNotify',
8575 allowGetting: true,
8576 validKeys: ['x', 'y'],
8577 beforeGet: function beforeGet(ele) {
8578 ele.updateCompoundBounds();
8579 },
8580 beforeSet: function beforeSet(eles, newPos) {
8581 beforePositionSet(eles, newPos, false);
8582 },
8583 onSet: function onSet(eles) {
8584 eles.dirtyCompoundBoundsCache();
8585 },
8586 canSet: function canSet(ele) {
8587 return !ele.locked();
8588 }
8589};
8590fn$2 = elesfn$j = {
8591 position: define$3.data(positionDef),
8592 // position but no notification to renderer
8593 silentPosition: define$3.data(extend({}, positionDef, {
8594 allowBinding: false,
8595 allowSetting: true,
8596 settingTriggersEvent: false,
8597 allowGetting: false,
8598 beforeSet: function beforeSet(eles, newPos) {
8599 beforePositionSet(eles, newPos, true);
8600 }
8601 })),
8602 positions: function positions(pos, silent) {
8603 if (plainObject(pos)) {
8604 if (silent) {
8605 this.silentPosition(pos);
8606 } else {
8607 this.position(pos);
8608 }
8609 } else if (fn(pos)) {
8610 var _fn = pos;
8611 var cy = this.cy();
8612 cy.startBatch();
8613
8614 for (var i = 0; i < this.length; i++) {
8615 var ele = this[i];
8616
8617 var _pos = void 0;
8618
8619 if (_pos = _fn(ele, i)) {
8620 if (silent) {
8621 ele.silentPosition(_pos);
8622 } else {
8623 ele.position(_pos);
8624 }
8625 }
8626 }
8627
8628 cy.endBatch();
8629 }
8630
8631 return this; // chaining
8632 },
8633 silentPositions: function silentPositions(pos) {
8634 return this.positions(pos, true);
8635 },
8636 shift: function shift(dim, val, silent) {
8637 var delta;
8638
8639 if (plainObject(dim)) {
8640 delta = {
8641 x: number(dim.x) ? dim.x : 0,
8642 y: number(dim.y) ? dim.y : 0
8643 };
8644 silent = val;
8645 } else if (string(dim) && number(val)) {
8646 delta = {
8647 x: 0,
8648 y: 0
8649 };
8650 delta[dim] = val;
8651 }
8652
8653 if (delta != null) {
8654 var cy = this.cy();
8655 cy.startBatch();
8656
8657 for (var i = 0; i < this.length; i++) {
8658 var ele = this[i];
8659 var pos = ele.position();
8660 var newPos = {
8661 x: pos.x + delta.x,
8662 y: pos.y + delta.y
8663 };
8664
8665 if (silent) {
8666 ele.silentPosition(newPos);
8667 } else {
8668 ele.position(newPos);
8669 }
8670 }
8671
8672 cy.endBatch();
8673 }
8674
8675 return this;
8676 },
8677 silentShift: function silentShift(dim, val) {
8678 if (plainObject(dim)) {
8679 this.shift(dim, true);
8680 } else if (string(dim) && number(val)) {
8681 this.shift(dim, val, true);
8682 }
8683
8684 return this;
8685 },
8686 // get/set the rendered (i.e. on screen) positon of the element
8687 renderedPosition: function renderedPosition(dim, val) {
8688 var ele = this[0];
8689 var cy = this.cy();
8690 var zoom = cy.zoom();
8691 var pan = cy.pan();
8692 var rpos = plainObject(dim) ? dim : undefined;
8693 var setting = rpos !== undefined || val !== undefined && string(dim);
8694
8695 if (ele && ele.isNode()) {
8696 // must have an element and must be a node to return position
8697 if (setting) {
8698 for (var i = 0; i < this.length; i++) {
8699 var _ele = this[i];
8700
8701 if (val !== undefined) {
8702 // set one dimension
8703 _ele.position(dim, (val - pan[dim]) / zoom);
8704 } else if (rpos !== undefined) {
8705 // set whole position
8706 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8707 }
8708 }
8709 } else {
8710 // getting
8711 var pos = ele.position();
8712 rpos = modelToRenderedPosition(pos, zoom, pan);
8713
8714 if (dim === undefined) {
8715 // then return the whole rendered position
8716 return rpos;
8717 } else {
8718 // then return the specified dimension
8719 return rpos[dim];
8720 }
8721 }
8722 } else if (!setting) {
8723 return undefined; // for empty collection case
8724 }
8725
8726 return this; // chaining
8727 },
8728 // get/set the position relative to the parent
8729 relativePosition: function relativePosition(dim, val) {
8730 var ele = this[0];
8731 var cy = this.cy();
8732 var ppos = plainObject(dim) ? dim : undefined;
8733 var setting = ppos !== undefined || val !== undefined && string(dim);
8734 var hasCompoundNodes = cy.hasCompoundNodes();
8735
8736 if (ele && ele.isNode()) {
8737 // must have an element and must be a node to return position
8738 if (setting) {
8739 for (var i = 0; i < this.length; i++) {
8740 var _ele2 = this[i];
8741 var parent = hasCompoundNodes ? _ele2.parent() : null;
8742 var hasParent = parent && parent.length > 0;
8743 var relativeToParent = hasParent;
8744
8745 if (hasParent) {
8746 parent = parent[0];
8747 }
8748
8749 var origin = relativeToParent ? parent.position() : {
8750 x: 0,
8751 y: 0
8752 };
8753
8754 if (val !== undefined) {
8755 // set one dimension
8756 _ele2.position(dim, val + origin[dim]);
8757 } else if (ppos !== undefined) {
8758 // set whole position
8759 _ele2.position({
8760 x: ppos.x + origin.x,
8761 y: ppos.y + origin.y
8762 });
8763 }
8764 }
8765 } else {
8766 // getting
8767 var pos = ele.position();
8768
8769 var _parent = hasCompoundNodes ? ele.parent() : null;
8770
8771 var _hasParent = _parent && _parent.length > 0;
8772
8773 var _relativeToParent = _hasParent;
8774
8775 if (_hasParent) {
8776 _parent = _parent[0];
8777 }
8778
8779 var _origin = _relativeToParent ? _parent.position() : {
8780 x: 0,
8781 y: 0
8782 };
8783
8784 ppos = {
8785 x: pos.x - _origin.x,
8786 y: pos.y - _origin.y
8787 };
8788
8789 if (dim === undefined) {
8790 // then return the whole rendered position
8791 return ppos;
8792 } else {
8793 // then return the specified dimension
8794 return ppos[dim];
8795 }
8796 }
8797 } else if (!setting) {
8798 return undefined; // for empty collection case
8799 }
8800
8801 return this; // chaining
8802 }
8803}; // aliases
8804
8805fn$2.modelPosition = fn$2.point = fn$2.position;
8806fn$2.modelPositions = fn$2.points = fn$2.positions;
8807fn$2.renderedPoint = fn$2.renderedPosition;
8808fn$2.relativePoint = fn$2.relativePosition;
8809var position = elesfn$j;
8810
8811var fn$3, elesfn$k;
8812fn$3 = elesfn$k = {};
8813
8814elesfn$k.renderedBoundingBox = function (options) {
8815 var bb = this.boundingBox(options);
8816 var cy = this.cy();
8817 var zoom = cy.zoom();
8818 var pan = cy.pan();
8819 var x1 = bb.x1 * zoom + pan.x;
8820 var x2 = bb.x2 * zoom + pan.x;
8821 var y1 = bb.y1 * zoom + pan.y;
8822 var y2 = bb.y2 * zoom + pan.y;
8823 return {
8824 x1: x1,
8825 x2: x2,
8826 y1: y1,
8827 y2: y2,
8828 w: x2 - x1,
8829 h: y2 - y1
8830 };
8831};
8832
8833elesfn$k.dirtyCompoundBoundsCache = function () {
8834 var cy = this.cy();
8835
8836 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
8837 return this;
8838 }
8839
8840 this.forEachUp(function (ele) {
8841 if (ele.isParent()) {
8842 var _p = ele._private;
8843 _p.compoundBoundsClean = false;
8844 _p.bbCache = null;
8845 ele.emitAndNotify('bounds');
8846 }
8847 });
8848 return this;
8849};
8850
8851elesfn$k.updateCompoundBounds = function () {
8852 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
8853 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
8854
8855 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
8856 return this;
8857 } // save cycles when batching -- but bounds will be stale (or not exist yet)
8858
8859
8860 if (!force && cy.batching()) {
8861 return this;
8862 }
8863
8864 function update(parent) {
8865 if (!parent.isParent()) {
8866 return;
8867 }
8868
8869 var _p = parent._private;
8870 var children = parent.children();
8871 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
8872 var min = {
8873 width: {
8874 val: parent.pstyle('min-width').pfValue,
8875 left: parent.pstyle('min-width-bias-left'),
8876 right: parent.pstyle('min-width-bias-right')
8877 },
8878 height: {
8879 val: parent.pstyle('min-height').pfValue,
8880 top: parent.pstyle('min-height-bias-top'),
8881 bottom: parent.pstyle('min-height-bias-bottom')
8882 }
8883 };
8884 var bb = children.boundingBox({
8885 includeLabels: includeLabels,
8886 includeOverlays: false,
8887 // updating the compound bounds happens outside of the regular
8888 // cache cycle (i.e. before fired events)
8889 useCache: false
8890 });
8891 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
8892
8893 if (bb.w === 0 || bb.h === 0) {
8894 bb = {
8895 w: parent.pstyle('width').pfValue,
8896 h: parent.pstyle('height').pfValue
8897 };
8898 bb.x1 = pos.x - bb.w / 2;
8899 bb.x2 = pos.x + bb.w / 2;
8900 bb.y1 = pos.y - bb.h / 2;
8901 bb.y2 = pos.y + bb.h / 2;
8902 }
8903
8904 function computeBiasValues(propDiff, propBias, propBiasComplement) {
8905 var biasDiff = 0;
8906 var biasComplementDiff = 0;
8907 var biasTotal = propBias + propBiasComplement;
8908
8909 if (propDiff > 0 && biasTotal > 0) {
8910 biasDiff = propBias / biasTotal * propDiff;
8911 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
8912 }
8913
8914 return {
8915 biasDiff: biasDiff,
8916 biasComplementDiff: biasComplementDiff
8917 };
8918 }
8919
8920 function computePaddingValues(width, height, paddingObject, relativeTo) {
8921 // Assuming percentage is number from 0 to 1
8922 if (paddingObject.units === '%') {
8923 switch (relativeTo) {
8924 case 'width':
8925 return width > 0 ? paddingObject.pfValue * width : 0;
8926
8927 case 'height':
8928 return height > 0 ? paddingObject.pfValue * height : 0;
8929
8930 case 'average':
8931 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
8932
8933 case 'min':
8934 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
8935
8936 case 'max':
8937 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
8938
8939 default:
8940 return 0;
8941 }
8942 } else if (paddingObject.units === 'px') {
8943 return paddingObject.pfValue;
8944 } else {
8945 return 0;
8946 }
8947 }
8948
8949 var leftVal = min.width.left.value;
8950
8951 if (min.width.left.units === 'px' && min.width.val > 0) {
8952 leftVal = leftVal * 100 / min.width.val;
8953 }
8954
8955 var rightVal = min.width.right.value;
8956
8957 if (min.width.right.units === 'px' && min.width.val > 0) {
8958 rightVal = rightVal * 100 / min.width.val;
8959 }
8960
8961 var topVal = min.height.top.value;
8962
8963 if (min.height.top.units === 'px' && min.height.val > 0) {
8964 topVal = topVal * 100 / min.height.val;
8965 }
8966
8967 var bottomVal = min.height.bottom.value;
8968
8969 if (min.height.bottom.units === 'px' && min.height.val > 0) {
8970 bottomVal = bottomVal * 100 / min.height.val;
8971 }
8972
8973 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
8974 var diffLeft = widthBiasDiffs.biasDiff;
8975 var diffRight = widthBiasDiffs.biasComplementDiff;
8976 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
8977 var diffTop = heightBiasDiffs.biasDiff;
8978 var diffBottom = heightBiasDiffs.biasComplementDiff;
8979 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
8980 _p.autoWidth = Math.max(bb.w, min.width.val);
8981 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
8982 _p.autoHeight = Math.max(bb.h, min.height.val);
8983 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
8984 }
8985
8986 for (var i = 0; i < this.length; i++) {
8987 var ele = this[i];
8988 var _p = ele._private;
8989
8990 if (!_p.compoundBoundsClean) {
8991 update(ele);
8992
8993 if (!cy.batching()) {
8994 _p.compoundBoundsClean = true;
8995 }
8996 }
8997 }
8998
8999 return this;
9000};
9001
9002var noninf = function noninf(x) {
9003 if (x === Infinity || x === -Infinity) {
9004 return 0;
9005 }
9006
9007 return x;
9008};
9009
9010var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9011 // don't update with zero area boxes
9012 if (x2 - x1 === 0 || y2 - y1 === 0) {
9013 return;
9014 } // don't update with null dim
9015
9016
9017 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9018 return;
9019 }
9020
9021 b.x1 = x1 < b.x1 ? x1 : b.x1;
9022 b.x2 = x2 > b.x2 ? x2 : b.x2;
9023 b.y1 = y1 < b.y1 ? y1 : b.y1;
9024 b.y2 = y2 > b.y2 ? y2 : b.y2;
9025 b.w = b.x2 - b.x1;
9026 b.h = b.y2 - b.y1;
9027};
9028
9029var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9030 if (b2 == null) {
9031 return b;
9032 }
9033
9034 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9035};
9036
9037var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9038 return getPrefixedProperty(obj, field, prefix);
9039};
9040
9041var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9042 if (ele.cy().headless()) {
9043 return;
9044 }
9045
9046 var _p = ele._private;
9047 var rstyle = _p.rstyle;
9048 var halfArW = rstyle.arrowWidth / 2;
9049 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9050 var x;
9051 var y;
9052
9053 if (arrowType !== 'none') {
9054 if (prefix === 'source') {
9055 x = rstyle.srcX;
9056 y = rstyle.srcY;
9057 } else if (prefix === 'target') {
9058 x = rstyle.tgtX;
9059 y = rstyle.tgtY;
9060 } else {
9061 x = rstyle.midX;
9062 y = rstyle.midY;
9063 } // always store the individual arrow bounds
9064
9065
9066 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9067 var bb = bbs[prefix] = bbs[prefix] || {};
9068 bb.x1 = x - halfArW;
9069 bb.y1 = y - halfArW;
9070 bb.x2 = x + halfArW;
9071 bb.y2 = y + halfArW;
9072 bb.w = bb.x2 - bb.x1;
9073 bb.h = bb.y2 - bb.y1;
9074 expandBoundingBox(bb, 1);
9075 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9076 }
9077};
9078
9079var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9080 if (ele.cy().headless()) {
9081 return;
9082 }
9083
9084 var prefixDash;
9085
9086 if (prefix) {
9087 prefixDash = prefix + '-';
9088 } else {
9089 prefixDash = '';
9090 }
9091
9092 var _p = ele._private;
9093 var rstyle = _p.rstyle;
9094 var label = ele.pstyle(prefixDash + 'label').strValue;
9095
9096 if (label) {
9097 var halign = ele.pstyle('text-halign');
9098 var valign = ele.pstyle('text-valign');
9099 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9100 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9101 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9102 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9103 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9104 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9105 var isEdge = ele.isEdge();
9106 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9107 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9108 var borderWidth = ele.pstyle('text-border-width').pfValue;
9109 var halfBorderWidth = borderWidth / 2;
9110 var padding = ele.pstyle('text-background-padding').pfValue;
9111 var lh = labelHeight;
9112 var lw = labelWidth;
9113 var lw_2 = lw / 2;
9114 var lh_2 = lh / 2;
9115 var lx1, lx2, ly1, ly2;
9116
9117 if (isEdge) {
9118 lx1 = labelX - lw_2;
9119 lx2 = labelX + lw_2;
9120 ly1 = labelY - lh_2;
9121 ly2 = labelY + lh_2;
9122 } else {
9123 switch (halign.value) {
9124 case 'left':
9125 lx1 = labelX - lw;
9126 lx2 = labelX;
9127 break;
9128
9129 case 'center':
9130 lx1 = labelX - lw_2;
9131 lx2 = labelX + lw_2;
9132 break;
9133
9134 case 'right':
9135 lx1 = labelX;
9136 lx2 = labelX + lw;
9137 break;
9138 }
9139
9140 switch (valign.value) {
9141 case 'top':
9142 ly1 = labelY - lh;
9143 ly2 = labelY;
9144 break;
9145
9146 case 'center':
9147 ly1 = labelY - lh_2;
9148 ly2 = labelY + lh_2;
9149 break;
9150
9151 case 'bottom':
9152 ly1 = labelY;
9153 ly2 = labelY + lh;
9154 break;
9155 }
9156 } // shift by margin and expand by outline and border
9157
9158
9159 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
9160 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
9161 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
9162 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
9163
9164 var bbPrefix = prefix || 'main';
9165 var bbs = _p.labelBounds;
9166 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9167 bb.x1 = lx1;
9168 bb.y1 = ly1;
9169 bb.x2 = lx2;
9170 bb.y2 = ly2;
9171 bb.w = lx2 - lx1;
9172 bb.h = ly2 - ly1;
9173 expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
9174
9175 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9176 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9177
9178 if (isAutorotate || isPfValue) {
9179 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9180 var cos = Math.cos(theta);
9181 var sin = Math.sin(theta); // rotation point (default value for center-center)
9182
9183 var xo = (lx1 + lx2) / 2;
9184 var yo = (ly1 + ly2) / 2;
9185
9186 if (!isEdge) {
9187 switch (halign.value) {
9188 case 'left':
9189 xo = lx2;
9190 break;
9191
9192 case 'right':
9193 xo = lx1;
9194 break;
9195 }
9196
9197 switch (valign.value) {
9198 case 'top':
9199 yo = ly2;
9200 break;
9201
9202 case 'bottom':
9203 yo = ly1;
9204 break;
9205 }
9206 }
9207
9208 var rotate = function rotate(x, y) {
9209 x = x - xo;
9210 y = y - yo;
9211 return {
9212 x: x * cos - y * sin + xo,
9213 y: x * sin + y * cos + yo
9214 };
9215 };
9216
9217 var px1y1 = rotate(lx1, ly1);
9218 var px1y2 = rotate(lx1, ly2);
9219 var px2y1 = rotate(lx2, ly1);
9220 var px2y2 = rotate(lx2, ly2);
9221 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9222 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9223 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9224 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9225 }
9226
9227 var bbPrefixRot = bbPrefix + 'Rot';
9228 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9229 bbRot.x1 = lx1;
9230 bbRot.y1 = ly1;
9231 bbRot.x2 = lx2;
9232 bbRot.y2 = ly2;
9233 bbRot.w = lx2 - lx1;
9234 bbRot.h = ly2 - ly1;
9235 updateBounds(bounds, lx1, ly1, lx2, ly2);
9236 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9237 }
9238
9239 return bounds;
9240}; // get the bounding box of the elements (in raw model position)
9241
9242
9243var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9244 var cy = ele._private.cy;
9245 var styleEnabled = cy.styleEnabled();
9246 var headless = cy.headless();
9247 var bounds = makeBoundingBox();
9248 var _p = ele._private;
9249 var isNode = ele.isNode();
9250 var isEdge = ele.isEdge();
9251 var ex1, ex2, ey1, ey2; // extrema of body / lines
9252
9253 var x, y; // node pos
9254
9255 var rstyle = _p.rstyle;
9256 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9257 // (other factors like width values will be considered later in this function anyway)
9258
9259 var isDisplayed = function isDisplayed(ele) {
9260 return ele.pstyle('display').value !== 'none';
9261 };
9262
9263 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9264 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9265
9266 if (displayed) {
9267 // displayed suffices, since we will find zero area eles anyway
9268 var overlayOpacity = 0;
9269 var overlayPadding = 0;
9270
9271 if (styleEnabled && options.includeOverlays) {
9272 overlayOpacity = ele.pstyle('overlay-opacity').value;
9273
9274 if (overlayOpacity !== 0) {
9275 overlayPadding = ele.pstyle('overlay-padding').value;
9276 }
9277 }
9278
9279 var w = 0;
9280 var wHalf = 0;
9281
9282 if (styleEnabled) {
9283 w = ele.pstyle('width').pfValue;
9284 wHalf = w / 2;
9285 }
9286
9287 if (isNode && options.includeNodes) {
9288 var pos = ele.position();
9289 x = pos.x;
9290 y = pos.y;
9291
9292 var _w = ele.outerWidth();
9293
9294 var halfW = _w / 2;
9295 var h = ele.outerHeight();
9296 var halfH = h / 2; // handle node dimensions
9297 /////////////////////////
9298
9299 ex1 = x - halfW;
9300 ex2 = x + halfW;
9301 ey1 = y - halfH;
9302 ey2 = y + halfH;
9303 updateBounds(bounds, ex1, ey1, ex2, ey2);
9304 } else if (isEdge && options.includeEdges) {
9305 if (styleEnabled && !headless) {
9306 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9307 //////////////////////////////////////////////
9308
9309 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9310 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9311 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9312 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9313
9314 ex1 -= wHalf;
9315 ex2 += wHalf;
9316 ey1 -= wHalf;
9317 ey2 += wHalf;
9318 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9319 ////////////////
9320
9321 if (curveStyle === 'haystack') {
9322 var hpts = rstyle.haystackPts;
9323
9324 if (hpts && hpts.length === 2) {
9325 ex1 = hpts[0].x;
9326 ey1 = hpts[0].y;
9327 ex2 = hpts[1].x;
9328 ey2 = hpts[1].y;
9329
9330 if (ex1 > ex2) {
9331 var temp = ex1;
9332 ex1 = ex2;
9333 ex2 = temp;
9334 }
9335
9336 if (ey1 > ey2) {
9337 var _temp = ey1;
9338 ey1 = ey2;
9339 ey2 = _temp;
9340 }
9341
9342 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9343 }
9344 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9345 var pts;
9346
9347 switch (curveStyle) {
9348 case 'bezier':
9349 case 'unbundled-bezier':
9350 pts = rstyle.bezierPts;
9351 break;
9352
9353 case 'segments':
9354 case 'taxi':
9355 pts = rstyle.linePts;
9356 break;
9357 }
9358
9359 if (pts != null) {
9360 for (var j = 0; j < pts.length; j++) {
9361 var pt = pts[j];
9362 ex1 = pt.x - wHalf;
9363 ex2 = pt.x + wHalf;
9364 ey1 = pt.y - wHalf;
9365 ey2 = pt.y + wHalf;
9366 updateBounds(bounds, ex1, ey1, ex2, ey2);
9367 }
9368 }
9369 } // bezier-like or segment-like edge
9370
9371 } else {
9372 // headless or style disabled
9373 // fallback on source and target positions
9374 //////////////////////////////////////////
9375 var n1 = ele.source();
9376 var n1pos = n1.position();
9377 var n2 = ele.target();
9378 var n2pos = n2.position();
9379 ex1 = n1pos.x;
9380 ex2 = n2pos.x;
9381 ey1 = n1pos.y;
9382 ey2 = n2pos.y;
9383
9384 if (ex1 > ex2) {
9385 var _temp2 = ex1;
9386 ex1 = ex2;
9387 ex2 = _temp2;
9388 }
9389
9390 if (ey1 > ey2) {
9391 var _temp3 = ey1;
9392 ey1 = ey2;
9393 ey2 = _temp3;
9394 } // take into account edge width
9395
9396
9397 ex1 -= wHalf;
9398 ex2 += wHalf;
9399 ey1 -= wHalf;
9400 ey2 += wHalf;
9401 updateBounds(bounds, ex1, ey1, ex2, ey2);
9402 } // headless or style disabled
9403
9404 } // edges
9405 // handle edge arrow size
9406 /////////////////////////
9407
9408
9409 if (styleEnabled && options.includeEdges && isEdge) {
9410 updateBoundsFromArrow(bounds, ele, 'mid-source');
9411 updateBoundsFromArrow(bounds, ele, 'mid-target');
9412 updateBoundsFromArrow(bounds, ele, 'source');
9413 updateBoundsFromArrow(bounds, ele, 'target');
9414 } // ghost
9415 ////////
9416
9417
9418 if (styleEnabled) {
9419 var ghost = ele.pstyle('ghost').value === 'yes';
9420
9421 if (ghost) {
9422 var gx = ele.pstyle('ghost-offset-x').pfValue;
9423 var gy = ele.pstyle('ghost-offset-y').pfValue;
9424 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9425 }
9426 } // always store the body bounds separately from the labels
9427
9428
9429 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9430 assignBoundingBox(bbBody, bounds);
9431 expandBoundingBoxSides(bbBody, manualExpansion);
9432 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9433 // overlay
9434 //////////
9435
9436 if (styleEnabled) {
9437 ex1 = bounds.x1;
9438 ex2 = bounds.x2;
9439 ey1 = bounds.y1;
9440 ey2 = bounds.y2;
9441 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
9442 } // always store the body bounds separately from the labels
9443
9444
9445 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9446 assignBoundingBox(bbOverlay, bounds);
9447 expandBoundingBoxSides(bbOverlay, manualExpansion);
9448 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9449 // handle label dimensions
9450 //////////////////////////
9451
9452 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9453
9454 if (bbLabels.all != null) {
9455 clearBoundingBox(bbLabels.all);
9456 } else {
9457 bbLabels.all = makeBoundingBox();
9458 }
9459
9460 if (styleEnabled && options.includeLabels) {
9461 if (options.includeMainLabels) {
9462 updateBoundsFromLabel(bounds, ele, null);
9463 }
9464
9465 if (isEdge) {
9466 if (options.includeSourceLabels) {
9467 updateBoundsFromLabel(bounds, ele, 'source');
9468 }
9469
9470 if (options.includeTargetLabels) {
9471 updateBoundsFromLabel(bounds, ele, 'target');
9472 }
9473 }
9474 } // style enabled for labels
9475
9476 } // if displayed
9477
9478
9479 bounds.x1 = noninf(bounds.x1);
9480 bounds.y1 = noninf(bounds.y1);
9481 bounds.x2 = noninf(bounds.x2);
9482 bounds.y2 = noninf(bounds.y2);
9483 bounds.w = noninf(bounds.x2 - bounds.x1);
9484 bounds.h = noninf(bounds.y2 - bounds.y1);
9485
9486 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9487 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9488
9489 expandBoundingBox(bounds, 1);
9490 }
9491
9492 return bounds;
9493};
9494
9495var getKey = function getKey(opts) {
9496 var i = 0;
9497
9498 var tf = function tf(val) {
9499 return (val ? 1 : 0) << i++;
9500 };
9501
9502 var key = 0;
9503 key += tf(opts.incudeNodes);
9504 key += tf(opts.includeEdges);
9505 key += tf(opts.includeLabels);
9506 key += tf(opts.includeMainLabels);
9507 key += tf(opts.includeSourceLabels);
9508 key += tf(opts.includeTargetLabels);
9509 key += tf(opts.includeOverlays);
9510 return key;
9511};
9512
9513var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9514 if (ele.isEdge()) {
9515 var p1 = ele.source().position();
9516 var p2 = ele.target().position();
9517
9518 var r = function r(x) {
9519 return Math.round(x);
9520 };
9521
9522 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9523 } else {
9524 return 0;
9525 }
9526};
9527
9528var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9529 var _p = ele._private;
9530 var bb;
9531 var isEdge = ele.isEdge();
9532 var key = opts == null ? defBbOptsKey : getKey(opts);
9533 var usingDefOpts = key === defBbOptsKey;
9534 var currPosKey = getBoundingBoxPosKey(ele);
9535 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9536 var useCache = opts.useCache && isPosKeySame;
9537 var needRecalc = !useCache || _p.bbCache == null;
9538
9539 if (needRecalc) {
9540 if (!isPosKeySame) {
9541 ele.recalculateRenderedStyle();
9542 }
9543
9544 bb = boundingBoxImpl(ele, defBbOpts);
9545 _p.bbCache = bb;
9546 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9547 _p.bbCachePosKey = currPosKey;
9548 } else {
9549 bb = _p.bbCache;
9550 }
9551
9552 if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
9553 var shift = assignShiftToBoundingBox;
9554 var delta = _p.bbCacheShift;
9555
9556 var safeShift = function safeShift(bb, delta) {
9557 if (bb != null) {
9558 shift(bb, delta);
9559 }
9560 };
9561
9562 shift(bb, delta);
9563 var bodyBounds = _p.bodyBounds,
9564 overlayBounds = _p.overlayBounds,
9565 labelBounds = _p.labelBounds,
9566 arrowBounds = _p.arrowBounds;
9567 safeShift(bodyBounds, delta);
9568 safeShift(overlayBounds, delta);
9569
9570 if (arrowBounds != null) {
9571 safeShift(arrowBounds.source, delta);
9572 safeShift(arrowBounds.target, delta);
9573 safeShift(arrowBounds['mid-source'], delta);
9574 safeShift(arrowBounds['mid-target'], delta);
9575 }
9576
9577 if (labelBounds != null) {
9578 safeShift(labelBounds.main, delta);
9579 safeShift(labelBounds.all, delta);
9580 safeShift(labelBounds.source, delta);
9581 safeShift(labelBounds.target, delta);
9582 }
9583 } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
9584
9585
9586 _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
9587
9588 if (!usingDefOpts) {
9589 var isNode = ele.isNode();
9590 bb = makeBoundingBox();
9591
9592 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9593 if (opts.includeOverlays) {
9594 updateBoundsFromBox(bb, _p.overlayBounds);
9595 } else {
9596 updateBoundsFromBox(bb, _p.bodyBounds);
9597 }
9598 }
9599
9600 if (opts.includeLabels) {
9601 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9602 updateBoundsFromBox(bb, _p.labelBounds.all);
9603 } else {
9604 if (opts.includeMainLabels) {
9605 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9606 }
9607
9608 if (opts.includeSourceLabels) {
9609 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9610 }
9611
9612 if (opts.includeTargetLabels) {
9613 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9614 }
9615 }
9616 }
9617
9618 bb.w = bb.x2 - bb.x1;
9619 bb.h = bb.y2 - bb.y1;
9620 }
9621
9622 return bb;
9623};
9624
9625var defBbOpts = {
9626 includeNodes: true,
9627 includeEdges: true,
9628 includeLabels: true,
9629 includeMainLabels: true,
9630 includeSourceLabels: true,
9631 includeTargetLabels: true,
9632 includeOverlays: true,
9633 useCache: true
9634};
9635var defBbOptsKey = getKey(defBbOpts);
9636var filledBbOpts = defaults(defBbOpts);
9637
9638elesfn$k.boundingBox = function (options) {
9639 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9640 // specified s.t. the cache is used, so check for this case to make it faster by
9641 // avoiding the overhead of the rest of the function
9642
9643 if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9644 if (options === undefined) {
9645 options = defBbOpts;
9646 } else {
9647 options = filledBbOpts(options);
9648 }
9649
9650 bounds = cachedBoundingBoxImpl(this[0], options);
9651 } else {
9652 bounds = makeBoundingBox();
9653 options = options || defBbOpts;
9654 var opts = filledBbOpts(options);
9655 var eles = this;
9656 var cy = eles.cy();
9657 var styleEnabled = cy.styleEnabled();
9658
9659 if (styleEnabled) {
9660 for (var i = 0; i < eles.length; i++) {
9661 var ele = eles[i];
9662 var _p = ele._private;
9663 var currPosKey = getBoundingBoxPosKey(ele);
9664 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9665 var useCache = opts.useCache && isPosKeySame;
9666 ele.recalculateRenderedStyle(useCache);
9667 }
9668 }
9669
9670 this.updateCompoundBounds();
9671
9672 for (var _i = 0; _i < eles.length; _i++) {
9673 var _ele = eles[_i];
9674 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9675 }
9676 }
9677
9678 bounds.x1 = noninf(bounds.x1);
9679 bounds.y1 = noninf(bounds.y1);
9680 bounds.x2 = noninf(bounds.x2);
9681 bounds.y2 = noninf(bounds.y2);
9682 bounds.w = noninf(bounds.x2 - bounds.x1);
9683 bounds.h = noninf(bounds.y2 - bounds.y1);
9684 return bounds;
9685};
9686
9687elesfn$k.dirtyBoundingBoxCache = function () {
9688 for (var i = 0; i < this.length; i++) {
9689 var _p = this[i]._private;
9690 _p.bbCache = null;
9691 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9692 _p.bbCachePosKey = null;
9693 _p.bodyBounds = null;
9694 _p.overlayBounds = null;
9695 _p.labelBounds.all = null;
9696 _p.labelBounds.source = null;
9697 _p.labelBounds.target = null;
9698 _p.labelBounds.main = null;
9699 _p.labelBounds.sourceRot = null;
9700 _p.labelBounds.targetRot = null;
9701 _p.labelBounds.mainRot = null;
9702 _p.arrowBounds.source = null;
9703 _p.arrowBounds.target = null;
9704 _p.arrowBounds['mid-source'] = null;
9705 _p.arrowBounds['mid-target'] = null;
9706 }
9707
9708 this.emitAndNotify('bounds');
9709 return this;
9710};
9711
9712elesfn$k.shiftCachedBoundingBox = function (delta) {
9713 for (var i = 0; i < this.length; i++) {
9714 var ele = this[i];
9715 var _p = ele._private;
9716 var bb = _p.bbCache;
9717
9718 if (bb != null) {
9719 _p.bbCacheShift.x += delta.x;
9720 _p.bbCacheShift.y += delta.y;
9721 }
9722 }
9723
9724 this.emitAndNotify('bounds');
9725 return this;
9726}; // private helper to get bounding box for custom node positions
9727// - good for perf in certain cases but currently requires dirtying the rendered style
9728// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9729// - try to use for only things like discrete layouts where the node position would change anyway
9730
9731
9732elesfn$k.boundingBoxAt = function (fn) {
9733 var nodes = this.nodes();
9734 var cy = this.cy();
9735 var hasCompoundNodes = cy.hasCompoundNodes();
9736
9737 if (hasCompoundNodes) {
9738 nodes = nodes.filter(function (node) {
9739 return !node.isParent();
9740 });
9741 }
9742
9743 if (plainObject(fn)) {
9744 var obj = fn;
9745
9746 fn = function fn() {
9747 return obj;
9748 };
9749 }
9750
9751 var storeOldPos = function storeOldPos(node, i) {
9752 return node._private.bbAtOldPos = fn(node, i);
9753 };
9754
9755 var getOldPos = function getOldPos(node) {
9756 return node._private.bbAtOldPos;
9757 };
9758
9759 cy.startBatch();
9760 nodes.forEach(storeOldPos).silentPositions(fn);
9761
9762 if (hasCompoundNodes) {
9763 this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9764 }
9765
9766 var bb = copyBoundingBox(this.boundingBox({
9767 useCache: false
9768 }));
9769 nodes.silentPositions(getOldPos);
9770 cy.endBatch();
9771 return bb;
9772};
9773
9774fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
9775fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
9776var bounds = elesfn$k;
9777
9778var fn$4, elesfn$l;
9779fn$4 = elesfn$l = {};
9780
9781var defineDimFns = function defineDimFns(opts) {
9782 opts.uppercaseName = capitalize(opts.name);
9783 opts.autoName = 'auto' + opts.uppercaseName;
9784 opts.labelName = 'label' + opts.uppercaseName;
9785 opts.outerName = 'outer' + opts.uppercaseName;
9786 opts.uppercaseOuterName = capitalize(opts.outerName);
9787
9788 fn$4[opts.name] = function dimImpl() {
9789 var ele = this[0];
9790 var _p = ele._private;
9791 var cy = _p.cy;
9792 var styleEnabled = cy._private.styleEnabled;
9793
9794 if (ele) {
9795 if (styleEnabled) {
9796 if (ele.isParent()) {
9797 ele.updateCompoundBounds();
9798 return _p[opts.autoName] || 0;
9799 }
9800
9801 var d = ele.pstyle(opts.name);
9802
9803 switch (d.strValue) {
9804 case 'label':
9805 ele.recalculateRenderedStyle();
9806 return _p.rstyle[opts.labelName] || 0;
9807
9808 default:
9809 return d.pfValue;
9810 }
9811 } else {
9812 return 1;
9813 }
9814 }
9815 };
9816
9817 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
9818 var ele = this[0];
9819 var _p = ele._private;
9820 var cy = _p.cy;
9821 var styleEnabled = cy._private.styleEnabled;
9822
9823 if (ele) {
9824 if (styleEnabled) {
9825 var dim = ele[opts.name]();
9826 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
9827
9828 var padding = 2 * ele.padding();
9829 return dim + border + padding;
9830 } else {
9831 return 1;
9832 }
9833 }
9834 };
9835
9836 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
9837 var ele = this[0];
9838
9839 if (ele) {
9840 var d = ele[opts.name]();
9841 return d * this.cy().zoom();
9842 }
9843 };
9844
9845 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
9846 var ele = this[0];
9847
9848 if (ele) {
9849 var od = ele[opts.outerName]();
9850 return od * this.cy().zoom();
9851 }
9852 };
9853};
9854
9855defineDimFns({
9856 name: 'width'
9857});
9858defineDimFns({
9859 name: 'height'
9860});
9861
9862elesfn$l.padding = function () {
9863 var ele = this[0];
9864 var _p = ele._private;
9865
9866 if (ele.isParent()) {
9867 ele.updateCompoundBounds();
9868
9869 if (_p.autoPadding !== undefined) {
9870 return _p.autoPadding;
9871 } else {
9872 return ele.pstyle('padding').pfValue;
9873 }
9874 } else {
9875 return ele.pstyle('padding').pfValue;
9876 }
9877};
9878
9879elesfn$l.paddedHeight = function () {
9880 var ele = this[0];
9881 return ele.height() + 2 * ele.padding();
9882};
9883
9884elesfn$l.paddedWidth = function () {
9885 var ele = this[0];
9886 return ele.width() + 2 * ele.padding();
9887};
9888
9889var widthHeight = elesfn$l;
9890
9891var ifEdge = function ifEdge(ele, getValue) {
9892 if (ele.isEdge()) {
9893 return getValue(ele);
9894 }
9895};
9896
9897var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
9898 if (ele.isEdge()) {
9899 var cy = ele.cy();
9900 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
9901 }
9902};
9903
9904var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
9905 if (ele.isEdge()) {
9906 var cy = ele.cy();
9907 var pan = cy.pan();
9908 var zoom = cy.zoom();
9909 return getPoints(ele).map(function (p) {
9910 return modelToRenderedPosition(p, zoom, pan);
9911 });
9912 }
9913};
9914
9915var controlPoints = function controlPoints(ele) {
9916 return ele.renderer().getControlPoints(ele);
9917};
9918
9919var segmentPoints = function segmentPoints(ele) {
9920 return ele.renderer().getSegmentPoints(ele);
9921};
9922
9923var sourceEndpoint = function sourceEndpoint(ele) {
9924 return ele.renderer().getSourceEndpoint(ele);
9925};
9926
9927var targetEndpoint = function targetEndpoint(ele) {
9928 return ele.renderer().getTargetEndpoint(ele);
9929};
9930
9931var midpoint = function midpoint(ele) {
9932 return ele.renderer().getEdgeMidpoint(ele);
9933};
9934
9935var pts = {
9936 controlPoints: {
9937 get: controlPoints,
9938 mult: true
9939 },
9940 segmentPoints: {
9941 get: segmentPoints,
9942 mult: true
9943 },
9944 sourceEndpoint: {
9945 get: sourceEndpoint
9946 },
9947 targetEndpoint: {
9948 get: targetEndpoint
9949 },
9950 midpoint: {
9951 get: midpoint
9952 }
9953};
9954
9955var renderedName = function renderedName(name) {
9956 return 'rendered' + name[0].toUpperCase() + name.substr(1);
9957};
9958
9959var edgePoints = Object.keys(pts).reduce(function (obj, name) {
9960 var spec = pts[name];
9961 var rName = renderedName(name);
9962
9963 obj[name] = function () {
9964 return ifEdge(this, spec.get);
9965 };
9966
9967 if (spec.mult) {
9968 obj[rName] = function () {
9969 return ifEdgeRenderedPositions(this, spec.get);
9970 };
9971 } else {
9972 obj[rName] = function () {
9973 return ifEdgeRenderedPosition(this, spec.get);
9974 };
9975 }
9976
9977 return obj;
9978}, {});
9979
9980var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
9981
9982/*!
9983Event object based on jQuery events, MIT license
9984
9985https://jquery.org/license/
9986https://tldrlegal.com/license/mit-license
9987https://github.com/jquery/jquery/blob/master/src/event.js
9988*/
9989var Event = function Event(src, props) {
9990 this.recycle(src, props);
9991};
9992
9993function returnFalse() {
9994 return false;
9995}
9996
9997function returnTrue() {
9998 return true;
9999} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10000
10001
10002Event.prototype = {
10003 instanceString: function instanceString() {
10004 return 'event';
10005 },
10006 recycle: function recycle(src, props) {
10007 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10008
10009 if (src != null && src.preventDefault) {
10010 // Browser Event object
10011 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10012 // by a handler lower down the tree; reflect the correct value.
10013
10014 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10015 } else if (src != null && src.type) {
10016 // Plain object containing all event details
10017 props = src;
10018 } else {
10019 // Event string
10020 this.type = src;
10021 } // Put explicitly provided properties onto the event object
10022
10023
10024 if (props != null) {
10025 // more efficient to manually copy fields we use
10026 this.originalEvent = props.originalEvent;
10027 this.type = props.type != null ? props.type : this.type;
10028 this.cy = props.cy;
10029 this.target = props.target;
10030 this.position = props.position;
10031 this.renderedPosition = props.renderedPosition;
10032 this.namespace = props.namespace;
10033 this.layout = props.layout;
10034 }
10035
10036 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10037 // create a rendered position based on the passed position
10038 var pos = this.position;
10039 var zoom = this.cy.zoom();
10040 var pan = this.cy.pan();
10041 this.renderedPosition = {
10042 x: pos.x * zoom + pan.x,
10043 y: pos.y * zoom + pan.y
10044 };
10045 } // Create a timestamp if incoming event doesn't have one
10046
10047
10048 this.timeStamp = src && src.timeStamp || Date.now();
10049 },
10050 preventDefault: function preventDefault() {
10051 this.isDefaultPrevented = returnTrue;
10052 var e = this.originalEvent;
10053
10054 if (!e) {
10055 return;
10056 } // if preventDefault exists run it on the original event
10057
10058
10059 if (e.preventDefault) {
10060 e.preventDefault();
10061 }
10062 },
10063 stopPropagation: function stopPropagation() {
10064 this.isPropagationStopped = returnTrue;
10065 var e = this.originalEvent;
10066
10067 if (!e) {
10068 return;
10069 } // if stopPropagation exists run it on the original event
10070
10071
10072 if (e.stopPropagation) {
10073 e.stopPropagation();
10074 }
10075 },
10076 stopImmediatePropagation: function stopImmediatePropagation() {
10077 this.isImmediatePropagationStopped = returnTrue;
10078 this.stopPropagation();
10079 },
10080 isDefaultPrevented: returnFalse,
10081 isPropagationStopped: returnFalse,
10082 isImmediatePropagationStopped: returnFalse
10083};
10084
10085var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10086
10087var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10088
10089var defaults$8 = {
10090 qualifierCompare: function qualifierCompare(q1, q2) {
10091 return q1 === q2;
10092 },
10093 eventMatches: function eventMatches()
10094 /*context, listener, eventObj*/
10095 {
10096 return true;
10097 },
10098 addEventFields: function addEventFields()
10099 /*context, evt*/
10100 {},
10101 callbackContext: function callbackContext(context
10102 /*, listener, eventObj*/
10103 ) {
10104 return context;
10105 },
10106 beforeEmit: function beforeEmit()
10107 /* context, listener, eventObj */
10108 {},
10109 afterEmit: function afterEmit()
10110 /* context, listener, eventObj */
10111 {},
10112 bubble: function bubble()
10113 /*context*/
10114 {
10115 return false;
10116 },
10117 parent: function parent()
10118 /*context*/
10119 {
10120 return null;
10121 },
10122 context: null
10123};
10124var defaultsKeys = Object.keys(defaults$8);
10125var emptyOpts = {};
10126
10127function Emitter() {
10128 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10129 var context = arguments.length > 1 ? arguments[1] : undefined;
10130
10131 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10132 for (var i = 0; i < defaultsKeys.length; i++) {
10133 var key = defaultsKeys[i];
10134 this[key] = opts[key] || defaults$8[key];
10135 }
10136
10137 this.context = context || this.context;
10138 this.listeners = [];
10139 this.emitting = 0;
10140}
10141
10142var p = Emitter.prototype;
10143
10144var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10145 if (fn(qualifier)) {
10146 callback = qualifier;
10147 qualifier = null;
10148 }
10149
10150 if (confOverrides) {
10151 if (conf == null) {
10152 conf = confOverrides;
10153 } else {
10154 conf = extend({}, conf, confOverrides);
10155 }
10156 }
10157
10158 var eventList = array(events) ? events : events.split(/\s+/);
10159
10160 for (var i = 0; i < eventList.length; i++) {
10161 var evt = eventList[i];
10162
10163 if (emptyString(evt)) {
10164 continue;
10165 }
10166
10167 var match = evt.match(eventRegex); // type[.namespace]
10168
10169 if (match) {
10170 var type = match[1];
10171 var namespace = match[2] ? match[2] : null;
10172 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10173
10174 if (ret === false) {
10175 break;
10176 } // allow exiting early
10177
10178 }
10179 }
10180};
10181
10182var makeEventObj = function makeEventObj(self, obj) {
10183 self.addEventFields(self.context, obj);
10184 return new Event(obj.type, obj);
10185};
10186
10187var forEachEventObj = function forEachEventObj(self, handler, events) {
10188 if (event(events)) {
10189 handler(self, events);
10190 return;
10191 } else if (plainObject(events)) {
10192 handler(self, makeEventObj(self, events));
10193 return;
10194 }
10195
10196 var eventList = array(events) ? events : events.split(/\s+/);
10197
10198 for (var i = 0; i < eventList.length; i++) {
10199 var evt = eventList[i];
10200
10201 if (emptyString(evt)) {
10202 continue;
10203 }
10204
10205 var match = evt.match(eventRegex); // type[.namespace]
10206
10207 if (match) {
10208 var type = match[1];
10209 var namespace = match[2] ? match[2] : null;
10210 var eventObj = makeEventObj(self, {
10211 type: type,
10212 namespace: namespace,
10213 target: self.context
10214 });
10215 handler(self, eventObj);
10216 }
10217 }
10218};
10219
10220p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10221 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10222 if (fn(callback)) {
10223 self.listeners.push({
10224 event: event,
10225 // full event string
10226 callback: callback,
10227 // callback to run
10228 type: type,
10229 // the event type (e.g. 'click')
10230 namespace: namespace,
10231 // the event namespace (e.g. ".foo")
10232 qualifier: qualifier,
10233 // a restriction on whether to match this emitter
10234 conf: conf // additional configuration
10235
10236 });
10237 }
10238 }, events, qualifier, callback, conf, confOverrides);
10239 return this;
10240};
10241
10242p.one = function (events, qualifier, callback, conf) {
10243 return this.on(events, qualifier, callback, conf, {
10244 one: true
10245 });
10246};
10247
10248p.removeListener = p.off = function (events, qualifier, callback, conf) {
10249 var _this = this;
10250
10251 if (this.emitting !== 0) {
10252 this.listeners = copyArray(this.listeners);
10253 }
10254
10255 var listeners = this.listeners;
10256
10257 var _loop = function _loop(i) {
10258 var listener = listeners[i];
10259 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10260 /*, conf*/
10261 ) {
10262 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10263 listeners.splice(i, 1);
10264 return false;
10265 }
10266 }, events, qualifier, callback, conf);
10267 };
10268
10269 for (var i = listeners.length - 1; i >= 0; i--) {
10270 _loop(i);
10271 }
10272
10273 return this;
10274};
10275
10276p.removeAllListeners = function () {
10277 return this.removeListener('*');
10278};
10279
10280p.emit = p.trigger = function (events, extraParams, manualCallback) {
10281 var listeners = this.listeners;
10282 var numListenersBeforeEmit = listeners.length;
10283 this.emitting++;
10284
10285 if (!array(extraParams)) {
10286 extraParams = [extraParams];
10287 }
10288
10289 forEachEventObj(this, function (self, eventObj) {
10290 if (manualCallback != null) {
10291 listeners = [{
10292 event: eventObj.event,
10293 type: eventObj.type,
10294 namespace: eventObj.namespace,
10295 callback: manualCallback
10296 }];
10297 numListenersBeforeEmit = listeners.length;
10298 }
10299
10300 var _loop2 = function _loop2(i) {
10301 var listener = listeners[i];
10302
10303 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10304 var args = [eventObj];
10305
10306 if (extraParams != null) {
10307 push(args, extraParams);
10308 }
10309
10310 self.beforeEmit(self.context, listener, eventObj);
10311
10312 if (listener.conf && listener.conf.one) {
10313 self.listeners = self.listeners.filter(function (l) {
10314 return l !== listener;
10315 });
10316 }
10317
10318 var context = self.callbackContext(self.context, listener, eventObj);
10319 var ret = listener.callback.apply(context, args);
10320 self.afterEmit(self.context, listener, eventObj);
10321
10322 if (ret === false) {
10323 eventObj.stopPropagation();
10324 eventObj.preventDefault();
10325 }
10326 } // if listener matches
10327
10328 };
10329
10330 for (var i = 0; i < numListenersBeforeEmit; i++) {
10331 _loop2(i);
10332 } // for listener
10333
10334
10335 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10336 self.parent(self.context).emit(eventObj, extraParams);
10337 }
10338 }, events);
10339 this.emitting--;
10340 return this;
10341};
10342
10343var emitterOptions = {
10344 qualifierCompare: function qualifierCompare(selector1, selector2) {
10345 if (selector1 == null || selector2 == null) {
10346 return selector1 == null && selector2 == null;
10347 } else {
10348 return selector1.sameText(selector2);
10349 }
10350 },
10351 eventMatches: function eventMatches(ele, listener, eventObj) {
10352 var selector = listener.qualifier;
10353
10354 if (selector != null) {
10355 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10356 }
10357
10358 return true;
10359 },
10360 addEventFields: function addEventFields(ele, evt) {
10361 evt.cy = ele.cy();
10362 evt.target = ele;
10363 },
10364 callbackContext: function callbackContext(ele, listener, eventObj) {
10365 return listener.qualifier != null ? eventObj.target : ele;
10366 },
10367 beforeEmit: function beforeEmit(context, listener
10368 /*, eventObj*/
10369 ) {
10370 if (listener.conf && listener.conf.once) {
10371 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10372 }
10373 },
10374 bubble: function bubble() {
10375 return true;
10376 },
10377 parent: function parent(ele) {
10378 return ele.isChild() ? ele.parent() : ele.cy();
10379 }
10380};
10381
10382var argSelector = function argSelector(arg) {
10383 if (string(arg)) {
10384 return new Selector(arg);
10385 } else {
10386 return arg;
10387 }
10388};
10389
10390var elesfn$m = {
10391 createEmitter: function createEmitter() {
10392 for (var i = 0; i < this.length; i++) {
10393 var ele = this[i];
10394 var _p = ele._private;
10395
10396 if (!_p.emitter) {
10397 _p.emitter = new Emitter(emitterOptions, ele);
10398 }
10399 }
10400
10401 return this;
10402 },
10403 emitter: function emitter() {
10404 return this._private.emitter;
10405 },
10406 on: function on(events, selector, callback) {
10407 var argSel = argSelector(selector);
10408
10409 for (var i = 0; i < this.length; i++) {
10410 var ele = this[i];
10411 ele.emitter().on(events, argSel, callback);
10412 }
10413
10414 return this;
10415 },
10416 removeListener: function removeListener(events, selector, callback) {
10417 var argSel = argSelector(selector);
10418
10419 for (var i = 0; i < this.length; i++) {
10420 var ele = this[i];
10421 ele.emitter().removeListener(events, argSel, callback);
10422 }
10423
10424 return this;
10425 },
10426 removeAllListeners: function removeAllListeners() {
10427 for (var i = 0; i < this.length; i++) {
10428 var ele = this[i];
10429 ele.emitter().removeAllListeners();
10430 }
10431
10432 return this;
10433 },
10434 one: function one(events, selector, callback) {
10435 var argSel = argSelector(selector);
10436
10437 for (var i = 0; i < this.length; i++) {
10438 var ele = this[i];
10439 ele.emitter().one(events, argSel, callback);
10440 }
10441
10442 return this;
10443 },
10444 once: function once(events, selector, callback) {
10445 var argSel = argSelector(selector);
10446
10447 for (var i = 0; i < this.length; i++) {
10448 var ele = this[i];
10449 ele.emitter().on(events, argSel, callback, {
10450 once: true,
10451 onceCollection: this
10452 });
10453 }
10454 },
10455 emit: function emit(events, extraParams) {
10456 for (var i = 0; i < this.length; i++) {
10457 var ele = this[i];
10458 ele.emitter().emit(events, extraParams);
10459 }
10460
10461 return this;
10462 },
10463 emitAndNotify: function emitAndNotify(event, extraParams) {
10464 // for internal use only
10465 if (this.length === 0) {
10466 return;
10467 } // empty collections don't need to notify anything
10468 // notify renderer
10469
10470
10471 this.cy().notify(event, this);
10472 this.emit(event, extraParams);
10473 return this;
10474 }
10475};
10476define$3.eventAliasesOn(elesfn$m);
10477
10478var elesfn$n = {
10479 nodes: function nodes(selector) {
10480 return this.filter(function (ele) {
10481 return ele.isNode();
10482 }).filter(selector);
10483 },
10484 edges: function edges(selector) {
10485 return this.filter(function (ele) {
10486 return ele.isEdge();
10487 }).filter(selector);
10488 },
10489 // internal helper to get nodes and edges as separate collections with single iteration over elements
10490 byGroup: function byGroup() {
10491 var nodes = this.spawn();
10492 var edges = this.spawn();
10493
10494 for (var i = 0; i < this.length; i++) {
10495 var ele = this[i];
10496
10497 if (ele.isNode()) {
10498 nodes.merge(ele);
10499 } else {
10500 edges.merge(ele);
10501 }
10502 }
10503
10504 return {
10505 nodes: nodes,
10506 edges: edges
10507 };
10508 },
10509 filter: function filter(_filter, thisArg) {
10510 if (_filter === undefined) {
10511 // check this first b/c it's the most common/performant case
10512 return this;
10513 } else if (string(_filter) || elementOrCollection(_filter)) {
10514 return new Selector(_filter).filter(this);
10515 } else if (fn(_filter)) {
10516 var filterEles = this.spawn();
10517 var eles = this;
10518
10519 for (var i = 0; i < eles.length; i++) {
10520 var ele = eles[i];
10521 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10522
10523 if (include) {
10524 filterEles.merge(ele);
10525 }
10526 }
10527
10528 return filterEles;
10529 }
10530
10531 return this.spawn(); // if not handled by above, give 'em an empty collection
10532 },
10533 not: function not(toRemove) {
10534 if (!toRemove) {
10535 return this;
10536 } else {
10537 if (string(toRemove)) {
10538 toRemove = this.filter(toRemove);
10539 }
10540
10541 var elements = [];
10542 var rMap = toRemove._private.map;
10543
10544 for (var i = 0; i < this.length; i++) {
10545 var element = this[i];
10546 var remove = rMap.has(element.id());
10547
10548 if (!remove) {
10549 elements.push(element);
10550 }
10551 }
10552
10553 return this.spawn(elements);
10554 }
10555 },
10556 absoluteComplement: function absoluteComplement() {
10557 var cy = this.cy();
10558 return cy.mutableElements().not(this);
10559 },
10560 intersect: function intersect(other) {
10561 // if a selector is specified, then filter by it instead
10562 if (string(other)) {
10563 var selector = other;
10564 return this.filter(selector);
10565 }
10566
10567 var elements = [];
10568 var col1 = this;
10569 var col2 = other;
10570 var col1Smaller = this.length < other.length;
10571 var map2 = col1Smaller ? col2._private.map : col1._private.map;
10572 var col = col1Smaller ? col1 : col2;
10573
10574 for (var i = 0; i < col.length; i++) {
10575 var id = col[i]._private.data.id;
10576 var entry = map2.get(id);
10577
10578 if (entry) {
10579 elements.push(entry.ele);
10580 }
10581 }
10582
10583 return this.spawn(elements);
10584 },
10585 xor: function xor(other) {
10586 var cy = this._private.cy;
10587
10588 if (string(other)) {
10589 other = cy.$(other);
10590 }
10591
10592 var elements = [];
10593 var col1 = this;
10594 var col2 = other;
10595
10596 var add = function add(col, other) {
10597 for (var i = 0; i < col.length; i++) {
10598 var ele = col[i];
10599 var id = ele._private.data.id;
10600 var inOther = other.hasElementWithId(id);
10601
10602 if (!inOther) {
10603 elements.push(ele);
10604 }
10605 }
10606 };
10607
10608 add(col1, col2);
10609 add(col2, col1);
10610 return this.spawn(elements);
10611 },
10612 diff: function diff(other) {
10613 var cy = this._private.cy;
10614
10615 if (string(other)) {
10616 other = cy.$(other);
10617 }
10618
10619 var left = [];
10620 var right = [];
10621 var both = [];
10622 var col1 = this;
10623 var col2 = other;
10624
10625 var add = function add(col, other, retEles) {
10626 for (var i = 0; i < col.length; i++) {
10627 var ele = col[i];
10628 var id = ele._private.data.id;
10629 var inOther = other.hasElementWithId(id);
10630
10631 if (inOther) {
10632 both.push(ele);
10633 } else {
10634 retEles.push(ele);
10635 }
10636 }
10637 };
10638
10639 add(col1, col2, left);
10640 add(col2, col1, right);
10641 return {
10642 left: this.spawn(left, {
10643 unique: true
10644 }),
10645 right: this.spawn(right, {
10646 unique: true
10647 }),
10648 both: this.spawn(both, {
10649 unique: true
10650 })
10651 };
10652 },
10653 add: function add(toAdd) {
10654 var cy = this._private.cy;
10655
10656 if (!toAdd) {
10657 return this;
10658 }
10659
10660 if (string(toAdd)) {
10661 var selector = toAdd;
10662 toAdd = cy.mutableElements().filter(selector);
10663 }
10664
10665 var elements = [];
10666
10667 for (var i = 0; i < this.length; i++) {
10668 elements.push(this[i]);
10669 }
10670
10671 var map = this._private.map;
10672
10673 for (var _i = 0; _i < toAdd.length; _i++) {
10674 var add = !map.has(toAdd[_i].id());
10675
10676 if (add) {
10677 elements.push(toAdd[_i]);
10678 }
10679 }
10680
10681 return this.spawn(elements);
10682 },
10683 // in place merge on calling collection
10684 merge: function merge(toAdd) {
10685 var _p = this._private;
10686 var cy = _p.cy;
10687
10688 if (!toAdd) {
10689 return this;
10690 }
10691
10692 if (toAdd && string(toAdd)) {
10693 var selector = toAdd;
10694 toAdd = cy.mutableElements().filter(selector);
10695 }
10696
10697 var map = _p.map;
10698
10699 for (var i = 0; i < toAdd.length; i++) {
10700 var toAddEle = toAdd[i];
10701 var id = toAddEle._private.data.id;
10702 var add = !map.has(id);
10703
10704 if (add) {
10705 var index = this.length++;
10706 this[index] = toAddEle;
10707 map.set(id, {
10708 ele: toAddEle,
10709 index: index
10710 });
10711 } else {
10712 // replace
10713 var _index = map.get(id).index;
10714 this[_index] = toAddEle;
10715 map.set(id, {
10716 ele: toAddEle,
10717 index: _index
10718 });
10719 }
10720 }
10721
10722 return this; // chaining
10723 },
10724 unmergeAt: function unmergeAt(i) {
10725 var ele = this[i];
10726 var id = ele.id();
10727 var _p = this._private;
10728 var map = _p.map; // remove ele
10729
10730 this[i] = undefined;
10731 map["delete"](id);
10732 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10733
10734 if (this.length > 1 && !unmergedLastEle) {
10735 var lastEleI = this.length - 1;
10736 var lastEle = this[lastEleI];
10737 var lastEleId = lastEle._private.data.id;
10738 this[lastEleI] = undefined;
10739 this[i] = lastEle;
10740 map.set(lastEleId, {
10741 ele: lastEle,
10742 index: i
10743 });
10744 } // the collection is now 1 ele smaller
10745
10746
10747 this.length--;
10748 return this;
10749 },
10750 // remove single ele in place in calling collection
10751 unmergeOne: function unmergeOne(ele) {
10752 ele = ele[0];
10753 var _p = this._private;
10754 var id = ele._private.data.id;
10755 var map = _p.map;
10756 var entry = map.get(id);
10757
10758 if (!entry) {
10759 return this; // no need to remove
10760 }
10761
10762 var i = entry.index;
10763 this.unmergeAt(i);
10764 return this;
10765 },
10766 // remove eles in place on calling collection
10767 unmerge: function unmerge(toRemove) {
10768 var cy = this._private.cy;
10769
10770 if (!toRemove) {
10771 return this;
10772 }
10773
10774 if (toRemove && string(toRemove)) {
10775 var selector = toRemove;
10776 toRemove = cy.mutableElements().filter(selector);
10777 }
10778
10779 for (var i = 0; i < toRemove.length; i++) {
10780 this.unmergeOne(toRemove[i]);
10781 }
10782
10783 return this; // chaining
10784 },
10785 unmergeBy: function unmergeBy(toRmFn) {
10786 for (var i = this.length - 1; i >= 0; i--) {
10787 var ele = this[i];
10788
10789 if (toRmFn(ele)) {
10790 this.unmergeAt(i);
10791 }
10792 }
10793
10794 return this;
10795 },
10796 map: function map(mapFn, thisArg) {
10797 var arr = [];
10798 var eles = this;
10799
10800 for (var i = 0; i < eles.length; i++) {
10801 var ele = eles[i];
10802 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
10803 arr.push(ret);
10804 }
10805
10806 return arr;
10807 },
10808 reduce: function reduce(fn, initialValue) {
10809 var val = initialValue;
10810 var eles = this;
10811
10812 for (var i = 0; i < eles.length; i++) {
10813 val = fn(val, eles[i], i, eles);
10814 }
10815
10816 return val;
10817 },
10818 max: function max(valFn, thisArg) {
10819 var max = -Infinity;
10820 var maxEle;
10821 var eles = this;
10822
10823 for (var i = 0; i < eles.length; i++) {
10824 var ele = eles[i];
10825 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
10826
10827 if (val > max) {
10828 max = val;
10829 maxEle = ele;
10830 }
10831 }
10832
10833 return {
10834 value: max,
10835 ele: maxEle
10836 };
10837 },
10838 min: function min(valFn, thisArg) {
10839 var min = Infinity;
10840 var minEle;
10841 var eles = this;
10842
10843 for (var i = 0; i < eles.length; i++) {
10844 var ele = eles[i];
10845 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
10846
10847 if (val < min) {
10848 min = val;
10849 minEle = ele;
10850 }
10851 }
10852
10853 return {
10854 value: min,
10855 ele: minEle
10856 };
10857 }
10858}; // aliases
10859
10860var fn$5 = elesfn$n;
10861fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
10862fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
10863fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
10864fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
10865fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
10866fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
10867
10868var elesfn$o = {
10869 isNode: function isNode() {
10870 return this.group() === 'nodes';
10871 },
10872 isEdge: function isEdge() {
10873 return this.group() === 'edges';
10874 },
10875 isLoop: function isLoop() {
10876 return this.isEdge() && this.source()[0] === this.target()[0];
10877 },
10878 isSimple: function isSimple() {
10879 return this.isEdge() && this.source()[0] !== this.target()[0];
10880 },
10881 group: function group() {
10882 var ele = this[0];
10883
10884 if (ele) {
10885 return ele._private.group;
10886 }
10887 }
10888};
10889
10890/**
10891 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
10892 * and z-index (low to high). These styles affect how this applies:
10893 *
10894 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
10895 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
10896 * root to leaves of the compound graph. The last drawn is `top`.
10897 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
10898 * `manual` ignores this convention and draws based on the `z-index` value setting.
10899 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
10900 * `z-index` will be drawn on top of an element with a lower `z-index`.
10901 */
10902
10903var zIndexSort = function zIndexSort(a, b) {
10904 var cy = a.cy();
10905 var hasCompoundNodes = cy.hasCompoundNodes();
10906
10907 function getDepth(ele) {
10908 var style = ele.pstyle('z-compound-depth');
10909
10910 if (style.value === 'auto') {
10911 return hasCompoundNodes ? ele.zDepth() : 0;
10912 } else if (style.value === 'bottom') {
10913 return -1;
10914 } else if (style.value === 'top') {
10915 return MAX_INT;
10916 } // 'orphan'
10917
10918
10919 return 0;
10920 }
10921
10922 var depthDiff = getDepth(a) - getDepth(b);
10923
10924 if (depthDiff !== 0) {
10925 return depthDiff;
10926 }
10927
10928 function getEleDepth(ele) {
10929 var style = ele.pstyle('z-index-compare');
10930
10931 if (style.value === 'auto') {
10932 return ele.isNode() ? 1 : 0;
10933 } // 'manual'
10934
10935
10936 return 0;
10937 }
10938
10939 var eleDiff = getEleDepth(a) - getEleDepth(b);
10940
10941 if (eleDiff !== 0) {
10942 return eleDiff;
10943 }
10944
10945 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
10946
10947 if (zDiff !== 0) {
10948 return zDiff;
10949 } // compare indices in the core (order added to graph w/ last on top)
10950
10951
10952 return a.poolIndex() - b.poolIndex();
10953};
10954
10955var elesfn$p = {
10956 forEach: function forEach(fn$1, thisArg) {
10957 if (fn(fn$1)) {
10958 var N = this.length;
10959
10960 for (var i = 0; i < N; i++) {
10961 var ele = this[i];
10962 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
10963
10964 if (ret === false) {
10965 break;
10966 } // exit each early on return false
10967
10968 }
10969 }
10970
10971 return this;
10972 },
10973 toArray: function toArray() {
10974 var array = [];
10975
10976 for (var i = 0; i < this.length; i++) {
10977 array.push(this[i]);
10978 }
10979
10980 return array;
10981 },
10982 slice: function slice(start, end) {
10983 var array = [];
10984 var thisSize = this.length;
10985
10986 if (end == null) {
10987 end = thisSize;
10988 }
10989
10990 if (start == null) {
10991 start = 0;
10992 }
10993
10994 if (start < 0) {
10995 start = thisSize + start;
10996 }
10997
10998 if (end < 0) {
10999 end = thisSize + end;
11000 }
11001
11002 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11003 array.push(this[i]);
11004 }
11005
11006 return this.spawn(array);
11007 },
11008 size: function size() {
11009 return this.length;
11010 },
11011 eq: function eq(i) {
11012 return this[i] || this.spawn();
11013 },
11014 first: function first() {
11015 return this[0] || this.spawn();
11016 },
11017 last: function last() {
11018 return this[this.length - 1] || this.spawn();
11019 },
11020 empty: function empty() {
11021 return this.length === 0;
11022 },
11023 nonempty: function nonempty() {
11024 return !this.empty();
11025 },
11026 sort: function sort(sortFn) {
11027 if (!fn(sortFn)) {
11028 return this;
11029 }
11030
11031 var sorted = this.toArray().sort(sortFn);
11032 return this.spawn(sorted);
11033 },
11034 sortByZIndex: function sortByZIndex() {
11035 return this.sort(zIndexSort);
11036 },
11037 zDepth: function zDepth() {
11038 var ele = this[0];
11039
11040 if (!ele) {
11041 return undefined;
11042 } // let cy = ele.cy();
11043
11044
11045 var _p = ele._private;
11046 var group = _p.group;
11047
11048 if (group === 'nodes') {
11049 var depth = _p.data.parent ? ele.parents().size() : 0;
11050
11051 if (!ele.isParent()) {
11052 return MAX_INT - 1; // childless nodes always on top
11053 }
11054
11055 return depth;
11056 } else {
11057 var src = _p.source;
11058 var tgt = _p.target;
11059 var srcDepth = src.zDepth();
11060 var tgtDepth = tgt.zDepth();
11061 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11062 }
11063 }
11064};
11065elesfn$p.each = elesfn$p.forEach;
11066
11067var getLayoutDimensionOptions = defaults({
11068 nodeDimensionsIncludeLabels: false
11069});
11070var elesfn$q = {
11071 // Calculates and returns node dimensions { x, y } based on options given
11072 layoutDimensions: function layoutDimensions(options) {
11073 options = getLayoutDimensionOptions(options);
11074 var dims;
11075
11076 if (!this.takesUpSpace()) {
11077 dims = {
11078 w: 0,
11079 h: 0
11080 };
11081 } else if (options.nodeDimensionsIncludeLabels) {
11082 var bbDim = this.boundingBox();
11083 dims = {
11084 w: bbDim.w,
11085 h: bbDim.h
11086 };
11087 } else {
11088 dims = {
11089 w: this.outerWidth(),
11090 h: this.outerHeight()
11091 };
11092 } // sanitise the dimensions for external layouts (avoid division by zero)
11093
11094
11095 if (dims.w === 0 || dims.h === 0) {
11096 dims.w = dims.h = 1;
11097 }
11098
11099 return dims;
11100 },
11101 // using standard layout options, apply position function (w/ or w/o animation)
11102 layoutPositions: function layoutPositions(layout, options, fn) {
11103 var nodes = this.nodes();
11104 var cy = this.cy();
11105 var layoutEles = options.eles; // nodes & edges
11106
11107 var getMemoizeKey = function getMemoizeKey(node) {
11108 return node.id();
11109 };
11110
11111 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11112
11113 layout.emit({
11114 type: 'layoutstart',
11115 layout: layout
11116 });
11117 layout.animations = [];
11118
11119 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11120 var center = {
11121 x: nodesBb.x1 + nodesBb.w / 2,
11122 y: nodesBb.y1 + nodesBb.h / 2
11123 };
11124 var spacingVector = {
11125 // scale from center of bounding box (not necessarily 0,0)
11126 x: (pos.x - center.x) * spacing,
11127 y: (pos.y - center.y) * spacing
11128 };
11129 return {
11130 x: center.x + spacingVector.x,
11131 y: center.y + spacingVector.y
11132 };
11133 };
11134
11135 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11136
11137 var spacingBb = function spacingBb() {
11138 if (!useSpacingFactor) {
11139 return null;
11140 }
11141
11142 var bb = makeBoundingBox();
11143
11144 for (var i = 0; i < nodes.length; i++) {
11145 var node = nodes[i];
11146 var pos = fnMem(node, i);
11147 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11148 }
11149
11150 return bb;
11151 };
11152
11153 var bb = spacingBb();
11154 var getFinalPos = memoize(function (node, i) {
11155 var newPos = fnMem(node, i);
11156
11157 if (useSpacingFactor) {
11158 var spacing = Math.abs(options.spacingFactor);
11159 newPos = calculateSpacing(spacing, bb, newPos);
11160 }
11161
11162 if (options.transform != null) {
11163 newPos = options.transform(node, newPos);
11164 }
11165
11166 return newPos;
11167 }, getMemoizeKey);
11168
11169 if (options.animate) {
11170 for (var i = 0; i < nodes.length; i++) {
11171 var node = nodes[i];
11172 var newPos = getFinalPos(node, i);
11173 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11174
11175 if (animateNode) {
11176 var ani = node.animation({
11177 position: newPos,
11178 duration: options.animationDuration,
11179 easing: options.animationEasing
11180 });
11181 layout.animations.push(ani);
11182 } else {
11183 node.position(newPos);
11184 }
11185 }
11186
11187 if (options.fit) {
11188 var fitAni = cy.animation({
11189 fit: {
11190 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11191 padding: options.padding
11192 },
11193 duration: options.animationDuration,
11194 easing: options.animationEasing
11195 });
11196 layout.animations.push(fitAni);
11197 } else if (options.zoom !== undefined && options.pan !== undefined) {
11198 var zoomPanAni = cy.animation({
11199 zoom: options.zoom,
11200 pan: options.pan,
11201 duration: options.animationDuration,
11202 easing: options.animationEasing
11203 });
11204 layout.animations.push(zoomPanAni);
11205 }
11206
11207 layout.animations.forEach(function (ani) {
11208 return ani.play();
11209 });
11210 layout.one('layoutready', options.ready);
11211 layout.emit({
11212 type: 'layoutready',
11213 layout: layout
11214 });
11215 Promise$1.all(layout.animations.map(function (ani) {
11216 return ani.promise();
11217 })).then(function () {
11218 layout.one('layoutstop', options.stop);
11219 layout.emit({
11220 type: 'layoutstop',
11221 layout: layout
11222 });
11223 });
11224 } else {
11225 nodes.positions(getFinalPos);
11226
11227 if (options.fit) {
11228 cy.fit(options.eles, options.padding);
11229 }
11230
11231 if (options.zoom != null) {
11232 cy.zoom(options.zoom);
11233 }
11234
11235 if (options.pan) {
11236 cy.pan(options.pan);
11237 }
11238
11239 layout.one('layoutready', options.ready);
11240 layout.emit({
11241 type: 'layoutready',
11242 layout: layout
11243 });
11244 layout.one('layoutstop', options.stop);
11245 layout.emit({
11246 type: 'layoutstop',
11247 layout: layout
11248 });
11249 }
11250
11251 return this; // chaining
11252 },
11253 layout: function layout(options) {
11254 var cy = this.cy();
11255 return cy.makeLayout(extend({}, options, {
11256 eles: this
11257 }));
11258 }
11259}; // aliases:
11260
11261elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11262
11263function styleCache(key, fn, ele) {
11264 var _p = ele._private;
11265 var cache = _p.styleCache = _p.styleCache || [];
11266 var val;
11267
11268 if ((val = cache[key]) != null) {
11269 return val;
11270 } else {
11271 val = cache[key] = fn(ele);
11272 return val;
11273 }
11274}
11275
11276function cacheStyleFunction(key, fn) {
11277 key = hashString(key);
11278 return function cachedStyleFunction(ele) {
11279 return styleCache(key, fn, ele);
11280 };
11281}
11282
11283function cachePrototypeStyleFunction(key, fn) {
11284 key = hashString(key);
11285
11286 var selfFn = function selfFn(ele) {
11287 return fn.call(ele);
11288 };
11289
11290 return function cachedPrototypeStyleFunction() {
11291 var ele = this[0];
11292
11293 if (ele) {
11294 return styleCache(key, selfFn, ele);
11295 }
11296 };
11297}
11298
11299var elesfn$r = {
11300 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11301 var cy = this.cy();
11302 var renderer = cy.renderer();
11303 var styleEnabled = cy.styleEnabled();
11304
11305 if (renderer && styleEnabled) {
11306 renderer.recalculateRenderedStyle(this, useCache);
11307 }
11308
11309 return this;
11310 },
11311 dirtyStyleCache: function dirtyStyleCache() {
11312 var cy = this.cy();
11313
11314 var dirty = function dirty(ele) {
11315 return ele._private.styleCache = null;
11316 };
11317
11318 if (cy.hasCompoundNodes()) {
11319 var eles;
11320 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11321 eles.merge(eles.connectedEdges());
11322 eles.forEach(dirty);
11323 } else {
11324 this.forEach(function (ele) {
11325 dirty(ele);
11326 ele.connectedEdges().forEach(dirty);
11327 });
11328 }
11329
11330 return this;
11331 },
11332 // fully updates (recalculates) the style for the elements
11333 updateStyle: function updateStyle(notifyRenderer) {
11334 var cy = this._private.cy;
11335
11336 if (!cy.styleEnabled()) {
11337 return this;
11338 }
11339
11340 if (cy.batching()) {
11341 var bEles = cy._private.batchStyleEles;
11342 bEles.merge(this);
11343 return this; // chaining and exit early when batching
11344 }
11345
11346 var hasCompounds = cy.hasCompoundNodes();
11347 var style = cy.style();
11348 var updatedEles = this;
11349 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11350
11351 if (hasCompounds) {
11352 // then add everything up and down for compound selector checks
11353 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11354 }
11355
11356 var changedEles = style.apply(updatedEles);
11357
11358 if (notifyRenderer) {
11359 changedEles.emitAndNotify('style'); // let renderer know we changed style
11360 } else {
11361 changedEles.emit('style'); // just fire the event
11362 }
11363
11364 return this; // chaining
11365 },
11366 // get the internal parsed style object for the specified property
11367 parsedStyle: function parsedStyle(property) {
11368 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11369 var ele = this[0];
11370 var cy = ele.cy();
11371
11372 if (!cy.styleEnabled()) {
11373 return;
11374 }
11375
11376 if (ele) {
11377 var overriddenStyle = ele._private.style[property];
11378
11379 if (overriddenStyle != null) {
11380 return overriddenStyle;
11381 } else if (includeNonDefault) {
11382 return cy.style().getDefaultProperty(property);
11383 } else {
11384 return null;
11385 }
11386 }
11387 },
11388 numericStyle: function numericStyle(property) {
11389 var ele = this[0];
11390
11391 if (!ele.cy().styleEnabled()) {
11392 return;
11393 }
11394
11395 if (ele) {
11396 var pstyle = ele.pstyle(property);
11397 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11398 }
11399 },
11400 numericStyleUnits: function numericStyleUnits(property) {
11401 var ele = this[0];
11402
11403 if (!ele.cy().styleEnabled()) {
11404 return;
11405 }
11406
11407 if (ele) {
11408 return ele.pstyle(property).units;
11409 }
11410 },
11411 // get the specified css property as a rendered value (i.e. on-screen value)
11412 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11413 renderedStyle: function renderedStyle(property) {
11414 var cy = this.cy();
11415
11416 if (!cy.styleEnabled()) {
11417 return this;
11418 }
11419
11420 var ele = this[0];
11421
11422 if (ele) {
11423 return cy.style().getRenderedStyle(ele, property);
11424 }
11425 },
11426 // read the calculated css style of the element or override the style (via a bypass)
11427 style: function style(name, value) {
11428 var cy = this.cy();
11429
11430 if (!cy.styleEnabled()) {
11431 return this;
11432 }
11433
11434 var updateTransitions = false;
11435 var style = cy.style();
11436
11437 if (plainObject(name)) {
11438 // then extend the bypass
11439 var props = name;
11440 style.applyBypass(this, props, updateTransitions);
11441 this.emitAndNotify('style'); // let the renderer know we've updated style
11442 } else if (string(name)) {
11443 if (value === undefined) {
11444 // then get the property from the style
11445 var ele = this[0];
11446
11447 if (ele) {
11448 return style.getStylePropertyValue(ele, name);
11449 } else {
11450 // empty collection => can't get any value
11451 return;
11452 }
11453 } else {
11454 // then set the bypass with the property value
11455 style.applyBypass(this, name, value, updateTransitions);
11456 this.emitAndNotify('style'); // let the renderer know we've updated style
11457 }
11458 } else if (name === undefined) {
11459 var _ele = this[0];
11460
11461 if (_ele) {
11462 return style.getRawStyle(_ele);
11463 } else {
11464 // empty collection => can't get any value
11465 return;
11466 }
11467 }
11468
11469 return this; // chaining
11470 },
11471 removeStyle: function removeStyle(names) {
11472 var cy = this.cy();
11473
11474 if (!cy.styleEnabled()) {
11475 return this;
11476 }
11477
11478 var updateTransitions = false;
11479 var style = cy.style();
11480 var eles = this;
11481
11482 if (names === undefined) {
11483 for (var i = 0; i < eles.length; i++) {
11484 var ele = eles[i];
11485 style.removeAllBypasses(ele, updateTransitions);
11486 }
11487 } else {
11488 names = names.split(/\s+/);
11489
11490 for (var _i = 0; _i < eles.length; _i++) {
11491 var _ele2 = eles[_i];
11492 style.removeBypasses(_ele2, names, updateTransitions);
11493 }
11494 }
11495
11496 this.emitAndNotify('style'); // let the renderer know we've updated style
11497
11498 return this; // chaining
11499 },
11500 show: function show() {
11501 this.css('display', 'element');
11502 return this; // chaining
11503 },
11504 hide: function hide() {
11505 this.css('display', 'none');
11506 return this; // chaining
11507 },
11508 effectiveOpacity: function effectiveOpacity() {
11509 var cy = this.cy();
11510
11511 if (!cy.styleEnabled()) {
11512 return 1;
11513 }
11514
11515 var hasCompoundNodes = cy.hasCompoundNodes();
11516 var ele = this[0];
11517
11518 if (ele) {
11519 var _p = ele._private;
11520 var parentOpacity = ele.pstyle('opacity').value;
11521
11522 if (!hasCompoundNodes) {
11523 return parentOpacity;
11524 }
11525
11526 var parents = !_p.data.parent ? null : ele.parents();
11527
11528 if (parents) {
11529 for (var i = 0; i < parents.length; i++) {
11530 var parent = parents[i];
11531 var opacity = parent.pstyle('opacity').value;
11532 parentOpacity = opacity * parentOpacity;
11533 }
11534 }
11535
11536 return parentOpacity;
11537 }
11538 },
11539 transparent: function transparent() {
11540 var cy = this.cy();
11541
11542 if (!cy.styleEnabled()) {
11543 return false;
11544 }
11545
11546 var ele = this[0];
11547 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11548
11549 if (ele) {
11550 if (!hasCompoundNodes) {
11551 return ele.pstyle('opacity').value === 0;
11552 } else {
11553 return ele.effectiveOpacity() === 0;
11554 }
11555 }
11556 },
11557 backgrounding: function backgrounding() {
11558 var cy = this.cy();
11559
11560 if (!cy.styleEnabled()) {
11561 return false;
11562 }
11563
11564 var ele = this[0];
11565 return ele._private.backgrounding ? true : false;
11566 }
11567};
11568
11569function checkCompound(ele, parentOk) {
11570 var _p = ele._private;
11571 var parents = _p.data.parent ? ele.parents() : null;
11572
11573 if (parents) {
11574 for (var i = 0; i < parents.length; i++) {
11575 var parent = parents[i];
11576
11577 if (!parentOk(parent)) {
11578 return false;
11579 }
11580 }
11581 }
11582
11583 return true;
11584}
11585
11586function defineDerivedStateFunction(specs) {
11587 var ok = specs.ok;
11588 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11589 var parentOk = specs.parentOk || specs.ok;
11590 return function () {
11591 var cy = this.cy();
11592
11593 if (!cy.styleEnabled()) {
11594 return true;
11595 }
11596
11597 var ele = this[0];
11598 var hasCompoundNodes = cy.hasCompoundNodes();
11599
11600 if (ele) {
11601 var _p = ele._private;
11602
11603 if (!ok(ele)) {
11604 return false;
11605 }
11606
11607 if (ele.isNode()) {
11608 return !hasCompoundNodes || checkCompound(ele, parentOk);
11609 } else {
11610 var src = _p.source;
11611 var tgt = _p.target;
11612 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11613 }
11614 }
11615 };
11616}
11617
11618var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11619 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11620});
11621elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11622 ok: eleTakesUpSpace
11623}));
11624var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11625 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11626});
11627var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11628 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11629});
11630elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11631 ok: eleInteractive,
11632 parentOk: parentInteractive,
11633 edgeOkViaNode: eleTakesUpSpace
11634}));
11635
11636elesfn$r.noninteractive = function () {
11637 var ele = this[0];
11638
11639 if (ele) {
11640 return !ele.interactive();
11641 }
11642};
11643
11644var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11645 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11646});
11647var edgeVisibleViaNode = eleTakesUpSpace;
11648elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11649 ok: eleVisible,
11650 edgeOkViaNode: edgeVisibleViaNode
11651}));
11652
11653elesfn$r.hidden = function () {
11654 var ele = this[0];
11655
11656 if (ele) {
11657 return !ele.visible();
11658 }
11659};
11660
11661elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11662 if (!this.cy().styleEnabled()) {
11663 return false;
11664 }
11665
11666 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11667});
11668elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11669elesfn$r.renderedCss = elesfn$r.renderedStyle;
11670elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11671elesfn$r.pstyle = elesfn$r.parsedStyle;
11672
11673var elesfn$s = {};
11674
11675function defineSwitchFunction(params) {
11676 return function () {
11677 var args = arguments;
11678 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11679
11680 if (args.length === 2) {
11681 var data = args[0];
11682 var handler = args[1];
11683 this.on(params.event, data, handler);
11684 } // e.g. cy.nodes().select( handler )
11685 else if (args.length === 1 && fn(args[0])) {
11686 var _handler = args[0];
11687 this.on(params.event, _handler);
11688 } // e.g. cy.nodes().select()
11689 // e.g. (private) cy.nodes().select(['tapselect'])
11690 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11691 var addlEvents = args.length === 1 ? args[0] : null;
11692
11693 for (var i = 0; i < this.length; i++) {
11694 var ele = this[i];
11695 var able = !params.ableField || ele._private[params.ableField];
11696 var changed = ele._private[params.field] != params.value;
11697
11698 if (params.overrideAble) {
11699 var overrideAble = params.overrideAble(ele);
11700
11701 if (overrideAble !== undefined) {
11702 able = overrideAble;
11703
11704 if (!overrideAble) {
11705 return this;
11706 } // to save cycles assume not able for all on override
11707
11708 }
11709 }
11710
11711 if (able) {
11712 ele._private[params.field] = params.value;
11713
11714 if (changed) {
11715 changedEles.push(ele);
11716 }
11717 }
11718 }
11719
11720 var changedColl = this.spawn(changedEles);
11721 changedColl.updateStyle(); // change of state => possible change of style
11722
11723 changedColl.emit(params.event);
11724
11725 if (addlEvents) {
11726 changedColl.emit(addlEvents);
11727 }
11728 }
11729
11730 return this;
11731 };
11732}
11733
11734function defineSwitchSet(params) {
11735 elesfn$s[params.field] = function () {
11736 var ele = this[0];
11737
11738 if (ele) {
11739 if (params.overrideField) {
11740 var val = params.overrideField(ele);
11741
11742 if (val !== undefined) {
11743 return val;
11744 }
11745 }
11746
11747 return ele._private[params.field];
11748 }
11749 };
11750
11751 elesfn$s[params.on] = defineSwitchFunction({
11752 event: params.on,
11753 field: params.field,
11754 ableField: params.ableField,
11755 overrideAble: params.overrideAble,
11756 value: true
11757 });
11758 elesfn$s[params.off] = defineSwitchFunction({
11759 event: params.off,
11760 field: params.field,
11761 ableField: params.ableField,
11762 overrideAble: params.overrideAble,
11763 value: false
11764 });
11765}
11766
11767defineSwitchSet({
11768 field: 'locked',
11769 overrideField: function overrideField(ele) {
11770 return ele.cy().autolock() ? true : undefined;
11771 },
11772 on: 'lock',
11773 off: 'unlock'
11774});
11775defineSwitchSet({
11776 field: 'grabbable',
11777 overrideField: function overrideField(ele) {
11778 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
11779 },
11780 on: 'grabify',
11781 off: 'ungrabify'
11782});
11783defineSwitchSet({
11784 field: 'selected',
11785 ableField: 'selectable',
11786 overrideAble: function overrideAble(ele) {
11787 return ele.cy().autounselectify() ? false : undefined;
11788 },
11789 on: 'select',
11790 off: 'unselect'
11791});
11792defineSwitchSet({
11793 field: 'selectable',
11794 overrideField: function overrideField(ele) {
11795 return ele.cy().autounselectify() ? false : undefined;
11796 },
11797 on: 'selectify',
11798 off: 'unselectify'
11799});
11800elesfn$s.deselect = elesfn$s.unselect;
11801
11802elesfn$s.grabbed = function () {
11803 var ele = this[0];
11804
11805 if (ele) {
11806 return ele._private.grabbed;
11807 }
11808};
11809
11810defineSwitchSet({
11811 field: 'active',
11812 on: 'activate',
11813 off: 'unactivate'
11814});
11815defineSwitchSet({
11816 field: 'pannable',
11817 on: 'panify',
11818 off: 'unpanify'
11819});
11820
11821elesfn$s.inactive = function () {
11822 var ele = this[0];
11823
11824 if (ele) {
11825 return !ele._private.active;
11826 }
11827};
11828
11829var elesfn$t = {}; // DAG functions
11830////////////////
11831
11832var defineDagExtremity = function defineDagExtremity(params) {
11833 return function dagExtremityImpl(selector) {
11834 var eles = this;
11835 var ret = [];
11836
11837 for (var i = 0; i < eles.length; i++) {
11838 var ele = eles[i];
11839
11840 if (!ele.isNode()) {
11841 continue;
11842 }
11843
11844 var disqualified = false;
11845 var edges = ele.connectedEdges();
11846
11847 for (var j = 0; j < edges.length; j++) {
11848 var edge = edges[j];
11849 var src = edge.source();
11850 var tgt = edge.target();
11851
11852 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
11853 disqualified = true;
11854 break;
11855 }
11856 }
11857
11858 if (!disqualified) {
11859 ret.push(ele);
11860 }
11861 }
11862
11863 return this.spawn(ret, {
11864 unique: true
11865 }).filter(selector);
11866 };
11867};
11868
11869var defineDagOneHop = function defineDagOneHop(params) {
11870 return function (selector) {
11871 var eles = this;
11872 var oEles = [];
11873
11874 for (var i = 0; i < eles.length; i++) {
11875 var ele = eles[i];
11876
11877 if (!ele.isNode()) {
11878 continue;
11879 }
11880
11881 var edges = ele.connectedEdges();
11882
11883 for (var j = 0; j < edges.length; j++) {
11884 var edge = edges[j];
11885 var src = edge.source();
11886 var tgt = edge.target();
11887
11888 if (params.outgoing && src === ele) {
11889 oEles.push(edge);
11890 oEles.push(tgt);
11891 } else if (params.incoming && tgt === ele) {
11892 oEles.push(edge);
11893 oEles.push(src);
11894 }
11895 }
11896 }
11897
11898 return this.spawn(oEles, {
11899 unique: true
11900 }).filter(selector);
11901 };
11902};
11903
11904var defineDagAllHops = function defineDagAllHops(params) {
11905 return function (selector) {
11906 var eles = this;
11907 var sEles = [];
11908 var sElesIds = {};
11909
11910 for (;;) {
11911 var next = params.outgoing ? eles.outgoers() : eles.incomers();
11912
11913 if (next.length === 0) {
11914 break;
11915 } // done if none left
11916
11917
11918 var newNext = false;
11919
11920 for (var i = 0; i < next.length; i++) {
11921 var n = next[i];
11922 var nid = n.id();
11923
11924 if (!sElesIds[nid]) {
11925 sElesIds[nid] = true;
11926 sEles.push(n);
11927 newNext = true;
11928 }
11929 }
11930
11931 if (!newNext) {
11932 break;
11933 } // done if touched all outgoers already
11934
11935
11936 eles = next;
11937 }
11938
11939 return this.spawn(sEles, {
11940 unique: true
11941 }).filter(selector);
11942 };
11943};
11944
11945elesfn$t.clearTraversalCache = function () {
11946 for (var i = 0; i < this.length; i++) {
11947 this[i]._private.traversalCache = null;
11948 }
11949};
11950
11951extend(elesfn$t, {
11952 // get the root nodes in the DAG
11953 roots: defineDagExtremity({
11954 noIncomingEdges: true
11955 }),
11956 // get the leaf nodes in the DAG
11957 leaves: defineDagExtremity({
11958 noOutgoingEdges: true
11959 }),
11960 // normally called children in graph theory
11961 // these nodes =edges=> outgoing nodes
11962 outgoers: cache(defineDagOneHop({
11963 outgoing: true
11964 }), 'outgoers'),
11965 // aka DAG descendants
11966 successors: defineDagAllHops({
11967 outgoing: true
11968 }),
11969 // normally called parents in graph theory
11970 // these nodes <=edges= incoming nodes
11971 incomers: cache(defineDagOneHop({
11972 incoming: true
11973 }), 'incomers'),
11974 // aka DAG ancestors
11975 predecessors: defineDagAllHops({
11976 incoming: true
11977 })
11978}); // Neighbourhood functions
11979//////////////////////////
11980
11981extend(elesfn$t, {
11982 neighborhood: cache(function (selector) {
11983 var elements = [];
11984 var nodes = this.nodes();
11985
11986 for (var i = 0; i < nodes.length; i++) {
11987 // for all nodes
11988 var node = nodes[i];
11989 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
11990
11991 for (var j = 0; j < connectedEdges.length; j++) {
11992 var edge = connectedEdges[j];
11993 var src = edge.source();
11994 var tgt = edge.target();
11995 var otherNode = node === src ? tgt : src; // need check in case of loop
11996
11997 if (otherNode.length > 0) {
11998 elements.push(otherNode[0]); // add node 1 hop away
11999 } // add connected edge
12000
12001
12002 elements.push(edge[0]);
12003 }
12004 }
12005
12006 return this.spawn(elements, {
12007 unique: true
12008 }).filter(selector);
12009 }, 'neighborhood'),
12010 closedNeighborhood: function closedNeighborhood(selector) {
12011 return this.neighborhood().add(this).filter(selector);
12012 },
12013 openNeighborhood: function openNeighborhood(selector) {
12014 return this.neighborhood(selector);
12015 }
12016}); // aliases
12017
12018elesfn$t.neighbourhood = elesfn$t.neighborhood;
12019elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12020elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12021/////////////////
12022
12023extend(elesfn$t, {
12024 source: cache(function sourceImpl(selector) {
12025 var ele = this[0];
12026 var src;
12027
12028 if (ele) {
12029 src = ele._private.source || ele.cy().collection();
12030 }
12031
12032 return src && selector ? src.filter(selector) : src;
12033 }, 'source'),
12034 target: cache(function targetImpl(selector) {
12035 var ele = this[0];
12036 var tgt;
12037
12038 if (ele) {
12039 tgt = ele._private.target || ele.cy().collection();
12040 }
12041
12042 return tgt && selector ? tgt.filter(selector) : tgt;
12043 }, 'target'),
12044 sources: defineSourceFunction({
12045 attr: 'source'
12046 }),
12047 targets: defineSourceFunction({
12048 attr: 'target'
12049 })
12050});
12051
12052function defineSourceFunction(params) {
12053 return function sourceImpl(selector) {
12054 var sources = [];
12055
12056 for (var i = 0; i < this.length; i++) {
12057 var ele = this[i];
12058 var src = ele._private[params.attr];
12059
12060 if (src) {
12061 sources.push(src);
12062 }
12063 }
12064
12065 return this.spawn(sources, {
12066 unique: true
12067 }).filter(selector);
12068 };
12069}
12070
12071extend(elesfn$t, {
12072 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12073 edgesTo: cache(defineEdgesWithFunction({
12074 thisIsSrc: true
12075 }), 'edgesTo')
12076});
12077
12078function defineEdgesWithFunction(params) {
12079 return function edgesWithImpl(otherNodes) {
12080 var elements = [];
12081 var cy = this._private.cy;
12082 var p = params || {}; // get elements if a selector is specified
12083
12084 if (string(otherNodes)) {
12085 otherNodes = cy.$(otherNodes);
12086 }
12087
12088 for (var h = 0; h < otherNodes.length; h++) {
12089 var edges = otherNodes[h]._private.edges;
12090
12091 for (var i = 0; i < edges.length; i++) {
12092 var edge = edges[i];
12093 var edgeData = edge._private.data;
12094 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12095 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12096 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12097
12098 if (!edgeConnectsThisAndOther) {
12099 continue;
12100 }
12101
12102 if (p.thisIsSrc || p.thisIsTgt) {
12103 if (p.thisIsSrc && !thisToOther) {
12104 continue;
12105 }
12106
12107 if (p.thisIsTgt && !otherToThis) {
12108 continue;
12109 }
12110 }
12111
12112 elements.push(edge);
12113 }
12114 }
12115
12116 return this.spawn(elements, {
12117 unique: true
12118 });
12119 };
12120}
12121
12122extend(elesfn$t, {
12123 connectedEdges: cache(function (selector) {
12124 var retEles = [];
12125 var eles = this;
12126
12127 for (var i = 0; i < eles.length; i++) {
12128 var node = eles[i];
12129
12130 if (!node.isNode()) {
12131 continue;
12132 }
12133
12134 var edges = node._private.edges;
12135
12136 for (var j = 0; j < edges.length; j++) {
12137 var edge = edges[j];
12138 retEles.push(edge);
12139 }
12140 }
12141
12142 return this.spawn(retEles, {
12143 unique: true
12144 }).filter(selector);
12145 }, 'connectedEdges'),
12146 connectedNodes: cache(function (selector) {
12147 var retEles = [];
12148 var eles = this;
12149
12150 for (var i = 0; i < eles.length; i++) {
12151 var edge = eles[i];
12152
12153 if (!edge.isEdge()) {
12154 continue;
12155 }
12156
12157 retEles.push(edge.source()[0]);
12158 retEles.push(edge.target()[0]);
12159 }
12160
12161 return this.spawn(retEles, {
12162 unique: true
12163 }).filter(selector);
12164 }, 'connectedNodes'),
12165 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12166 codirectedEdges: cache(defineParallelEdgesFunction({
12167 codirected: true
12168 }), 'codirectedEdges')
12169});
12170
12171function defineParallelEdgesFunction(params) {
12172 var defaults = {
12173 codirected: false
12174 };
12175 params = extend({}, defaults, params);
12176 return function parallelEdgesImpl(selector) {
12177 // micro-optimised for renderer
12178 var elements = [];
12179 var edges = this.edges();
12180 var p = params; // look at all the edges in the collection
12181
12182 for (var i = 0; i < edges.length; i++) {
12183 var edge1 = edges[i];
12184 var edge1_p = edge1._private;
12185 var src1 = edge1_p.source;
12186 var srcid1 = src1._private.data.id;
12187 var tgtid1 = edge1_p.data.target;
12188 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12189
12190 for (var j = 0; j < srcEdges1.length; j++) {
12191 var edge2 = srcEdges1[j];
12192 var edge2data = edge2._private.data;
12193 var tgtid2 = edge2data.target;
12194 var srcid2 = edge2data.source;
12195 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12196 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12197
12198 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12199 elements.push(edge2);
12200 }
12201 }
12202 }
12203
12204 return this.spawn(elements, {
12205 unique: true
12206 }).filter(selector);
12207 };
12208} // Misc functions
12209/////////////////
12210
12211
12212extend(elesfn$t, {
12213 components: function components(root) {
12214 var self = this;
12215 var cy = self.cy();
12216 var visited = cy.collection();
12217 var unvisited = root == null ? self.nodes() : root.nodes();
12218 var components = [];
12219
12220 if (root != null && unvisited.empty()) {
12221 // root may contain only edges
12222 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12223 }
12224
12225 var visitInComponent = function visitInComponent(node, component) {
12226 visited.merge(node);
12227 unvisited.unmerge(node);
12228 component.merge(node);
12229 };
12230
12231 if (unvisited.empty()) {
12232 return self.spawn();
12233 }
12234
12235 var _loop = function _loop() {
12236 // each iteration yields a component
12237 var cmpt = cy.collection();
12238 components.push(cmpt);
12239 var root = unvisited[0];
12240 visitInComponent(root, cmpt);
12241 self.bfs({
12242 directed: false,
12243 roots: root,
12244 visit: function visit(v) {
12245 return visitInComponent(v, cmpt);
12246 }
12247 });
12248 cmpt.forEach(function (node) {
12249 node.connectedEdges().forEach(function (e) {
12250 // connectedEdges() usually cached
12251 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12252 // has() is cheap
12253 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12254 }
12255 });
12256 });
12257 };
12258
12259 do {
12260 _loop();
12261 } while (unvisited.length > 0);
12262
12263 return components;
12264 },
12265 component: function component() {
12266 var ele = this[0];
12267 return ele.cy().mutableElements().components(ele)[0];
12268 }
12269});
12270elesfn$t.componentsOf = elesfn$t.components;
12271
12272var idFactory = {
12273 generate: function generate(cy, element, tryThisId) {
12274 var id = tryThisId != null ? tryThisId : uuid();
12275
12276 while (cy.hasElementWithId(id)) {
12277 id = uuid();
12278 }
12279
12280 return id;
12281 }
12282}; // represents a set of nodes, edges, or both together
12283
12284var Collection = function Collection(cy, elements, options) {
12285 if (cy === undefined || !core(cy)) {
12286 error('A collection must have a reference to the core');
12287 return;
12288 }
12289
12290 var map = new Map$1();
12291 var createdElements = false;
12292
12293 if (!elements) {
12294 elements = [];
12295 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12296 createdElements = true; // make elements from json and restore all at once later
12297
12298 var eles = [];
12299 var elesIds = new Set$1();
12300
12301 for (var i = 0, l = elements.length; i < l; i++) {
12302 var json = elements[i];
12303
12304 if (json.data == null) {
12305 json.data = {};
12306 }
12307
12308 var _data = json.data; // make sure newly created elements have valid ids
12309
12310 if (_data.id == null) {
12311 _data.id = idFactory.generate(cy, json);
12312 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12313 continue; // can't create element if prior id already exists
12314 }
12315
12316 var ele = new Element(cy, json, false);
12317 eles.push(ele);
12318 elesIds.add(_data.id);
12319 }
12320
12321 elements = eles;
12322 }
12323
12324 this.length = 0;
12325
12326 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12327 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12328
12329 if (element$1 == null) {
12330 continue;
12331 }
12332
12333 var id = element$1._private.data.id;
12334
12335 if (options == null || options.unique && !map.has(id)) {
12336 map.set(id, {
12337 index: this.length,
12338 ele: element$1
12339 });
12340 this[this.length] = element$1;
12341 this.length++;
12342 }
12343 }
12344
12345 this._private = {
12346 cy: cy,
12347 map: map
12348 }; // restore the elements if we created them from json
12349
12350 if (createdElements) {
12351 this.restore();
12352 }
12353}; // Functions
12354////////////////////////////////////////////////////////////////////////////////////////////////////
12355// keep the prototypes in sync (an element has the same functions as a collection)
12356// and use elefn and elesfn as shorthands to the prototypes
12357
12358
12359var elesfn$u = Element.prototype = Collection.prototype;
12360
12361elesfn$u.instanceString = function () {
12362 return 'collection';
12363};
12364
12365elesfn$u.spawn = function (cy, eles, opts) {
12366 if (!core(cy)) {
12367 // cy is optional
12368 opts = eles;
12369 eles = cy;
12370 cy = this.cy();
12371 }
12372
12373 return new Collection(cy, eles, opts);
12374};
12375
12376elesfn$u.spawnSelf = function () {
12377 return this.spawn(this);
12378};
12379
12380elesfn$u.cy = function () {
12381 return this._private.cy;
12382};
12383
12384elesfn$u.renderer = function () {
12385 return this._private.cy.renderer();
12386};
12387
12388elesfn$u.element = function () {
12389 return this[0];
12390};
12391
12392elesfn$u.collection = function () {
12393 if (collection(this)) {
12394 return this;
12395 } else {
12396 // an element
12397 return new Collection(this._private.cy, [this]);
12398 }
12399};
12400
12401elesfn$u.unique = function () {
12402 return new Collection(this._private.cy, this, {
12403 unique: true
12404 });
12405};
12406
12407elesfn$u.hasElementWithId = function (id) {
12408 id = '' + id; // id must be string
12409
12410 return this._private.map.has(id);
12411};
12412
12413elesfn$u.getElementById = function (id) {
12414 id = '' + id; // id must be string
12415
12416 var cy = this._private.cy;
12417
12418 var entry = this._private.map.get(id);
12419
12420 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12421};
12422
12423elesfn$u.$id = elesfn$u.getElementById;
12424
12425elesfn$u.poolIndex = function () {
12426 var cy = this._private.cy;
12427 var eles = cy._private.elements;
12428 var id = this[0]._private.data.id;
12429 return eles._private.map.get(id).index;
12430};
12431
12432elesfn$u.indexOf = function (ele) {
12433 var id = ele[0]._private.data.id;
12434 return this._private.map.get(id).index;
12435};
12436
12437elesfn$u.indexOfId = function (id) {
12438 id = '' + id; // id must be string
12439
12440 return this._private.map.get(id).index;
12441};
12442
12443elesfn$u.json = function (obj) {
12444 var ele = this.element();
12445 var cy = this.cy();
12446
12447 if (ele == null && obj) {
12448 return this;
12449 } // can't set to no eles
12450
12451
12452 if (ele == null) {
12453 return undefined;
12454 } // can't get from no eles
12455
12456
12457 var p = ele._private;
12458
12459 if (plainObject(obj)) {
12460 // set
12461 cy.startBatch();
12462
12463 if (obj.data) {
12464 ele.data(obj.data);
12465 var _data2 = p.data;
12466
12467 if (ele.isEdge()) {
12468 // source and target are immutable via data()
12469 var move = false;
12470 var spec = {};
12471 var src = obj.data.source;
12472 var tgt = obj.data.target;
12473
12474 if (src != null && src != _data2.source) {
12475 spec.source = '' + src; // id must be string
12476
12477 move = true;
12478 }
12479
12480 if (tgt != null && tgt != _data2.target) {
12481 spec.target = '' + tgt; // id must be string
12482
12483 move = true;
12484 }
12485
12486 if (move) {
12487 ele = ele.move(spec);
12488 }
12489 } else {
12490 // parent is immutable via data()
12491 var parent = obj.data.parent;
12492
12493 if ((parent != null || _data2.parent != null) && parent != _data2.parent) {
12494 if (parent === undefined) {
12495 // can't set undefined imperatively, so use null
12496 parent = null;
12497 }
12498
12499 if (parent != null) {
12500 parent = '' + parent; // id must be string
12501 }
12502
12503 ele = ele.move({
12504 parent: parent
12505 });
12506 }
12507 }
12508 }
12509
12510 if (obj.position) {
12511 ele.position(obj.position);
12512 } // ignore group -- immutable
12513
12514
12515 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12516 var obj_k = obj[k];
12517
12518 if (obj_k != null && obj_k !== p[k]) {
12519 if (obj_k) {
12520 ele[trueFnName]();
12521 } else {
12522 ele[falseFnName]();
12523 }
12524 }
12525 };
12526
12527 checkSwitch('removed', 'remove', 'restore');
12528 checkSwitch('selected', 'select', 'unselect');
12529 checkSwitch('selectable', 'selectify', 'unselectify');
12530 checkSwitch('locked', 'lock', 'unlock');
12531 checkSwitch('grabbable', 'grabify', 'ungrabify');
12532 checkSwitch('pannable', 'panify', 'unpanify');
12533
12534 if (obj.classes != null) {
12535 ele.classes(obj.classes);
12536 }
12537
12538 cy.endBatch();
12539 return this;
12540 } else if (obj === undefined) {
12541 // get
12542 var json = {
12543 data: copy(p.data),
12544 position: copy(p.position),
12545 group: p.group,
12546 removed: p.removed,
12547 selected: p.selected,
12548 selectable: p.selectable,
12549 locked: p.locked,
12550 grabbable: p.grabbable,
12551 pannable: p.pannable,
12552 classes: null
12553 };
12554 json.classes = '';
12555 var i = 0;
12556 p.classes.forEach(function (cls) {
12557 return json.classes += i++ === 0 ? cls : ' ' + cls;
12558 });
12559 return json;
12560 }
12561};
12562
12563elesfn$u.jsons = function () {
12564 var jsons = [];
12565
12566 for (var i = 0; i < this.length; i++) {
12567 var ele = this[i];
12568 var json = ele.json();
12569 jsons.push(json);
12570 }
12571
12572 return jsons;
12573};
12574
12575elesfn$u.clone = function () {
12576 var cy = this.cy();
12577 var elesArr = [];
12578
12579 for (var i = 0; i < this.length; i++) {
12580 var ele = this[i];
12581 var json = ele.json();
12582 var clone = new Element(cy, json, false); // NB no restore
12583
12584 elesArr.push(clone);
12585 }
12586
12587 return new Collection(cy, elesArr);
12588};
12589
12590elesfn$u.copy = elesfn$u.clone;
12591
12592elesfn$u.restore = function () {
12593 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12594 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12595 var self = this;
12596 var cy = self.cy();
12597 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12598 // restore the nodes first
12599
12600 var nodes = [];
12601 var edges = [];
12602 var elements;
12603
12604 for (var _i2 = 0, l = self.length; _i2 < l; _i2++) {
12605 var ele = self[_i2];
12606
12607 if (addToPool && !ele.removed()) {
12608 // don't need to handle this ele
12609 continue;
12610 } // keep nodes first in the array and edges after
12611
12612
12613 if (ele.isNode()) {
12614 // put to front of array if node
12615 nodes.push(ele);
12616 } else {
12617 // put to end of array if edge
12618 edges.push(ele);
12619 }
12620 }
12621
12622 elements = nodes.concat(edges);
12623 var i;
12624
12625 var removeFromElements = function removeFromElements() {
12626 elements.splice(i, 1);
12627 i--;
12628 }; // now, restore each element
12629
12630
12631 for (i = 0; i < elements.length; i++) {
12632 var _ele = elements[i];
12633 var _private = _ele._private;
12634 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12635
12636 _ele.clearTraversalCache(); // set id and validate
12637
12638
12639 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12640 _data3.id = idFactory.generate(cy, _ele);
12641 } else if (number(_data3.id)) {
12642 _data3.id = '' + _data3.id; // now it's a string
12643 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12644 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
12645
12646 removeFromElements();
12647 continue;
12648 } else if (cy.hasElementWithId(_data3.id)) {
12649 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12650
12651 removeFromElements();
12652 continue;
12653 }
12654
12655 var id = _data3.id; // id is finalised, now let's keep a ref
12656
12657 if (_ele.isNode()) {
12658 // extra checks for nodes
12659 var pos = _private.position; // make sure the nodes have a defined position
12660
12661 if (pos.x == null) {
12662 pos.x = 0;
12663 }
12664
12665 if (pos.y == null) {
12666 pos.y = 0;
12667 }
12668 }
12669
12670 if (_ele.isEdge()) {
12671 // extra checks for edges
12672 var edge = _ele;
12673 var fields = ['source', 'target'];
12674 var fieldsLength = fields.length;
12675 var badSourceOrTarget = false;
12676
12677 for (var j = 0; j < fieldsLength; j++) {
12678 var field = fields[j];
12679 var val = _data3[field];
12680
12681 if (number(val)) {
12682 val = _data3[field] = '' + _data3[field]; // now string
12683 }
12684
12685 if (val == null || val === '') {
12686 // can't create if source or target is not defined properly
12687 error('Can not create edge `' + id + '` with unspecified ' + field);
12688 badSourceOrTarget = true;
12689 } else if (!cy.hasElementWithId(val)) {
12690 // can't create edge if one of its nodes doesn't exist
12691 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12692 badSourceOrTarget = true;
12693 }
12694 }
12695
12696 if (badSourceOrTarget) {
12697 removeFromElements();
12698 continue;
12699 } // can't create this
12700
12701
12702 var src = cy.getElementById(_data3.source);
12703 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
12704
12705 if (src.same(tgt)) {
12706 src._private.edges.push(edge);
12707 } else {
12708 src._private.edges.push(edge);
12709
12710 tgt._private.edges.push(edge);
12711 }
12712
12713 edge._private.source = src;
12714 edge._private.target = tgt;
12715 } // if is edge
12716 // create mock ids / indexes maps for element so it can be used like collections
12717
12718
12719 _private.map = new Map$1();
12720
12721 _private.map.set(id, {
12722 ele: _ele,
12723 index: 0
12724 });
12725
12726 _private.removed = false;
12727
12728 if (addToPool) {
12729 cy.addToPool(_ele);
12730 }
12731 } // for each element
12732 // do compound node sanity checks
12733
12734
12735 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
12736 // each node
12737 var node = nodes[_i3];
12738 var _data4 = node._private.data;
12739
12740 if (number(_data4.parent)) {
12741 // then automake string
12742 _data4.parent = '' + _data4.parent;
12743 }
12744
12745 var parentId = _data4.parent;
12746 var specifiedParent = parentId != null;
12747
12748 if (specifiedParent) {
12749 var parent = cy.getElementById(parentId);
12750
12751 if (parent.empty()) {
12752 // non-existant parent; just remove it
12753 _data4.parent = undefined;
12754 } else {
12755 var selfAsParent = false;
12756 var ancestor = parent;
12757
12758 while (!ancestor.empty()) {
12759 if (node.same(ancestor)) {
12760 // mark self as parent and remove from data
12761 selfAsParent = true;
12762 _data4.parent = undefined; // remove parent reference
12763 // exit or we loop forever
12764
12765 break;
12766 }
12767
12768 ancestor = ancestor.parent();
12769 }
12770
12771 if (!selfAsParent) {
12772 // connect with children
12773 parent[0]._private.children.push(node);
12774
12775 node._private.parent = parent[0]; // let the core know we have a compound graph
12776
12777 cy_p.hasCompoundNodes = true;
12778 }
12779 } // else
12780
12781 } // if specified parent
12782
12783 } // for each node
12784
12785
12786 if (elements.length > 0) {
12787 var restored = new Collection(cy, elements);
12788
12789 for (var _i4 = 0; _i4 < restored.length; _i4++) {
12790 var _ele2 = restored[_i4];
12791
12792 if (_ele2.isNode()) {
12793 continue;
12794 } // adding an edge invalidates the traversal caches for the parallel edges
12795
12796
12797 _ele2.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
12798
12799
12800 _ele2.source().clearTraversalCache();
12801
12802 _ele2.target().clearTraversalCache();
12803 }
12804
12805 var toUpdateStyle;
12806
12807 if (cy_p.hasCompoundNodes) {
12808 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
12809 } else {
12810 toUpdateStyle = restored;
12811 }
12812
12813 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
12814
12815 if (notifyRenderer) {
12816 restored.emitAndNotify('add');
12817 } else if (addToPool) {
12818 restored.emit('add');
12819 }
12820 }
12821
12822 return self; // chainability
12823};
12824
12825elesfn$u.removed = function () {
12826 var ele = this[0];
12827 return ele && ele._private.removed;
12828};
12829
12830elesfn$u.inside = function () {
12831 var ele = this[0];
12832 return ele && !ele._private.removed;
12833};
12834
12835elesfn$u.remove = function () {
12836 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12837 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12838 var self = this;
12839 var elesToRemove = [];
12840 var elesToRemoveIds = {};
12841 var cy = self._private.cy; // add connected edges
12842
12843 function addConnectedEdges(node) {
12844 var edges = node._private.edges;
12845
12846 for (var i = 0; i < edges.length; i++) {
12847 add(edges[i]);
12848 }
12849 } // add descendant nodes
12850
12851
12852 function addChildren(node) {
12853 var children = node._private.children;
12854
12855 for (var i = 0; i < children.length; i++) {
12856 add(children[i]);
12857 }
12858 }
12859
12860 function add(ele) {
12861 var alreadyAdded = elesToRemoveIds[ele.id()];
12862
12863 if (removeFromPool && ele.removed() || alreadyAdded) {
12864 return;
12865 } else {
12866 elesToRemoveIds[ele.id()] = true;
12867 }
12868
12869 if (ele.isNode()) {
12870 elesToRemove.push(ele); // nodes are removed last
12871
12872 addConnectedEdges(ele);
12873 addChildren(ele);
12874 } else {
12875 elesToRemove.unshift(ele); // edges are removed first
12876 }
12877 } // make the list of elements to remove
12878 // (may be removing more than specified due to connected edges etc)
12879
12880
12881 for (var i = 0, l = self.length; i < l; i++) {
12882 var ele = self[i];
12883 add(ele);
12884 }
12885
12886 function removeEdgeRef(node, edge) {
12887 var connectedEdges = node._private.edges;
12888 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
12889
12890 node.clearTraversalCache();
12891 }
12892
12893 function removeParallelRef(pllEdge) {
12894 // removing an edge invalidates the traversal caches for the parallel edges
12895 pllEdge.clearTraversalCache();
12896 }
12897
12898 var alteredParents = [];
12899 alteredParents.ids = {};
12900
12901 function removeChildRef(parent, ele) {
12902 ele = ele[0];
12903 parent = parent[0];
12904 var children = parent._private.children;
12905 var pid = parent.id();
12906 removeFromArray(children, ele); // remove parent => child ref
12907
12908 ele._private.parent = null; // remove child => parent ref
12909
12910 if (!alteredParents.ids[pid]) {
12911 alteredParents.ids[pid] = true;
12912 alteredParents.push(parent);
12913 }
12914 }
12915
12916 self.dirtyCompoundBoundsCache();
12917
12918 if (removeFromPool) {
12919 cy.removeFromPool(elesToRemove); // remove from core pool
12920 }
12921
12922 for (var _i5 = 0; _i5 < elesToRemove.length; _i5++) {
12923 var _ele3 = elesToRemove[_i5];
12924
12925 if (_ele3.isEdge()) {
12926 // remove references to this edge in its connected nodes
12927 var src = _ele3.source()[0];
12928
12929 var tgt = _ele3.target()[0];
12930
12931 removeEdgeRef(src, _ele3);
12932 removeEdgeRef(tgt, _ele3);
12933
12934 var pllEdges = _ele3.parallelEdges();
12935
12936 for (var j = 0; j < pllEdges.length; j++) {
12937 var pllEdge = pllEdges[j];
12938 removeParallelRef(pllEdge);
12939
12940 if (pllEdge.isBundledBezier()) {
12941 pllEdge.dirtyBoundingBoxCache();
12942 }
12943 }
12944 } else {
12945 // remove reference to parent
12946 var parent = _ele3.parent();
12947
12948 if (parent.length !== 0) {
12949 removeChildRef(parent, _ele3);
12950 }
12951 }
12952
12953 if (removeFromPool) {
12954 // mark as removed
12955 _ele3._private.removed = true;
12956 }
12957 } // check to see if we have a compound graph or not
12958
12959
12960 var elesStillInside = cy._private.elements;
12961 cy._private.hasCompoundNodes = false;
12962
12963 for (var _i6 = 0; _i6 < elesStillInside.length; _i6++) {
12964 var _ele4 = elesStillInside[_i6];
12965
12966 if (_ele4.isParent()) {
12967 cy._private.hasCompoundNodes = true;
12968 break;
12969 }
12970 }
12971
12972 var removedElements = new Collection(this.cy(), elesToRemove);
12973
12974 if (removedElements.size() > 0) {
12975 // must manually notify since trigger won't do this automatically once removed
12976 if (notifyRenderer) {
12977 removedElements.emitAndNotify('remove');
12978 } else if (removeFromPool) {
12979 removedElements.emit('remove');
12980 }
12981 } // the parents who were modified by the removal need their style updated
12982
12983
12984 for (var _i7 = 0; _i7 < alteredParents.length; _i7++) {
12985 var _ele5 = alteredParents[_i7];
12986
12987 if (!removeFromPool || !_ele5.removed()) {
12988 _ele5.updateStyle();
12989 }
12990 }
12991
12992 return removedElements;
12993};
12994
12995elesfn$u.move = function (struct) {
12996 var cy = this._private.cy;
12997 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
12998 // (our calls to remove/restore do not remove from the graph or make events)
12999
13000 var notifyRenderer = false;
13001 var modifyPool = false;
13002
13003 var toString = function toString(id) {
13004 return id == null ? id : '' + id;
13005 }; // id must be string
13006
13007
13008 if (struct.source !== undefined || struct.target !== undefined) {
13009 var srcId = toString(struct.source);
13010 var tgtId = toString(struct.target);
13011 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13012 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13013
13014 if (srcExists || tgtExists) {
13015 cy.batch(function () {
13016 // avoid duplicate style updates
13017 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13018
13019 eles.emitAndNotify('moveout');
13020
13021 for (var i = 0; i < eles.length; i++) {
13022 var ele = eles[i];
13023 var _data5 = ele._private.data;
13024
13025 if (ele.isEdge()) {
13026 if (srcExists) {
13027 _data5.source = srcId;
13028 }
13029
13030 if (tgtExists) {
13031 _data5.target = tgtId;
13032 }
13033 }
13034 }
13035
13036 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13037 });
13038 eles.emitAndNotify('move');
13039 }
13040 } else if (struct.parent !== undefined) {
13041 // move node to new parent
13042 var parentId = toString(struct.parent);
13043 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13044
13045 if (parentExists) {
13046 var pidToAssign = parentId === null ? undefined : parentId;
13047 cy.batch(function () {
13048 // avoid duplicate style updates
13049 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13050
13051 updated.emitAndNotify('moveout');
13052
13053 for (var i = 0; i < eles.length; i++) {
13054 var ele = eles[i];
13055 var _data6 = ele._private.data;
13056
13057 if (ele.isNode()) {
13058 _data6.parent = pidToAssign;
13059 }
13060 }
13061
13062 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13063 });
13064 eles.emitAndNotify('move');
13065 }
13066 }
13067
13068 return this;
13069};
13070
13071[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) {
13072 extend(elesfn$u, props);
13073});
13074
13075var corefn = {
13076 add: function add(opts) {
13077 var elements;
13078 var cy = this; // add the elements
13079
13080 if (elementOrCollection(opts)) {
13081 var eles = opts;
13082
13083 if (eles._private.cy === cy) {
13084 // same instance => just restore
13085 elements = eles.restore();
13086 } else {
13087 // otherwise, copy from json
13088 var jsons = [];
13089
13090 for (var i = 0; i < eles.length; i++) {
13091 var ele = eles[i];
13092 jsons.push(ele.json());
13093 }
13094
13095 elements = new Collection(cy, jsons);
13096 }
13097 } // specify an array of options
13098 else if (array(opts)) {
13099 var _jsons = opts;
13100 elements = new Collection(cy, _jsons);
13101 } // specify via opts.nodes and opts.edges
13102 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13103 var elesByGroup = opts;
13104 var _jsons2 = [];
13105 var grs = ['nodes', 'edges'];
13106
13107 for (var _i = 0, il = grs.length; _i < il; _i++) {
13108 var group = grs[_i];
13109 var elesArray = elesByGroup[group];
13110
13111 if (array(elesArray)) {
13112 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13113 var json = extend({
13114 group: group
13115 }, elesArray[j]);
13116
13117 _jsons2.push(json);
13118 }
13119 }
13120 }
13121
13122 elements = new Collection(cy, _jsons2);
13123 } // specify options for one element
13124 else {
13125 var _json = opts;
13126 elements = new Element(cy, _json).collection();
13127 }
13128
13129 return elements;
13130 },
13131 remove: function remove(collection) {
13132 if (elementOrCollection(collection)) ; else if (string(collection)) {
13133 var selector = collection;
13134 collection = this.$(selector);
13135 }
13136
13137 return collection.remove();
13138 }
13139};
13140
13141/* global Float32Array */
13142
13143/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13144function generateCubicBezier(mX1, mY1, mX2, mY2) {
13145 var NEWTON_ITERATIONS = 4,
13146 NEWTON_MIN_SLOPE = 0.001,
13147 SUBDIVISION_PRECISION = 0.0000001,
13148 SUBDIVISION_MAX_ITERATIONS = 10,
13149 kSplineTableSize = 11,
13150 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13151 float32ArraySupported = typeof Float32Array !== 'undefined';
13152 /* Must contain four arguments. */
13153
13154 if (arguments.length !== 4) {
13155 return false;
13156 }
13157 /* Arguments must be numbers. */
13158
13159
13160 for (var i = 0; i < 4; ++i) {
13161 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13162 return false;
13163 }
13164 }
13165 /* X values must be in the [0, 1] range. */
13166
13167
13168 mX1 = Math.min(mX1, 1);
13169 mX2 = Math.min(mX2, 1);
13170 mX1 = Math.max(mX1, 0);
13171 mX2 = Math.max(mX2, 0);
13172 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13173
13174 function A(aA1, aA2) {
13175 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13176 }
13177
13178 function B(aA1, aA2) {
13179 return 3.0 * aA2 - 6.0 * aA1;
13180 }
13181
13182 function C(aA1) {
13183 return 3.0 * aA1;
13184 }
13185
13186 function calcBezier(aT, aA1, aA2) {
13187 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13188 }
13189
13190 function getSlope(aT, aA1, aA2) {
13191 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13192 }
13193
13194 function newtonRaphsonIterate(aX, aGuessT) {
13195 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13196 var currentSlope = getSlope(aGuessT, mX1, mX2);
13197
13198 if (currentSlope === 0.0) {
13199 return aGuessT;
13200 }
13201
13202 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13203 aGuessT -= currentX / currentSlope;
13204 }
13205
13206 return aGuessT;
13207 }
13208
13209 function calcSampleValues() {
13210 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13211 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13212 }
13213 }
13214
13215 function binarySubdivide(aX, aA, aB) {
13216 var currentX,
13217 currentT,
13218 i = 0;
13219
13220 do {
13221 currentT = aA + (aB - aA) / 2.0;
13222 currentX = calcBezier(currentT, mX1, mX2) - aX;
13223
13224 if (currentX > 0.0) {
13225 aB = currentT;
13226 } else {
13227 aA = currentT;
13228 }
13229 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13230
13231 return currentT;
13232 }
13233
13234 function getTForX(aX) {
13235 var intervalStart = 0.0,
13236 currentSample = 1,
13237 lastSample = kSplineTableSize - 1;
13238
13239 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13240 intervalStart += kSampleStepSize;
13241 }
13242
13243 --currentSample;
13244 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13245 guessForT = intervalStart + dist * kSampleStepSize,
13246 initialSlope = getSlope(guessForT, mX1, mX2);
13247
13248 if (initialSlope >= NEWTON_MIN_SLOPE) {
13249 return newtonRaphsonIterate(aX, guessForT);
13250 } else if (initialSlope === 0.0) {
13251 return guessForT;
13252 } else {
13253 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13254 }
13255 }
13256
13257 var _precomputed = false;
13258
13259 function precompute() {
13260 _precomputed = true;
13261
13262 if (mX1 !== mY1 || mX2 !== mY2) {
13263 calcSampleValues();
13264 }
13265 }
13266
13267 var f = function f(aX) {
13268 if (!_precomputed) {
13269 precompute();
13270 }
13271
13272 if (mX1 === mY1 && mX2 === mY2) {
13273 return aX;
13274 }
13275
13276 if (aX === 0) {
13277 return 0;
13278 }
13279
13280 if (aX === 1) {
13281 return 1;
13282 }
13283
13284 return calcBezier(getTForX(aX), mY1, mY2);
13285 };
13286
13287 f.getControlPoints = function () {
13288 return [{
13289 x: mX1,
13290 y: mY1
13291 }, {
13292 x: mX2,
13293 y: mY2
13294 }];
13295 };
13296
13297 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13298
13299 f.toString = function () {
13300 return str;
13301 };
13302
13303 return f;
13304}
13305
13306/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13307
13308/* 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
13309 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13310var generateSpringRK4 = function () {
13311 function springAccelerationForState(state) {
13312 return -state.tension * state.x - state.friction * state.v;
13313 }
13314
13315 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13316 var state = {
13317 x: initialState.x + derivative.dx * dt,
13318 v: initialState.v + derivative.dv * dt,
13319 tension: initialState.tension,
13320 friction: initialState.friction
13321 };
13322 return {
13323 dx: state.v,
13324 dv: springAccelerationForState(state)
13325 };
13326 }
13327
13328 function springIntegrateState(state, dt) {
13329 var a = {
13330 dx: state.v,
13331 dv: springAccelerationForState(state)
13332 },
13333 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13334 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13335 d = springEvaluateStateWithDerivative(state, dt, c),
13336 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13337 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13338 state.x = state.x + dxdt * dt;
13339 state.v = state.v + dvdt * dt;
13340 return state;
13341 }
13342
13343 return function springRK4Factory(tension, friction, duration) {
13344 var initState = {
13345 x: -1,
13346 v: 0,
13347 tension: null,
13348 friction: null
13349 },
13350 path = [0],
13351 time_lapsed = 0,
13352 tolerance = 1 / 10000,
13353 DT = 16 / 1000,
13354 have_duration,
13355 dt,
13356 last_state;
13357 tension = parseFloat(tension) || 500;
13358 friction = parseFloat(friction) || 20;
13359 duration = duration || null;
13360 initState.tension = tension;
13361 initState.friction = friction;
13362 have_duration = duration !== null;
13363 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13364
13365 if (have_duration) {
13366 /* Run the simulation without a duration. */
13367 time_lapsed = springRK4Factory(tension, friction);
13368 /* Compute the adjusted time delta. */
13369
13370 dt = time_lapsed / duration * DT;
13371 } else {
13372 dt = DT;
13373 }
13374
13375 for (;;) {
13376 /* Next/step function .*/
13377 last_state = springIntegrateState(last_state || initState, dt);
13378 /* Store the position. */
13379
13380 path.push(1 + last_state.x);
13381 time_lapsed += 16;
13382 /* If the change threshold is reached, break. */
13383
13384 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13385 break;
13386 }
13387 }
13388 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13389 computed path and returns a snapshot of the position according to a given percentComplete. */
13390
13391
13392 return !have_duration ? time_lapsed : function (percentComplete) {
13393 return path[percentComplete * (path.length - 1) | 0];
13394 };
13395 };
13396}();
13397
13398var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13399 var bezier = generateCubicBezier(t1, p1, t2, p2);
13400 return function (start, end, percent) {
13401 return start + (end - start) * bezier(percent);
13402 };
13403};
13404
13405var easings = {
13406 'linear': function linear(start, end, percent) {
13407 return start + (end - start) * percent;
13408 },
13409 // default easings
13410 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13411 'ease-in': cubicBezier(0.42, 0, 1, 1),
13412 'ease-out': cubicBezier(0, 0, 0.58, 1),
13413 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13414 // sine
13415 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13416 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13417 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13418 // quad
13419 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13420 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13421 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13422 // cubic
13423 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13424 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13425 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13426 // quart
13427 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13428 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13429 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13430 // quint
13431 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13432 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13433 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13434 // expo
13435 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13436 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13437 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13438 // circ
13439 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13440 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13441 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13442 // user param easings...
13443 'spring': function spring(tension, friction, duration) {
13444 if (duration === 0) {
13445 // can't get a spring w/ duration 0
13446 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13447 }
13448
13449 var spring = generateSpringRK4(tension, friction, duration);
13450 return function (start, end, percent) {
13451 return start + (end - start) * spring(percent);
13452 };
13453 },
13454 'cubic-bezier': cubicBezier
13455};
13456
13457function getEasedValue(type, start, end, percent, easingFn) {
13458 if (percent === 1) {
13459 return end;
13460 }
13461
13462 var val = easingFn(start, end, percent);
13463
13464 if (type == null) {
13465 return val;
13466 }
13467
13468 if (type.roundValue || type.color) {
13469 val = Math.round(val);
13470 }
13471
13472 if (type.min !== undefined) {
13473 val = Math.max(val, type.min);
13474 }
13475
13476 if (type.max !== undefined) {
13477 val = Math.min(val, type.max);
13478 }
13479
13480 return val;
13481}
13482
13483function getValue(prop, spec) {
13484 if (prop.pfValue != null || prop.value != null) {
13485 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13486 return prop.pfValue;
13487 } else {
13488 return prop.value;
13489 }
13490 } else {
13491 return prop;
13492 }
13493}
13494
13495function ease(startProp, endProp, percent, easingFn, propSpec) {
13496 var type = propSpec != null ? propSpec.type : null;
13497
13498 if (percent < 0) {
13499 percent = 0;
13500 } else if (percent > 1) {
13501 percent = 1;
13502 }
13503
13504 var start = getValue(startProp, propSpec);
13505 var end = getValue(endProp, propSpec);
13506
13507 if (number(start) && number(end)) {
13508 return getEasedValue(type, start, end, percent, easingFn);
13509 } else if (array(start) && array(end)) {
13510 var easedArr = [];
13511
13512 for (var i = 0; i < end.length; i++) {
13513 var si = start[i];
13514 var ei = end[i];
13515
13516 if (si != null && ei != null) {
13517 var val = getEasedValue(type, si, ei, percent, easingFn);
13518 easedArr.push(val);
13519 } else {
13520 easedArr.push(ei);
13521 }
13522 }
13523
13524 return easedArr;
13525 }
13526
13527 return undefined;
13528}
13529
13530function step(self, ani, now, isCore) {
13531 var isEles = !isCore;
13532 var _p = self._private;
13533 var ani_p = ani._private;
13534 var pEasing = ani_p.easing;
13535 var startTime = ani_p.startTime;
13536 var cy = isCore ? self : self.cy();
13537 var style = cy.style();
13538
13539 if (!ani_p.easingImpl) {
13540 if (pEasing == null) {
13541 // use default
13542 ani_p.easingImpl = easings['linear'];
13543 } else {
13544 // then define w/ name
13545 var easingVals;
13546
13547 if (string(pEasing)) {
13548 var easingProp = style.parse('transition-timing-function', pEasing);
13549 easingVals = easingProp.value;
13550 } else {
13551 // then assume preparsed array
13552 easingVals = pEasing;
13553 }
13554
13555 var name, args;
13556
13557 if (string(easingVals)) {
13558 name = easingVals;
13559 args = [];
13560 } else {
13561 name = easingVals[1];
13562 args = easingVals.slice(2).map(function (n) {
13563 return +n;
13564 });
13565 }
13566
13567 if (args.length > 0) {
13568 // create with args
13569 if (name === 'spring') {
13570 args.push(ani_p.duration); // need duration to generate spring
13571 }
13572
13573 ani_p.easingImpl = easings[name].apply(null, args);
13574 } else {
13575 // static impl by name
13576 ani_p.easingImpl = easings[name];
13577 }
13578 }
13579 }
13580
13581 var easing = ani_p.easingImpl;
13582 var percent;
13583
13584 if (ani_p.duration === 0) {
13585 percent = 1;
13586 } else {
13587 percent = (now - startTime) / ani_p.duration;
13588 }
13589
13590 if (ani_p.applying) {
13591 percent = ani_p.progress;
13592 }
13593
13594 if (percent < 0) {
13595 percent = 0;
13596 } else if (percent > 1) {
13597 percent = 1;
13598 }
13599
13600 if (ani_p.delay == null) {
13601 // then update
13602 var startPos = ani_p.startPosition;
13603 var endPos = ani_p.position;
13604
13605 if (endPos && isEles && !self.locked()) {
13606 var newPos = {};
13607
13608 if (valid(startPos.x, endPos.x)) {
13609 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13610 }
13611
13612 if (valid(startPos.y, endPos.y)) {
13613 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13614 }
13615
13616 self.position(newPos);
13617 }
13618
13619 var startPan = ani_p.startPan;
13620 var endPan = ani_p.pan;
13621 var pan = _p.pan;
13622 var animatingPan = endPan != null && isCore;
13623
13624 if (animatingPan) {
13625 if (valid(startPan.x, endPan.x)) {
13626 pan.x = ease(startPan.x, endPan.x, percent, easing);
13627 }
13628
13629 if (valid(startPan.y, endPan.y)) {
13630 pan.y = ease(startPan.y, endPan.y, percent, easing);
13631 }
13632
13633 self.emit('pan');
13634 }
13635
13636 var startZoom = ani_p.startZoom;
13637 var endZoom = ani_p.zoom;
13638 var animatingZoom = endZoom != null && isCore;
13639
13640 if (animatingZoom) {
13641 if (valid(startZoom, endZoom)) {
13642 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13643 }
13644
13645 self.emit('zoom');
13646 }
13647
13648 if (animatingPan || animatingZoom) {
13649 self.emit('viewport');
13650 }
13651
13652 var props = ani_p.style;
13653
13654 if (props && props.length > 0 && isEles) {
13655 for (var i = 0; i < props.length; i++) {
13656 var prop = props[i];
13657 var _name = prop.name;
13658 var end = prop;
13659 var start = ani_p.startStyle[_name];
13660 var propSpec = style.properties[start.name];
13661 var easedVal = ease(start, end, percent, easing, propSpec);
13662 style.overrideBypass(self, _name, easedVal);
13663 } // for props
13664
13665
13666 self.emit('style');
13667 } // if
13668
13669 }
13670
13671 ani_p.progress = percent;
13672 return percent;
13673}
13674
13675function valid(start, end) {
13676 if (start == null || end == null) {
13677 return false;
13678 }
13679
13680 if (number(start) && number(end)) {
13681 return true;
13682 } else if (start && end) {
13683 return true;
13684 }
13685
13686 return false;
13687}
13688
13689function startAnimation(self, ani, now, isCore) {
13690 var ani_p = ani._private;
13691 ani_p.started = true;
13692 ani_p.startTime = now - ani_p.progress * ani_p.duration;
13693}
13694
13695function stepAll(now, cy) {
13696 var eles = cy._private.aniEles;
13697 var doneEles = [];
13698
13699 function stepOne(ele, isCore) {
13700 var _p = ele._private;
13701 var current = _p.animation.current;
13702 var queue = _p.animation.queue;
13703 var ranAnis = false; // cancel all animations on display:none ele
13704
13705 if (!isCore && ele.pstyle('display').value === 'none') {
13706 // put all current and queue animations in this tick's current list
13707 // and empty the lists for the element
13708 current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); // stop all animations
13709
13710 for (var i = 0; i < current.length; i++) {
13711 current[i].stop();
13712 }
13713 } // if nothing currently animating, get something from the queue
13714
13715
13716 if (current.length === 0) {
13717 var next = queue.shift();
13718
13719 if (next) {
13720 current.push(next);
13721 }
13722 }
13723
13724 var callbacks = function callbacks(_callbacks) {
13725 for (var j = _callbacks.length - 1; j >= 0; j--) {
13726 var cb = _callbacks[j];
13727 cb();
13728 }
13729
13730 _callbacks.splice(0, _callbacks.length);
13731 }; // step and remove if done
13732
13733
13734 for (var _i = current.length - 1; _i >= 0; _i--) {
13735 var ani = current[_i];
13736 var ani_p = ani._private;
13737
13738 if (ani_p.stopped) {
13739 current.splice(_i, 1);
13740 ani_p.hooked = false;
13741 ani_p.playing = false;
13742 ani_p.started = false;
13743 callbacks(ani_p.frames);
13744 continue;
13745 }
13746
13747 if (!ani_p.playing && !ani_p.applying) {
13748 continue;
13749 } // an apply() while playing shouldn't do anything
13750
13751
13752 if (ani_p.playing && ani_p.applying) {
13753 ani_p.applying = false;
13754 }
13755
13756 if (!ani_p.started) {
13757 startAnimation(ele, ani, now);
13758 }
13759
13760 step(ele, ani, now, isCore);
13761
13762 if (ani_p.applying) {
13763 ani_p.applying = false;
13764 }
13765
13766 callbacks(ani_p.frames);
13767
13768 if (ani_p.step != null) {
13769 ani_p.step(now);
13770 }
13771
13772 if (ani.completed()) {
13773 current.splice(_i, 1);
13774 ani_p.hooked = false;
13775 ani_p.playing = false;
13776 ani_p.started = false;
13777 callbacks(ani_p.completes);
13778 }
13779
13780 ranAnis = true;
13781 }
13782
13783 if (!isCore && current.length === 0 && queue.length === 0) {
13784 doneEles.push(ele);
13785 }
13786
13787 return ranAnis;
13788 } // stepElement
13789 // handle all eles
13790
13791
13792 var ranEleAni = false;
13793
13794 for (var e = 0; e < eles.length; e++) {
13795 var ele = eles[e];
13796 var handledThisEle = stepOne(ele);
13797 ranEleAni = ranEleAni || handledThisEle;
13798 } // each element
13799
13800
13801 var ranCoreAni = stepOne(cy, true); // notify renderer
13802
13803 if (ranEleAni || ranCoreAni) {
13804 if (eles.length > 0) {
13805 cy.notify('draw', eles);
13806 } else {
13807 cy.notify('draw');
13808 }
13809 } // remove elements from list of currently animating if its queues are empty
13810
13811
13812 eles.unmerge(doneEles);
13813 cy.emit('step');
13814} // stepAll
13815
13816var corefn$1 = {
13817 // pull in animation functions
13818 animate: define$3.animate(),
13819 animation: define$3.animation(),
13820 animated: define$3.animated(),
13821 clearQueue: define$3.clearQueue(),
13822 delay: define$3.delay(),
13823 delayAnimation: define$3.delayAnimation(),
13824 stop: define$3.stop(),
13825 addToAnimationPool: function addToAnimationPool(eles) {
13826 var cy = this;
13827
13828 if (!cy.styleEnabled()) {
13829 return;
13830 } // save cycles when no style used
13831
13832
13833 cy._private.aniEles.merge(eles);
13834 },
13835 stopAnimationLoop: function stopAnimationLoop() {
13836 this._private.animationsRunning = false;
13837 },
13838 startAnimationLoop: function startAnimationLoop() {
13839 var cy = this;
13840 cy._private.animationsRunning = true;
13841
13842 if (!cy.styleEnabled()) {
13843 return;
13844 } // save cycles when no style used
13845 // NB the animation loop will exec in headless environments if style enabled
13846 // and explicit cy.destroy() is necessary to stop the loop
13847
13848
13849 function headlessStep() {
13850 if (!cy._private.animationsRunning) {
13851 return;
13852 }
13853
13854 requestAnimationFrame(function animationStep(now) {
13855 stepAll(now, cy);
13856 headlessStep();
13857 });
13858 }
13859
13860 var renderer = cy.renderer();
13861
13862 if (renderer && renderer.beforeRender) {
13863 // let the renderer schedule animations
13864 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
13865 stepAll(now, cy);
13866 }, renderer.beforeRenderPriorities.animations);
13867 } else {
13868 // manage the animation loop ourselves
13869 headlessStep(); // first call
13870 }
13871 }
13872};
13873
13874var emitterOptions$1 = {
13875 qualifierCompare: function qualifierCompare(selector1, selector2) {
13876 if (selector1 == null || selector2 == null) {
13877 return selector1 == null && selector2 == null;
13878 } else {
13879 return selector1.sameText(selector2);
13880 }
13881 },
13882 eventMatches: function eventMatches(cy, listener, eventObj) {
13883 var selector = listener.qualifier;
13884
13885 if (selector != null) {
13886 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
13887 }
13888
13889 return true;
13890 },
13891 addEventFields: function addEventFields(cy, evt) {
13892 evt.cy = cy;
13893 evt.target = cy;
13894 },
13895 callbackContext: function callbackContext(cy, listener, eventObj) {
13896 return listener.qualifier != null ? eventObj.target : cy;
13897 }
13898};
13899
13900var argSelector$1 = function argSelector(arg) {
13901 if (string(arg)) {
13902 return new Selector(arg);
13903 } else {
13904 return arg;
13905 }
13906};
13907
13908var elesfn$v = {
13909 createEmitter: function createEmitter() {
13910 var _p = this._private;
13911
13912 if (!_p.emitter) {
13913 _p.emitter = new Emitter(emitterOptions$1, this);
13914 }
13915
13916 return this;
13917 },
13918 emitter: function emitter() {
13919 return this._private.emitter;
13920 },
13921 on: function on(events, selector, callback) {
13922 this.emitter().on(events, argSelector$1(selector), callback);
13923 return this;
13924 },
13925 removeListener: function removeListener(events, selector, callback) {
13926 this.emitter().removeListener(events, argSelector$1(selector), callback);
13927 return this;
13928 },
13929 removeAllListeners: function removeAllListeners() {
13930 this.emitter().removeAllListeners();
13931 return this;
13932 },
13933 one: function one(events, selector, callback) {
13934 this.emitter().one(events, argSelector$1(selector), callback);
13935 return this;
13936 },
13937 once: function once(events, selector, callback) {
13938 this.emitter().one(events, argSelector$1(selector), callback);
13939 return this;
13940 },
13941 emit: function emit(events, extraParams) {
13942 this.emitter().emit(events, extraParams);
13943 return this;
13944 },
13945 emitAndNotify: function emitAndNotify(event, eles) {
13946 this.emit(event);
13947 this.notify(event, eles);
13948 return this;
13949 }
13950};
13951define$3.eventAliasesOn(elesfn$v);
13952
13953var corefn$2 = {
13954 png: function png(options) {
13955 var renderer = this._private.renderer;
13956 options = options || {};
13957 return renderer.png(options);
13958 },
13959 jpg: function jpg(options) {
13960 var renderer = this._private.renderer;
13961 options = options || {};
13962 options.bg = options.bg || '#fff';
13963 return renderer.jpg(options);
13964 }
13965};
13966corefn$2.jpeg = corefn$2.jpg;
13967
13968var corefn$3 = {
13969 layout: function layout(options) {
13970 var cy = this;
13971
13972 if (options == null) {
13973 error('Layout options must be specified to make a layout');
13974 return;
13975 }
13976
13977 if (options.name == null) {
13978 error('A `name` must be specified to make a layout');
13979 return;
13980 }
13981
13982 var name = options.name;
13983 var Layout = cy.extension('layout', name);
13984
13985 if (Layout == null) {
13986 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
13987 return;
13988 }
13989
13990 var eles;
13991
13992 if (string(options.eles)) {
13993 eles = cy.$(options.eles);
13994 } else {
13995 eles = options.eles != null ? options.eles : cy.$();
13996 }
13997
13998 var layout = new Layout(extend({}, options, {
13999 cy: cy,
14000 eles: eles
14001 }));
14002 return layout;
14003 }
14004};
14005corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14006
14007var corefn$4 = {
14008 notify: function notify(eventName, eventEles) {
14009 var _p = this._private;
14010
14011 if (this.batching()) {
14012 _p.batchNotifications = _p.batchNotifications || {};
14013 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14014
14015 if (eventEles != null) {
14016 eles.merge(eventEles);
14017 }
14018
14019 return; // notifications are disabled during batching
14020 }
14021
14022 if (!_p.notificationsEnabled) {
14023 return;
14024 } // exit on disabled
14025
14026
14027 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14028
14029 if (this.destroyed() || !renderer) {
14030 return;
14031 }
14032
14033 renderer.notify(eventName, eventEles);
14034 },
14035 notifications: function notifications(bool) {
14036 var p = this._private;
14037
14038 if (bool === undefined) {
14039 return p.notificationsEnabled;
14040 } else {
14041 p.notificationsEnabled = bool ? true : false;
14042 }
14043
14044 return this;
14045 },
14046 noNotifications: function noNotifications(callback) {
14047 this.notifications(false);
14048 callback();
14049 this.notifications(true);
14050 },
14051 batching: function batching() {
14052 return this._private.batchCount > 0;
14053 },
14054 startBatch: function startBatch() {
14055 var _p = this._private;
14056
14057 if (_p.batchCount == null) {
14058 _p.batchCount = 0;
14059 }
14060
14061 if (_p.batchCount === 0) {
14062 _p.batchStyleEles = this.collection();
14063 _p.batchNotifications = {};
14064 }
14065
14066 _p.batchCount++;
14067 return this;
14068 },
14069 endBatch: function endBatch() {
14070 var _p = this._private;
14071
14072 if (_p.batchCount === 0) {
14073 return this;
14074 }
14075
14076 _p.batchCount--;
14077
14078 if (_p.batchCount === 0) {
14079 // update style for dirty eles
14080 _p.batchStyleEles.updateStyle();
14081
14082 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14083
14084 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14085 var eles = _p.batchNotifications[eventName];
14086
14087 if (eles.empty()) {
14088 renderer.notify(eventName);
14089 } else {
14090 renderer.notify(eventName, eles);
14091 }
14092 });
14093 }
14094
14095 return this;
14096 },
14097 batch: function batch(callback) {
14098 this.startBatch();
14099 callback();
14100 this.endBatch();
14101 return this;
14102 },
14103 // for backwards compatibility
14104 batchData: function batchData(map) {
14105 var cy = this;
14106 return this.batch(function () {
14107 var ids = Object.keys(map);
14108
14109 for (var i = 0; i < ids.length; i++) {
14110 var id = ids[i];
14111 var data = map[id];
14112 var ele = cy.getElementById(id);
14113 ele.data(data);
14114 }
14115 });
14116 }
14117};
14118
14119var rendererDefaults = defaults({
14120 hideEdgesOnViewport: false,
14121 textureOnViewport: false,
14122 motionBlur: false,
14123 motionBlurOpacity: 0.05,
14124 pixelRatio: undefined,
14125 desktopTapThreshold: 4,
14126 touchTapThreshold: 8,
14127 wheelSensitivity: 1,
14128 debug: false,
14129 showFps: false
14130});
14131var corefn$5 = {
14132 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14133 var r = this._private.renderer;
14134 r.renderTo(context, zoom, pan, pxRatio);
14135 return this;
14136 },
14137 renderer: function renderer() {
14138 return this._private.renderer;
14139 },
14140 forceRender: function forceRender() {
14141 this.notify('draw');
14142 return this;
14143 },
14144 resize: function resize() {
14145 this.invalidateSize();
14146 this.emitAndNotify('resize');
14147 return this;
14148 },
14149 initRenderer: function initRenderer(options) {
14150 var cy = this;
14151 var RendererProto = cy.extension('renderer', options.name);
14152
14153 if (RendererProto == null) {
14154 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14155 return;
14156 }
14157
14158 if (options.wheelSensitivity !== undefined) {
14159 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.");
14160 }
14161
14162 var rOpts = rendererDefaults(options);
14163 rOpts.cy = cy;
14164 cy._private.renderer = new RendererProto(rOpts);
14165 this.notify('init');
14166 },
14167 destroyRenderer: function destroyRenderer() {
14168 var cy = this;
14169 cy.notify('destroy'); // destroy the renderer
14170
14171 var domEle = cy.container();
14172
14173 if (domEle) {
14174 domEle._cyreg = null;
14175
14176 while (domEle.childNodes.length > 0) {
14177 domEle.removeChild(domEle.childNodes[0]);
14178 }
14179 }
14180
14181 cy._private.renderer = null; // to be extra safe, remove the ref
14182
14183 cy.mutableElements().forEach(function (ele) {
14184 var _p = ele._private;
14185 _p.rscratch = {};
14186 _p.rstyle = {};
14187 _p.animation.current = [];
14188 _p.animation.queue = [];
14189 });
14190 },
14191 onRender: function onRender(fn) {
14192 return this.on('render', fn);
14193 },
14194 offRender: function offRender(fn) {
14195 return this.off('render', fn);
14196 }
14197};
14198corefn$5.invalidateDimensions = corefn$5.resize;
14199
14200var corefn$6 = {
14201 // get a collection
14202 // - empty collection on no args
14203 // - collection of elements in the graph on selector arg
14204 // - guarantee a returned collection when elements or collection specified
14205 collection: function collection(eles, opts) {
14206 if (string(eles)) {
14207 return this.$(eles);
14208 } else if (elementOrCollection(eles)) {
14209 return eles.collection();
14210 } else if (array(eles)) {
14211 return new Collection(this, eles, opts);
14212 }
14213
14214 return new Collection(this);
14215 },
14216 nodes: function nodes(selector) {
14217 var nodes = this.$(function (ele) {
14218 return ele.isNode();
14219 });
14220
14221 if (selector) {
14222 return nodes.filter(selector);
14223 }
14224
14225 return nodes;
14226 },
14227 edges: function edges(selector) {
14228 var edges = this.$(function (ele) {
14229 return ele.isEdge();
14230 });
14231
14232 if (selector) {
14233 return edges.filter(selector);
14234 }
14235
14236 return edges;
14237 },
14238 // search the graph like jQuery
14239 $: function $(selector) {
14240 var eles = this._private.elements;
14241
14242 if (selector) {
14243 return eles.filter(selector);
14244 } else {
14245 return eles.spawnSelf();
14246 }
14247 },
14248 mutableElements: function mutableElements() {
14249 return this._private.elements;
14250 }
14251}; // aliases
14252
14253corefn$6.elements = corefn$6.filter = corefn$6.$;
14254
14255var styfn = {}; // keys for style blocks, e.g. ttfftt
14256
14257var TRUE = 't';
14258var FALSE = 'f'; // (potentially expensive calculation)
14259// apply the style to the element based on
14260// - its bypass
14261// - what selectors match it
14262
14263styfn.apply = function (eles) {
14264 var self = this;
14265 var _p = self._private;
14266 var cy = _p.cy;
14267 var updatedEles = cy.collection();
14268
14269 if (_p.newStyle) {
14270 // clear style caches
14271 _p.contextStyles = {};
14272 _p.propDiffs = {};
14273 self.cleanElements(eles, true);
14274 }
14275
14276 for (var ie = 0; ie < eles.length; ie++) {
14277 var ele = eles[ie];
14278 var cxtMeta = self.getContextMeta(ele);
14279
14280 if (cxtMeta.empty) {
14281 continue;
14282 }
14283
14284 var cxtStyle = self.getContextStyle(cxtMeta);
14285 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14286
14287 if (!_p.newStyle) {
14288 self.updateTransitions(ele, app.diffProps);
14289 }
14290
14291 var hintsDiff = self.updateStyleHints(ele);
14292
14293 if (hintsDiff) {
14294 updatedEles.merge(ele);
14295 }
14296 } // for elements
14297
14298
14299 _p.newStyle = false;
14300 return updatedEles;
14301};
14302
14303styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14304 var self = this;
14305 var cache = self._private.propDiffs = self._private.propDiffs || {};
14306 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14307 var cachedVal = cache[dualCxtKey];
14308
14309 if (cachedVal) {
14310 return cachedVal;
14311 }
14312
14313 var diffProps = [];
14314 var addedProp = {};
14315
14316 for (var i = 0; i < self.length; i++) {
14317 var cxt = self[i];
14318 var oldHasCxt = oldCxtKey[i] === TRUE;
14319 var newHasCxt = newCxtKey[i] === TRUE;
14320 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14321 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14322
14323 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14324 var props = void 0;
14325
14326 if (cxtHasDiffed && cxtHasMappedProps) {
14327 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14328 } else if (cxtHasDiffed) {
14329 props = cxt.properties; // need to check them all
14330 } else if (cxtHasMappedProps) {
14331 props = cxt.mappedProperties; // only need to check mapped
14332 }
14333
14334 for (var j = 0; j < props.length; j++) {
14335 var prop = props[j];
14336 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14337 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14338 // is cached)
14339
14340 var laterCxtOverrides = false;
14341
14342 for (var k = i + 1; k < self.length; k++) {
14343 var laterCxt = self[k];
14344 var hasLaterCxt = newCxtKey[k] === TRUE;
14345
14346 if (!hasLaterCxt) {
14347 continue;
14348 } // can't override unless the context is active
14349
14350
14351 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14352
14353 if (laterCxtOverrides) {
14354 break;
14355 } // exit early as long as one later context overrides
14356
14357 }
14358
14359 if (!addedProp[name] && !laterCxtOverrides) {
14360 addedProp[name] = true;
14361 diffProps.push(name);
14362 }
14363 } // for props
14364
14365 } // if
14366
14367 } // for contexts
14368
14369
14370 cache[dualCxtKey] = diffProps;
14371 return diffProps;
14372};
14373
14374styfn.getContextMeta = function (ele) {
14375 var self = this;
14376 var cxtKey = '';
14377 var diffProps;
14378 var prevKey = ele._private.styleCxtKey || '';
14379
14380 if (self._private.newStyle) {
14381 prevKey = ''; // since we need to apply all style if a fresh stylesheet
14382 } // get the cxt key
14383
14384
14385 for (var i = 0; i < self.length; i++) {
14386 var context = self[i];
14387 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14388
14389 if (contextSelectorMatches) {
14390 cxtKey += TRUE;
14391 } else {
14392 cxtKey += FALSE;
14393 }
14394 } // for context
14395
14396
14397 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14398 ele._private.styleCxtKey = cxtKey;
14399 return {
14400 key: cxtKey,
14401 diffPropNames: diffProps,
14402 empty: diffProps.length === 0
14403 };
14404}; // gets a computed ele style object based on matched contexts
14405
14406
14407styfn.getContextStyle = function (cxtMeta) {
14408 var cxtKey = cxtMeta.key;
14409 var self = this;
14410 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14411
14412 if (cxtStyles[cxtKey]) {
14413 return cxtStyles[cxtKey];
14414 }
14415
14416 var style = {
14417 _private: {
14418 key: cxtKey
14419 }
14420 };
14421
14422 for (var i = 0; i < self.length; i++) {
14423 var cxt = self[i];
14424 var hasCxt = cxtKey[i] === TRUE;
14425
14426 if (!hasCxt) {
14427 continue;
14428 }
14429
14430 for (var j = 0; j < cxt.properties.length; j++) {
14431 var prop = cxt.properties[j];
14432 style[prop.name] = prop;
14433 }
14434 }
14435
14436 cxtStyles[cxtKey] = style;
14437 return style;
14438};
14439
14440styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14441 var self = this;
14442 var diffProps = cxtMeta.diffPropNames;
14443 var retDiffProps = {};
14444 var types = self.types;
14445
14446 for (var i = 0; i < diffProps.length; i++) {
14447 var diffPropName = diffProps[i];
14448 var cxtProp = cxtStyle[diffPropName];
14449 var eleProp = ele.pstyle(diffPropName);
14450
14451 if (!cxtProp) {
14452 // no context prop means delete
14453 if (!eleProp) {
14454 continue; // no existing prop means nothing needs to be removed
14455 // nb affects initial application on mapped values like control-point-distances
14456 } else if (eleProp.bypass) {
14457 cxtProp = {
14458 name: diffPropName,
14459 deleteBypassed: true
14460 };
14461 } else {
14462 cxtProp = {
14463 name: diffPropName,
14464 "delete": true
14465 };
14466 }
14467 } // save cycles when the context prop doesn't need to be applied
14468
14469
14470 if (eleProp === cxtProp) {
14471 continue;
14472 } // save cycles when a mapped context prop doesn't need to be applied
14473
14474
14475 if (cxtProp.mapped === types.fn // context prop is function mapper
14476 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14477 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14478 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14479 ) {
14480 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14481 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14482
14483 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14484
14485 if (fnValue === mapping.prevFnValue) {
14486 continue;
14487 }
14488 }
14489
14490 var retDiffProp = retDiffProps[diffPropName] = {
14491 prev: eleProp
14492 };
14493 self.applyParsedProperty(ele, cxtProp);
14494 retDiffProp.next = ele.pstyle(diffPropName);
14495
14496 if (retDiffProp.next && retDiffProp.next.bypass) {
14497 retDiffProp.next = retDiffProp.next.bypassed;
14498 }
14499 }
14500
14501 return {
14502 diffProps: retDiffProps
14503 };
14504};
14505
14506styfn.updateStyleHints = function (ele) {
14507 var _p = ele._private;
14508 var self = this;
14509 var propNames = self.propertyGroupNames;
14510 var propGrKeys = self.propertyGroupKeys;
14511
14512 var propHash = function propHash(ele, propNames, seedKey) {
14513 return self.getPropertiesHash(ele, propNames, seedKey);
14514 };
14515
14516 var oldStyleKey = _p.styleKey;
14517
14518 if (ele.removed()) {
14519 return false;
14520 }
14521
14522 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14523 // but lazily -- only use non-default prop values to reduce the number of hashes
14524 //
14525
14526 var overriddenStyles = ele._private.style;
14527 propNames = Object.keys(overriddenStyles);
14528
14529 for (var i = 0; i < propGrKeys.length; i++) {
14530 var grKey = propGrKeys[i];
14531 _p.styleKeys[grKey] = 0;
14532 }
14533
14534 var updateGrKey = function updateGrKey(val, grKey) {
14535 return _p.styleKeys[grKey] = hashInt(val, _p.styleKeys[grKey]);
14536 };
14537
14538 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14539 for (var j = 0; j < strVal.length; j++) {
14540 updateGrKey(strVal.charCodeAt(j), grKey);
14541 }
14542 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14543 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14544 // - raise up small numbers so more significant digits are seen by hashing
14545 // - make small numbers larger than a normal value to avoid collisions
14546 // - works in practice and it's relatively cheap
14547
14548
14549 var N = 2000000000;
14550
14551 var cleanNum = function cleanNum(val) {
14552 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14553 };
14554
14555 for (var _i = 0; _i < propNames.length; _i++) {
14556 var name = propNames[_i];
14557 var parsedProp = overriddenStyles[name];
14558
14559 if (parsedProp == null) {
14560 continue;
14561 }
14562
14563 var propInfo = this.properties[name];
14564 var type = propInfo.type;
14565 var _grKey = propInfo.groupKey;
14566 var normalizedNumberVal = void 0;
14567
14568 if (propInfo.hashOverride != null) {
14569 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14570 } else if (parsedProp.pfValue != null) {
14571 normalizedNumberVal = parsedProp.pfValue;
14572 } // might not be a number if it allows enums
14573
14574
14575 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14576 var haveNormNum = normalizedNumberVal != null;
14577 var haveUnitedNum = numberVal != null;
14578 var haveNum = haveNormNum || haveUnitedNum;
14579 var units = parsedProp.units; // numbers are cheaper to hash than strings
14580 // 1 hash op vs n hash ops (for length n string)
14581
14582 if (type.number && haveNum) {
14583 var v = haveNormNum ? normalizedNumberVal : numberVal;
14584
14585 if (type.multiple) {
14586 for (var _i2 = 0; _i2 < v.length; _i2++) {
14587 updateGrKey(cleanNum(v[_i2]), _grKey);
14588 }
14589 } else {
14590 updateGrKey(cleanNum(v), _grKey);
14591 }
14592
14593 if (!haveNormNum && units != null) {
14594 updateGrKeyWStr(units, _grKey);
14595 }
14596 } else {
14597 updateGrKeyWStr(parsedProp.strValue, _grKey);
14598 }
14599 } // overall style key
14600 //
14601
14602
14603 var hash = 0;
14604
14605 for (var _i3 = 0; _i3 < propGrKeys.length; _i3++) {
14606 var _grKey2 = propGrKeys[_i3];
14607 var grHash = _p.styleKeys[_grKey2];
14608 hash = hashInt(grHash, hash);
14609 }
14610
14611 _p.styleKey = hash; // label dims
14612 //
14613
14614 var labelDimsKey = _p.labelDimsKey = _p.styleKeys.labelDimensions;
14615 _p.labelKey = propHash(ele, ['label'], labelDimsKey);
14616 _p.labelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.labelKey);
14617
14618 if (!isNode) {
14619 _p.sourceLabelKey = propHash(ele, ['source-label'], labelDimsKey);
14620 _p.sourceLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.sourceLabelKey);
14621 _p.targetLabelKey = propHash(ele, ['target-label'], labelDimsKey);
14622 _p.targetLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.targetLabelKey);
14623 } // node
14624 //
14625
14626
14627 if (isNode) {
14628 var _p$styleKeys = _p.styleKeys,
14629 nodeBody = _p$styleKeys.nodeBody,
14630 nodeBorder = _p$styleKeys.nodeBorder,
14631 backgroundImage = _p$styleKeys.backgroundImage,
14632 compound = _p$styleKeys.compound,
14633 pie = _p$styleKeys.pie;
14634 _p.nodeKey = hashIntsArray([nodeBorder, backgroundImage, compound, pie], nodeBody);
14635 _p.hasPie = pie != 0;
14636 }
14637
14638 return oldStyleKey !== _p.styleKey;
14639};
14640
14641styfn.clearStyleHints = function (ele) {
14642 var _p = ele._private;
14643 _p.styleKeys = {};
14644 _p.styleKey = null;
14645 _p.labelKey = null;
14646 _p.labelStyleKey = null;
14647 _p.sourceLabelKey = null;
14648 _p.sourceLabelStyleKey = null;
14649 _p.targetLabelKey = null;
14650 _p.targetLabelStyleKey = null;
14651 _p.nodeKey = null;
14652 _p.hasPie = null;
14653}; // apply a property to the style (for internal use)
14654// returns whether application was successful
14655//
14656// now, this function flattens the property, and here's how:
14657//
14658// for parsedProp:{ bypass: true, deleteBypass: true }
14659// no property is generated, instead the bypass property in the
14660// element's style is replaced by what's pointed to by the `bypassed`
14661// field in the bypass property (i.e. restoring the property the
14662// bypass was overriding)
14663//
14664// for parsedProp:{ mapped: truthy }
14665// the generated flattenedProp:{ mapping: prop }
14666//
14667// for parsedProp:{ bypass: true }
14668// the generated flattenedProp:{ bypassed: parsedProp }
14669
14670
14671styfn.applyParsedProperty = function (ele, parsedProp) {
14672 var self = this;
14673 var prop = parsedProp;
14674 var style = ele._private.style;
14675 var flatProp;
14676 var types = self.types;
14677 var type = self.properties[prop.name].type;
14678 var propIsBypass = prop.bypass;
14679 var origProp = style[prop.name];
14680 var origPropIsBypass = origProp && origProp.bypass;
14681 var _p = ele._private;
14682 var flatPropMapping = 'mapping';
14683
14684 var getVal = function getVal(p) {
14685 if (p == null) {
14686 return null;
14687 } else if (p.pfValue != null) {
14688 return p.pfValue;
14689 } else {
14690 return p.value;
14691 }
14692 };
14693
14694 var checkTriggers = function checkTriggers() {
14695 var fromVal = getVal(origProp);
14696 var toVal = getVal(prop);
14697 self.checkTriggers(ele, prop.name, fromVal, toVal);
14698 }; // edge sanity checks to prevent the client from making serious mistakes
14699
14700
14701 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
14702 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
14703 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
14704 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
14705 }
14706
14707 if (prop["delete"]) {
14708 // delete the property and use the default value on falsey value
14709 style[prop.name] = undefined;
14710 checkTriggers();
14711 return true;
14712 }
14713
14714 if (prop.deleteBypassed) {
14715 // delete the property that the
14716 if (!origProp) {
14717 checkTriggers();
14718 return true; // can't delete if no prop
14719 } else if (origProp.bypass) {
14720 // delete bypassed
14721 origProp.bypassed = undefined;
14722 checkTriggers();
14723 return true;
14724 } else {
14725 return false; // we're unsuccessful deleting the bypassed
14726 }
14727 } // check if we need to delete the current bypass
14728
14729
14730 if (prop.deleteBypass) {
14731 // then this property is just here to indicate we need to delete
14732 if (!origProp) {
14733 checkTriggers();
14734 return true; // property is already not defined
14735 } else if (origProp.bypass) {
14736 // then replace the bypass property with the original
14737 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
14738 style[prop.name] = origProp.bypassed;
14739 checkTriggers();
14740 return true;
14741 } else {
14742 return false; // we're unsuccessful deleting the bypass
14743 }
14744 }
14745
14746 var printMappingErr = function printMappingErr() {
14747 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');
14748 }; // put the property in the style objects
14749
14750
14751 switch (prop.mapped) {
14752 // flatten the property if mapped
14753 case types.mapData:
14754 {
14755 // flatten the field (e.g. data.foo.bar)
14756 var fields = prop.field.split('.');
14757 var fieldVal = _p.data;
14758
14759 for (var i = 0; i < fields.length && fieldVal; i++) {
14760 var field = fields[i];
14761 fieldVal = fieldVal[field];
14762 }
14763
14764 if (fieldVal == null) {
14765 printMappingErr();
14766 return false;
14767 }
14768
14769 var percent;
14770
14771 if (!number(fieldVal)) {
14772 // then don't apply and fall back on the existing style
14773 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
14774 return false;
14775 } else {
14776 var fieldWidth = prop.fieldMax - prop.fieldMin;
14777
14778 if (fieldWidth === 0) {
14779 // safety check -- not strictly necessary as no props of zero range should be passed here
14780 percent = 0;
14781 } else {
14782 percent = (fieldVal - prop.fieldMin) / fieldWidth;
14783 }
14784 } // make sure to bound percent value
14785
14786
14787 if (percent < 0) {
14788 percent = 0;
14789 } else if (percent > 1) {
14790 percent = 1;
14791 }
14792
14793 if (type.color) {
14794 var r1 = prop.valueMin[0];
14795 var r2 = prop.valueMax[0];
14796 var g1 = prop.valueMin[1];
14797 var g2 = prop.valueMax[1];
14798 var b1 = prop.valueMin[2];
14799 var b2 = prop.valueMax[2];
14800 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
14801 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
14802 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)];
14803 flatProp = {
14804 // colours are simple, so just create the flat property instead of expensive string parsing
14805 bypass: prop.bypass,
14806 // we're a bypass if the mapping property is a bypass
14807 name: prop.name,
14808 value: clr,
14809 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
14810 };
14811 } else if (type.number) {
14812 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
14813 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
14814 } else {
14815 return false; // can only map to colours and numbers
14816 }
14817
14818 if (!flatProp) {
14819 // if we can't flatten the property, then don't apply the property and fall back on the existing style
14820 printMappingErr();
14821 return false;
14822 }
14823
14824 flatProp.mapping = prop; // keep a reference to the mapping
14825
14826 prop = flatProp; // the flattened (mapped) property is the one we want
14827
14828 break;
14829 }
14830 // direct mapping
14831
14832 case types.data:
14833 {
14834 // flatten the field (e.g. data.foo.bar)
14835 var _fields = prop.field.split('.');
14836
14837 var _fieldVal = _p.data;
14838
14839 for (var _i4 = 0; _i4 < _fields.length && _fieldVal; _i4++) {
14840 var _field = _fields[_i4];
14841 _fieldVal = _fieldVal[_field];
14842 }
14843
14844 if (_fieldVal != null) {
14845 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
14846 }
14847
14848 if (!flatProp) {
14849 // if we can't flatten the property, then don't apply and fall back on the existing style
14850 printMappingErr();
14851 return false;
14852 }
14853
14854 flatProp.mapping = prop; // keep a reference to the mapping
14855
14856 prop = flatProp; // the flattened (mapped) property is the one we want
14857
14858 break;
14859 }
14860
14861 case types.fn:
14862 {
14863 var fn = prop.value;
14864 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
14865
14866 prop.prevFnValue = fnRetVal;
14867
14868 if (fnRetVal == null) {
14869 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
14870 return false;
14871 }
14872
14873 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
14874
14875 if (!flatProp) {
14876 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
14877 return false;
14878 }
14879
14880 flatProp.mapping = copy(prop); // keep a reference to the mapping
14881
14882 prop = flatProp; // the flattened (mapped) property is the one we want
14883
14884 break;
14885 }
14886
14887 case undefined:
14888 break;
14889 // just set the property
14890
14891 default:
14892 return false;
14893 // not a valid mapping
14894 } // if the property is a bypass property, then link the resultant property to the original one
14895
14896
14897 if (propIsBypass) {
14898 if (origPropIsBypass) {
14899 // then this bypass overrides the existing one
14900 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
14901 } else {
14902 // then link the orig prop to the new bypass
14903 prop.bypassed = origProp;
14904 }
14905
14906 style[prop.name] = prop; // and set
14907 } else {
14908 // prop is not bypass
14909 if (origPropIsBypass) {
14910 // then keep the orig prop (since it's a bypass) and link to the new prop
14911 origProp.bypassed = prop;
14912 } else {
14913 // then just replace the old prop with the new one
14914 style[prop.name] = prop;
14915 }
14916 }
14917
14918 checkTriggers();
14919 return true;
14920};
14921
14922styfn.cleanElements = function (eles, keepBypasses) {
14923 for (var i = 0; i < eles.length; i++) {
14924 var ele = eles[i];
14925 this.clearStyleHints(ele);
14926 ele.dirtyCompoundBoundsCache();
14927 ele.dirtyBoundingBoxCache();
14928
14929 if (!keepBypasses) {
14930 ele._private.style = {};
14931 } else {
14932 var style = ele._private.style;
14933 var propNames = Object.keys(style);
14934
14935 for (var j = 0; j < propNames.length; j++) {
14936 var propName = propNames[j];
14937 var eleProp = style[propName];
14938
14939 if (eleProp != null) {
14940 if (eleProp.bypass) {
14941 eleProp.bypassed = null;
14942 } else {
14943 style[propName] = null;
14944 }
14945 }
14946 }
14947 }
14948 }
14949}; // updates the visual style for all elements (useful for manual style modification after init)
14950
14951
14952styfn.update = function () {
14953 var cy = this._private.cy;
14954 var eles = cy.mutableElements();
14955 eles.updateStyle();
14956}; // diffProps : { name => { prev, next } }
14957
14958
14959styfn.updateTransitions = function (ele, diffProps) {
14960 var self = this;
14961 var _p = ele._private;
14962 var props = ele.pstyle('transition-property').value;
14963 var duration = ele.pstyle('transition-duration').pfValue;
14964 var delay = ele.pstyle('transition-delay').pfValue;
14965
14966 if (props.length > 0 && duration > 0) {
14967 var style = {}; // build up the style to animate towards
14968
14969 var anyPrev = false;
14970
14971 for (var i = 0; i < props.length; i++) {
14972 var prop = props[i];
14973 var styProp = ele.pstyle(prop);
14974 var diffProp = diffProps[prop];
14975
14976 if (!diffProp) {
14977 continue;
14978 }
14979
14980 var prevProp = diffProp.prev;
14981 var fromProp = prevProp;
14982 var toProp = diffProp.next != null ? diffProp.next : styProp;
14983 var diff = false;
14984 var initVal = void 0;
14985 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
14986
14987 if (!fromProp) {
14988 continue;
14989 } // consider px values
14990
14991
14992 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
14993 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
14994
14995 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
14996 } else if (number(fromProp.value) && number(toProp.value)) {
14997 diff = toProp.value - fromProp.value; // nonzero is truthy
14998
14999 initVal = fromProp.value + initDt * diff; // consider colour values
15000 } else if (array(fromProp.value) && array(toProp.value)) {
15001 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15002 initVal = fromProp.strValue;
15003 } // the previous value is good for an animation only if it's different
15004
15005
15006 if (diff) {
15007 style[prop] = toProp.strValue; // to val
15008
15009 this.applyBypass(ele, prop, initVal); // from val
15010
15011 anyPrev = true;
15012 }
15013 } // end if props allow ani
15014 // can't transition if there's nothing previous to transition from
15015
15016
15017 if (!anyPrev) {
15018 return;
15019 }
15020
15021 _p.transitioning = true;
15022 new Promise$1(function (resolve) {
15023 if (delay > 0) {
15024 ele.delayAnimation(delay).play().promise().then(resolve);
15025 } else {
15026 resolve();
15027 }
15028 }).then(function () {
15029 return ele.animation({
15030 style: style,
15031 duration: duration,
15032 easing: ele.pstyle('transition-timing-function').value,
15033 queue: false
15034 }).play().promise();
15035 }).then(function () {
15036 // if( !isBypass ){
15037 self.removeBypasses(ele, props);
15038 ele.emitAndNotify('style'); // }
15039
15040 _p.transitioning = false;
15041 });
15042 } else if (_p.transitioning) {
15043 this.removeBypasses(ele, props);
15044 ele.emitAndNotify('style');
15045 _p.transitioning = false;
15046 }
15047};
15048
15049styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15050 var prop = this.properties[name];
15051 var triggerCheck = getTrigger(prop);
15052
15053 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15054 onTrigger(prop);
15055 }
15056};
15057
15058styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15059 var _this = this;
15060
15061 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15062 return prop.triggersZOrder;
15063 }, function () {
15064 _this._private.cy.notify('zorder', ele);
15065 });
15066};
15067
15068styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15069 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15070 return prop.triggersBounds;
15071 }, function (prop) {
15072 ele.dirtyCompoundBoundsCache();
15073 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15074 // then dirty the pll edge bb cache as well
15075
15076 if ( // only for beziers -- so performance of other edges isn't affected
15077 (ele.pstyle('curve-style').value === 'bezier' // already a bezier
15078 // was just now changed to or from a bezier:
15079 || name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) && prop.triggersBoundsOfParallelBeziers) {
15080 ele.parallelEdges().forEach(function (pllEdge) {
15081 if (pllEdge.isBundledBezier()) {
15082 pllEdge.dirtyBoundingBoxCache();
15083 }
15084 });
15085 }
15086 });
15087};
15088
15089styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15090 ele.dirtyStyleCache();
15091 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15092 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15093};
15094
15095var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15096// returns true iff application was successful for at least 1 specified property
15097
15098styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15099 var self = this;
15100 var props = [];
15101 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15102
15103 if (name === '*' || name === '**') {
15104 // apply to all property names
15105 if (value !== undefined) {
15106 for (var i = 0; i < self.properties.length; i++) {
15107 var prop = self.properties[i];
15108 var _name = prop.name;
15109 var parsedProp = this.parse(_name, value, true);
15110
15111 if (parsedProp) {
15112 props.push(parsedProp);
15113 }
15114 }
15115 }
15116 } else if (string(name)) {
15117 // then parse the single property
15118 var _parsedProp = this.parse(name, value, true);
15119
15120 if (_parsedProp) {
15121 props.push(_parsedProp);
15122 }
15123 } else if (plainObject(name)) {
15124 // then parse each property
15125 var specifiedProps = name;
15126 updateTransitions = value;
15127 var names = Object.keys(specifiedProps);
15128
15129 for (var _i = 0; _i < names.length; _i++) {
15130 var _name2 = names[_i];
15131 var _value = specifiedProps[_name2];
15132
15133 if (_value === undefined) {
15134 // try camel case name too
15135 _value = specifiedProps[dash2camel(_name2)];
15136 }
15137
15138 if (_value !== undefined) {
15139 var _parsedProp2 = this.parse(_name2, _value, true);
15140
15141 if (_parsedProp2) {
15142 props.push(_parsedProp2);
15143 }
15144 }
15145 }
15146 } else {
15147 // can't do anything without well defined properties
15148 return false;
15149 } // we've failed if there are no valid properties
15150
15151
15152 if (props.length === 0) {
15153 return false;
15154 } // now, apply the bypass properties on the elements
15155
15156
15157 var ret = false; // return true if at least one succesful bypass applied
15158
15159 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15160 // for each ele
15161 var ele = eles[_i2];
15162 var diffProps = {};
15163 var diffProp = void 0;
15164
15165 for (var j = 0; j < props.length; j++) {
15166 // for each prop
15167 var _prop = props[j];
15168
15169 if (updateTransitions) {
15170 var prevProp = ele.pstyle(_prop.name);
15171 diffProp = diffProps[_prop.name] = {
15172 prev: prevProp
15173 };
15174 }
15175
15176 ret = this.applyParsedProperty(ele, _prop) || ret;
15177
15178 if (updateTransitions) {
15179 diffProp.next = ele.pstyle(_prop.name);
15180 }
15181 } // for props
15182
15183
15184 if (ret) {
15185 this.updateStyleHints(ele);
15186 }
15187
15188 if (updateTransitions) {
15189 this.updateTransitions(ele, diffProps, isBypass);
15190 }
15191 } // for eles
15192
15193
15194 return ret;
15195}; // only useful in specific cases like animation
15196
15197
15198styfn$1.overrideBypass = function (eles, name, value) {
15199 name = camel2dash(name);
15200
15201 for (var i = 0; i < eles.length; i++) {
15202 var ele = eles[i];
15203 var prop = ele._private.style[name];
15204 var type = this.properties[name].type;
15205 var isColor = type.color;
15206 var isMulti = type.mutiple;
15207 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15208
15209 if (!prop || !prop.bypass) {
15210 // need a bypass if one doesn't exist
15211 this.applyBypass(ele, name, value);
15212 } else {
15213 prop.value = value;
15214
15215 if (prop.pfValue != null) {
15216 prop.pfValue = value;
15217 }
15218
15219 if (isColor) {
15220 prop.strValue = 'rgb(' + value.join(',') + ')';
15221 } else if (isMulti) {
15222 prop.strValue = value.join(' ');
15223 } else {
15224 prop.strValue = '' + value;
15225 }
15226
15227 this.updateStyleHints(ele);
15228 }
15229
15230 this.checkTriggers(ele, name, oldValue, value);
15231 }
15232};
15233
15234styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15235 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15236};
15237
15238styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15239 var isBypass = true;
15240
15241 for (var j = 0; j < eles.length; j++) {
15242 var ele = eles[j];
15243 var diffProps = {};
15244
15245 for (var i = 0; i < props.length; i++) {
15246 var name = props[i];
15247 var prop = this.properties[name];
15248 var prevProp = ele.pstyle(prop.name);
15249
15250 if (!prevProp || !prevProp.bypass) {
15251 // if a bypass doesn't exist for the prop, nothing needs to be removed
15252 continue;
15253 }
15254
15255 var value = ''; // empty => remove bypass
15256
15257 var parsedProp = this.parse(name, value, true);
15258 var diffProp = diffProps[prop.name] = {
15259 prev: prevProp
15260 };
15261 this.applyParsedProperty(ele, parsedProp);
15262 diffProp.next = ele.pstyle(prop.name);
15263 } // for props
15264
15265
15266 this.updateStyleHints(ele);
15267
15268 if (updateTransitions) {
15269 this.updateTransitions(ele, diffProps, isBypass);
15270 }
15271 } // for eles
15272
15273};
15274
15275var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15276
15277styfn$2.getEmSizeInPixels = function () {
15278 var px = this.containerCss('font-size');
15279
15280 if (px != null) {
15281 return parseFloat(px);
15282 } else {
15283 return 1; // for headless
15284 }
15285}; // gets css property from the core container
15286
15287
15288styfn$2.containerCss = function (propName) {
15289 var cy = this._private.cy;
15290 var domElement = cy.container();
15291
15292 if (window$1 && domElement && window$1.getComputedStyle) {
15293 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15294 }
15295};
15296
15297var styfn$3 = {}; // gets the rendered style for an element
15298
15299styfn$3.getRenderedStyle = function (ele, prop) {
15300 if (prop) {
15301 return this.getStylePropertyValue(ele, prop, true);
15302 } else {
15303 return this.getRawStyle(ele, true);
15304 }
15305}; // gets the raw style for an element
15306
15307
15308styfn$3.getRawStyle = function (ele, isRenderedVal) {
15309 var self = this;
15310 ele = ele[0]; // insure it's an element
15311
15312 if (ele) {
15313 var rstyle = {};
15314
15315 for (var i = 0; i < self.properties.length; i++) {
15316 var prop = self.properties[i];
15317 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15318
15319 if (val != null) {
15320 rstyle[prop.name] = val;
15321 rstyle[dash2camel(prop.name)] = val;
15322 }
15323 }
15324
15325 return rstyle;
15326 }
15327};
15328
15329styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15330 var pstyle = ele.pstyle(property)[subproperty][index];
15331 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15332};
15333
15334styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15335 var self = this;
15336 ele = ele[0]; // insure it's an element
15337
15338 if (ele) {
15339 var prop = self.properties[propName];
15340
15341 if (prop.alias) {
15342 prop = prop.pointsTo;
15343 }
15344
15345 var type = prop.type;
15346 var styleProp = ele.pstyle(prop.name);
15347
15348 if (styleProp) {
15349 var value = styleProp.value,
15350 units = styleProp.units,
15351 strValue = styleProp.strValue;
15352
15353 if (isRenderedVal && type.number && value != null && number(value)) {
15354 var zoom = ele.cy().zoom();
15355
15356 var getRenderedValue = function getRenderedValue(val) {
15357 return val * zoom;
15358 };
15359
15360 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15361 return getRenderedValue(val) + units;
15362 };
15363
15364 var isArrayValue = array(value);
15365 var haveUnits = isArrayValue ? units.every(function (u) {
15366 return u != null;
15367 }) : units != null;
15368
15369 if (haveUnits) {
15370 if (isArrayValue) {
15371 return value.map(function (v, i) {
15372 return getValueStringWithUnits(v, units[i]);
15373 }).join(' ');
15374 } else {
15375 return getValueStringWithUnits(value, units);
15376 }
15377 } else {
15378 if (isArrayValue) {
15379 return value.map(function (v) {
15380 return string(v) ? v : '' + getRenderedValue(v);
15381 }).join(' ');
15382 } else {
15383 return '' + getRenderedValue(value);
15384 }
15385 }
15386 } else if (strValue != null) {
15387 return strValue;
15388 }
15389 }
15390
15391 return null;
15392 }
15393};
15394
15395styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15396 var rstyle = {};
15397
15398 for (var i = 0; i < aniProps.length; i++) {
15399 var aniProp = aniProps[i];
15400 var name = aniProp.name;
15401 var styleProp = ele.pstyle(name);
15402
15403 if (styleProp !== undefined) {
15404 // then make a prop of it
15405 if (plainObject(styleProp)) {
15406 styleProp = this.parse(name, styleProp.strValue);
15407 } else {
15408 styleProp = this.parse(name, styleProp);
15409 }
15410 }
15411
15412 if (styleProp) {
15413 rstyle[name] = styleProp;
15414 }
15415 }
15416
15417 return rstyle;
15418};
15419
15420styfn$3.getPropsList = function (propsObj) {
15421 var self = this;
15422 var rstyle = [];
15423 var style = propsObj;
15424 var props = self.properties;
15425
15426 if (style) {
15427 var names = Object.keys(style);
15428
15429 for (var i = 0; i < names.length; i++) {
15430 var name = names[i];
15431 var val = style[name];
15432 var prop = props[name] || props[camel2dash(name)];
15433 var styleProp = this.parse(prop.name, val);
15434
15435 if (styleProp) {
15436 rstyle.push(styleProp);
15437 }
15438 }
15439 }
15440
15441 return rstyle;
15442};
15443
15444styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15445 var hash = seed;
15446 var name, val, strVal, chVal;
15447 var i, j;
15448
15449 for (i = 0; i < propNames.length; i++) {
15450 name = propNames[i];
15451 val = ele.pstyle(name, false);
15452
15453 if (val == null) {
15454 continue;
15455 } else if (val.pfValue != null) {
15456 hash = hashInt(chVal, hash);
15457 } else {
15458 strVal = val.strValue;
15459
15460 for (j = 0; j < strVal.length; j++) {
15461 chVal = strVal.charCodeAt(j);
15462 hash = hashInt(chVal, hash);
15463 }
15464 }
15465 }
15466
15467 return hash;
15468};
15469
15470styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15471
15472var styfn$4 = {};
15473
15474styfn$4.appendFromJson = function (json) {
15475 var style = this;
15476
15477 for (var i = 0; i < json.length; i++) {
15478 var context = json[i];
15479 var selector = context.selector;
15480 var props = context.style || context.css;
15481 var names = Object.keys(props);
15482 style.selector(selector); // apply selector
15483
15484 for (var j = 0; j < names.length; j++) {
15485 var name = names[j];
15486 var value = props[name];
15487 style.css(name, value); // apply property
15488 }
15489 }
15490
15491 return style;
15492}; // accessible cy.style() function
15493
15494
15495styfn$4.fromJson = function (json) {
15496 var style = this;
15497 style.resetToDefault();
15498 style.appendFromJson(json);
15499 return style;
15500}; // get json from cy.style() api
15501
15502
15503styfn$4.json = function () {
15504 var json = [];
15505
15506 for (var i = this.defaultLength; i < this.length; i++) {
15507 var cxt = this[i];
15508 var selector = cxt.selector;
15509 var props = cxt.properties;
15510 var css = {};
15511
15512 for (var j = 0; j < props.length; j++) {
15513 var prop = props[j];
15514 css[prop.name] = prop.strValue;
15515 }
15516
15517 json.push({
15518 selector: !selector ? 'core' : selector.toString(),
15519 style: css
15520 });
15521 }
15522
15523 return json;
15524};
15525
15526var styfn$5 = {};
15527
15528styfn$5.appendFromString = function (string) {
15529 var self = this;
15530 var style = this;
15531 var remaining = '' + string;
15532 var selAndBlockStr;
15533 var blockRem;
15534 var propAndValStr; // remove comments from the style string
15535
15536 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15537
15538 function removeSelAndBlockFromRemaining() {
15539 // remove the parsed selector and block from the remaining text to parse
15540 if (remaining.length > selAndBlockStr.length) {
15541 remaining = remaining.substr(selAndBlockStr.length);
15542 } else {
15543 remaining = '';
15544 }
15545 }
15546
15547 function removePropAndValFromRem() {
15548 // remove the parsed property and value from the remaining block text to parse
15549 if (blockRem.length > propAndValStr.length) {
15550 blockRem = blockRem.substr(propAndValStr.length);
15551 } else {
15552 blockRem = '';
15553 }
15554 }
15555
15556 for (;;) {
15557 var nothingLeftToParse = remaining.match(/^\s*$/);
15558
15559 if (nothingLeftToParse) {
15560 break;
15561 }
15562
15563 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15564
15565 if (!selAndBlock) {
15566 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15567 break;
15568 }
15569
15570 selAndBlockStr = selAndBlock[0]; // parse the selector
15571
15572 var selectorStr = selAndBlock[1];
15573
15574 if (selectorStr !== 'core') {
15575 var selector = new Selector(selectorStr);
15576
15577 if (selector.invalid) {
15578 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15579
15580 removeSelAndBlockFromRemaining();
15581 continue;
15582 }
15583 } // parse the block of properties and values
15584
15585
15586 var blockStr = selAndBlock[2];
15587 var invalidBlock = false;
15588 blockRem = blockStr;
15589 var props = [];
15590
15591 for (;;) {
15592 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15593
15594 if (_nothingLeftToParse) {
15595 break;
15596 }
15597
15598 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15599
15600 if (!propAndVal) {
15601 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15602 invalidBlock = true;
15603 break;
15604 }
15605
15606 propAndValStr = propAndVal[0];
15607 var propStr = propAndVal[1];
15608 var valStr = propAndVal[2];
15609 var prop = self.properties[propStr];
15610
15611 if (!prop) {
15612 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15613
15614 removePropAndValFromRem();
15615 continue;
15616 }
15617
15618 var parsedProp = style.parse(propStr, valStr);
15619
15620 if (!parsedProp) {
15621 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15622
15623 removePropAndValFromRem();
15624 continue;
15625 }
15626
15627 props.push({
15628 name: propStr,
15629 val: valStr
15630 });
15631 removePropAndValFromRem();
15632 }
15633
15634 if (invalidBlock) {
15635 removeSelAndBlockFromRemaining();
15636 break;
15637 } // put the parsed block in the style
15638
15639
15640 style.selector(selectorStr);
15641
15642 for (var i = 0; i < props.length; i++) {
15643 var _prop = props[i];
15644 style.css(_prop.name, _prop.val);
15645 }
15646
15647 removeSelAndBlockFromRemaining();
15648 }
15649
15650 return style;
15651};
15652
15653styfn$5.fromString = function (string) {
15654 var style = this;
15655 style.resetToDefault();
15656 style.appendFromString(string);
15657 return style;
15658};
15659
15660var styfn$6 = {};
15661
15662(function () {
15663 var number = number$1;
15664 var rgba = rgbaNoBackRefs;
15665 var hsla = hslaNoBackRefs;
15666 var hex3$1 = hex3;
15667 var hex6$1 = hex6;
15668
15669 var data = function data(prefix) {
15670 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15671 };
15672
15673 var mapData = function mapData(prefix) {
15674 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15675 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15676 };
15677
15678 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15679
15680 styfn$6.types = {
15681 time: {
15682 number: true,
15683 min: 0,
15684 units: 's|ms',
15685 implicitUnits: 'ms'
15686 },
15687 percent: {
15688 number: true,
15689 min: 0,
15690 max: 100,
15691 units: '%',
15692 implicitUnits: '%'
15693 },
15694 percentages: {
15695 number: true,
15696 min: 0,
15697 max: 100,
15698 units: '%',
15699 implicitUnits: '%',
15700 multiple: true
15701 },
15702 zeroOneNumber: {
15703 number: true,
15704 min: 0,
15705 max: 1,
15706 unitless: true
15707 },
15708 zeroOneNumbers: {
15709 number: true,
15710 min: 0,
15711 max: 1,
15712 unitless: true,
15713 multiple: true
15714 },
15715 nOneOneNumber: {
15716 number: true,
15717 min: -1,
15718 max: 1,
15719 unitless: true
15720 },
15721 nonNegativeInt: {
15722 number: true,
15723 min: 0,
15724 integer: true,
15725 unitless: true
15726 },
15727 position: {
15728 enums: ['parent', 'origin']
15729 },
15730 nodeSize: {
15731 number: true,
15732 min: 0,
15733 enums: ['label']
15734 },
15735 number: {
15736 number: true,
15737 unitless: true
15738 },
15739 numbers: {
15740 number: true,
15741 unitless: true,
15742 multiple: true
15743 },
15744 positiveNumber: {
15745 number: true,
15746 unitless: true,
15747 min: 0,
15748 strictMin: true
15749 },
15750 size: {
15751 number: true,
15752 min: 0
15753 },
15754 bidirectionalSize: {
15755 number: true
15756 },
15757 // allows negative
15758 bidirectionalSizes: {
15759 number: true,
15760 multiple: true
15761 },
15762 // allows negative
15763 sizeMaybePercent: {
15764 number: true,
15765 min: 0,
15766 allowPercent: true
15767 },
15768 axisDirection: {
15769 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
15770 },
15771 paddingRelativeTo: {
15772 enums: ['width', 'height', 'average', 'min', 'max']
15773 },
15774 bgWH: {
15775 number: true,
15776 min: 0,
15777 allowPercent: true,
15778 enums: ['auto'],
15779 multiple: true
15780 },
15781 bgPos: {
15782 number: true,
15783 allowPercent: true,
15784 multiple: true
15785 },
15786 bgRelativeTo: {
15787 enums: ['inner', 'include-padding'],
15788 multiple: true
15789 },
15790 bgRepeat: {
15791 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
15792 multiple: true
15793 },
15794 bgFit: {
15795 enums: ['none', 'contain', 'cover'],
15796 multiple: true
15797 },
15798 bgCrossOrigin: {
15799 enums: ['anonymous', 'use-credentials'],
15800 multiple: true
15801 },
15802 bgClip: {
15803 enums: ['none', 'node'],
15804 multiple: true
15805 },
15806 color: {
15807 color: true
15808 },
15809 colors: {
15810 color: true,
15811 multiple: true
15812 },
15813 fill: {
15814 enums: ['solid', 'linear-gradient', 'radial-gradient']
15815 },
15816 bool: {
15817 enums: ['yes', 'no']
15818 },
15819 lineStyle: {
15820 enums: ['solid', 'dotted', 'dashed']
15821 },
15822 lineCap: {
15823 enums: ['butt', 'round', 'square']
15824 },
15825 borderStyle: {
15826 enums: ['solid', 'dotted', 'dashed', 'double']
15827 },
15828 curveStyle: {
15829 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
15830 },
15831 fontFamily: {
15832 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
15833 },
15834 fontStyle: {
15835 enums: ['italic', 'normal', 'oblique']
15836 },
15837 fontWeight: {
15838 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
15839 },
15840 textDecoration: {
15841 enums: ['none', 'underline', 'overline', 'line-through']
15842 },
15843 textTransform: {
15844 enums: ['none', 'uppercase', 'lowercase']
15845 },
15846 textWrap: {
15847 enums: ['none', 'wrap', 'ellipsis']
15848 },
15849 textOverflowWrap: {
15850 enums: ['whitespace', 'anywhere']
15851 },
15852 textBackgroundShape: {
15853 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
15854 },
15855 nodeShape: {
15856 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']
15857 },
15858 compoundIncludeLabels: {
15859 enums: ['include', 'exclude']
15860 },
15861 arrowShape: {
15862 enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
15863 },
15864 arrowFill: {
15865 enums: ['filled', 'hollow']
15866 },
15867 display: {
15868 enums: ['element', 'none']
15869 },
15870 visibility: {
15871 enums: ['hidden', 'visible']
15872 },
15873 zCompoundDepth: {
15874 enums: ['bottom', 'orphan', 'auto', 'top']
15875 },
15876 zIndexCompare: {
15877 enums: ['auto', 'manual']
15878 },
15879 valign: {
15880 enums: ['top', 'center', 'bottom']
15881 },
15882 halign: {
15883 enums: ['left', 'center', 'right']
15884 },
15885 justification: {
15886 enums: ['left', 'center', 'right', 'auto']
15887 },
15888 text: {
15889 string: true
15890 },
15891 data: {
15892 mapping: true,
15893 regex: data('data')
15894 },
15895 layoutData: {
15896 mapping: true,
15897 regex: data('layoutData')
15898 },
15899 scratch: {
15900 mapping: true,
15901 regex: data('scratch')
15902 },
15903 mapData: {
15904 mapping: true,
15905 regex: mapData('mapData')
15906 },
15907 mapLayoutData: {
15908 mapping: true,
15909 regex: mapData('mapLayoutData')
15910 },
15911 mapScratch: {
15912 mapping: true,
15913 regex: mapData('mapScratch')
15914 },
15915 fn: {
15916 mapping: true,
15917 fn: true
15918 },
15919 url: {
15920 regexes: urlRegexes,
15921 singleRegexMatchValue: true
15922 },
15923 urls: {
15924 regexes: urlRegexes,
15925 singleRegexMatchValue: true,
15926 multiple: true
15927 },
15928 propList: {
15929 propList: true
15930 },
15931 angle: {
15932 number: true,
15933 units: 'deg|rad',
15934 implicitUnits: 'rad'
15935 },
15936 textRotation: {
15937 number: true,
15938 units: 'deg|rad',
15939 implicitUnits: 'rad',
15940 enums: ['none', 'autorotate']
15941 },
15942 polygonPointList: {
15943 number: true,
15944 multiple: true,
15945 evenMultiple: true,
15946 min: -1,
15947 max: 1,
15948 unitless: true
15949 },
15950 edgeDistances: {
15951 enums: ['intersection', 'node-position']
15952 },
15953 edgeEndpoint: {
15954 number: true,
15955 multiple: true,
15956 units: '%|px|em|deg|rad',
15957 implicitUnits: 'px',
15958 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
15959 singleEnum: true,
15960 validate: function validate(valArr, unitsArr) {
15961 switch (valArr.length) {
15962 case 2:
15963 // can be % or px only
15964 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
15965
15966 case 1:
15967 // can be enum, deg, or rad only
15968 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
15969
15970 default:
15971 return false;
15972 }
15973 }
15974 },
15975 easing: {
15976 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
15977 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']
15978 },
15979 gradientDirection: {
15980 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']
15981 },
15982 boundsExpansion: {
15983 number: true,
15984 multiple: true,
15985 min: 0,
15986 validate: function validate(valArr) {
15987 var length = valArr.length;
15988 return length === 1 || length === 2 || length === 4;
15989 }
15990 }
15991 };
15992 var diff = {
15993 zeroNonZero: function zeroNonZero(val1, val2) {
15994 if ((val1 == null || val2 == null) && val1 !== val2) {
15995 return true; // null cases could represent any value
15996 }
15997
15998 if (val1 == 0 && val2 != 0) {
15999 return true;
16000 } else if (val1 != 0 && val2 == 0) {
16001 return true;
16002 } else {
16003 return false;
16004 }
16005 },
16006 any: function any(val1, val2) {
16007 return val1 != val2;
16008 }
16009 }; // define visual style properties
16010 //
16011 // - n.b. adding a new group of props may require updates to updateStyleHints()
16012 // - adding new props to an existing group gets handled automatically
16013
16014 var t = styfn$6.types;
16015 var mainLabel = [{
16016 name: 'label',
16017 type: t.text,
16018 triggersBounds: diff.any
16019 }, {
16020 name: 'text-rotation',
16021 type: t.textRotation,
16022 triggersBounds: diff.any
16023 }, {
16024 name: 'text-margin-x',
16025 type: t.bidirectionalSize,
16026 triggersBounds: diff.any
16027 }, {
16028 name: 'text-margin-y',
16029 type: t.bidirectionalSize,
16030 triggersBounds: diff.any
16031 }];
16032 var sourceLabel = [{
16033 name: 'source-label',
16034 type: t.text,
16035 triggersBounds: diff.any
16036 }, {
16037 name: 'source-text-rotation',
16038 type: t.textRotation,
16039 triggersBounds: diff.any
16040 }, {
16041 name: 'source-text-margin-x',
16042 type: t.bidirectionalSize,
16043 triggersBounds: diff.any
16044 }, {
16045 name: 'source-text-margin-y',
16046 type: t.bidirectionalSize,
16047 triggersBounds: diff.any
16048 }, {
16049 name: 'source-text-offset',
16050 type: t.size,
16051 triggersBounds: diff.any
16052 }];
16053 var targetLabel = [{
16054 name: 'target-label',
16055 type: t.text,
16056 triggersBounds: diff.any
16057 }, {
16058 name: 'target-text-rotation',
16059 type: t.textRotation,
16060 triggersBounds: diff.any
16061 }, {
16062 name: 'target-text-margin-x',
16063 type: t.bidirectionalSize,
16064 triggersBounds: diff.any
16065 }, {
16066 name: 'target-text-margin-y',
16067 type: t.bidirectionalSize,
16068 triggersBounds: diff.any
16069 }, {
16070 name: 'target-text-offset',
16071 type: t.size,
16072 triggersBounds: diff.any
16073 }];
16074 var labelDimensions = [{
16075 name: 'font-family',
16076 type: t.fontFamily,
16077 triggersBounds: diff.any
16078 }, {
16079 name: 'font-style',
16080 type: t.fontStyle,
16081 triggersBounds: diff.any
16082 }, {
16083 name: 'font-weight',
16084 type: t.fontWeight,
16085 triggersBounds: diff.any
16086 }, {
16087 name: 'font-size',
16088 type: t.size,
16089 triggersBounds: diff.any
16090 }, {
16091 name: 'text-transform',
16092 type: t.textTransform,
16093 triggersBounds: diff.any
16094 }, {
16095 name: 'text-wrap',
16096 type: t.textWrap,
16097 triggersBounds: diff.any
16098 }, {
16099 name: 'text-overflow-wrap',
16100 type: t.textOverflowWrap,
16101 triggersBounds: diff.any
16102 }, {
16103 name: 'text-max-width',
16104 type: t.size,
16105 triggersBounds: diff.any
16106 }, {
16107 name: 'text-outline-width',
16108 type: t.size,
16109 triggersBounds: diff.any
16110 }, {
16111 name: 'line-height',
16112 type: t.positiveNumber,
16113 triggersBounds: diff.any
16114 }];
16115 var commonLabel = [{
16116 name: 'text-valign',
16117 type: t.valign,
16118 triggersBounds: diff.any
16119 }, {
16120 name: 'text-halign',
16121 type: t.halign,
16122 triggersBounds: diff.any
16123 }, {
16124 name: 'color',
16125 type: t.color
16126 }, {
16127 name: 'text-outline-color',
16128 type: t.color
16129 }, {
16130 name: 'text-outline-opacity',
16131 type: t.zeroOneNumber
16132 }, {
16133 name: 'text-background-color',
16134 type: t.color
16135 }, {
16136 name: 'text-background-opacity',
16137 type: t.zeroOneNumber
16138 }, {
16139 name: 'text-background-padding',
16140 type: t.size,
16141 triggersBounds: diff.any
16142 }, {
16143 name: 'text-border-opacity',
16144 type: t.zeroOneNumber
16145 }, {
16146 name: 'text-border-color',
16147 type: t.color
16148 }, {
16149 name: 'text-border-width',
16150 type: t.size,
16151 triggersBounds: diff.any
16152 }, {
16153 name: 'text-border-style',
16154 type: t.borderStyle,
16155 triggersBounds: diff.any
16156 }, {
16157 name: 'text-background-shape',
16158 type: t.textBackgroundShape,
16159 triggersBounds: diff.any
16160 }, {
16161 name: 'text-justification',
16162 type: t.justification
16163 }];
16164 var behavior = [{
16165 name: 'events',
16166 type: t.bool
16167 }, {
16168 name: 'text-events',
16169 type: t.bool
16170 }];
16171 var visibility = [{
16172 name: 'display',
16173 type: t.display,
16174 triggersZOrder: diff.any,
16175 triggersBounds: diff.any,
16176 triggersBoundsOfParallelBeziers: true
16177 }, {
16178 name: 'visibility',
16179 type: t.visibility,
16180 triggersZOrder: diff.any
16181 }, {
16182 name: 'opacity',
16183 type: t.zeroOneNumber,
16184 triggersZOrder: diff.zeroNonZero
16185 }, {
16186 name: 'text-opacity',
16187 type: t.zeroOneNumber
16188 }, {
16189 name: 'min-zoomed-font-size',
16190 type: t.size
16191 }, {
16192 name: 'z-compound-depth',
16193 type: t.zCompoundDepth,
16194 triggersZOrder: diff.any
16195 }, {
16196 name: 'z-index-compare',
16197 type: t.zIndexCompare,
16198 triggersZOrder: diff.any
16199 }, {
16200 name: 'z-index',
16201 type: t.nonNegativeInt,
16202 triggersZOrder: diff.any
16203 }];
16204 var overlay = [{
16205 name: 'overlay-padding',
16206 type: t.size,
16207 triggersBounds: diff.any
16208 }, {
16209 name: 'overlay-color',
16210 type: t.color
16211 }, {
16212 name: 'overlay-opacity',
16213 type: t.zeroOneNumber,
16214 triggersBounds: diff.zeroNonZero
16215 }];
16216 var transition = [{
16217 name: 'transition-property',
16218 type: t.propList
16219 }, {
16220 name: 'transition-duration',
16221 type: t.time
16222 }, {
16223 name: 'transition-delay',
16224 type: t.time
16225 }, {
16226 name: 'transition-timing-function',
16227 type: t.easing
16228 }];
16229
16230 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16231 if (parsedProp.value === 'label') {
16232 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16233 } else {
16234 return parsedProp.pfValue;
16235 }
16236 };
16237
16238 var nodeBody = [{
16239 name: 'height',
16240 type: t.nodeSize,
16241 triggersBounds: diff.any,
16242 hashOverride: nodeSizeHashOverride
16243 }, {
16244 name: 'width',
16245 type: t.nodeSize,
16246 triggersBounds: diff.any,
16247 hashOverride: nodeSizeHashOverride
16248 }, {
16249 name: 'shape',
16250 type: t.nodeShape,
16251 triggersBounds: diff.any
16252 }, {
16253 name: 'shape-polygon-points',
16254 type: t.polygonPointList,
16255 triggersBounds: diff.any
16256 }, {
16257 name: 'background-color',
16258 type: t.color
16259 }, {
16260 name: 'background-fill',
16261 type: t.fill
16262 }, {
16263 name: 'background-opacity',
16264 type: t.zeroOneNumber
16265 }, {
16266 name: 'background-blacken',
16267 type: t.nOneOneNumber
16268 }, {
16269 name: 'background-gradient-stop-colors',
16270 type: t.colors
16271 }, {
16272 name: 'background-gradient-stop-positions',
16273 type: t.percentages
16274 }, {
16275 name: 'background-gradient-direction',
16276 type: t.gradientDirection
16277 }, {
16278 name: 'padding',
16279 type: t.sizeMaybePercent,
16280 triggersBounds: diff.any
16281 }, {
16282 name: 'padding-relative-to',
16283 type: t.paddingRelativeTo,
16284 triggersBounds: diff.any
16285 }, {
16286 name: 'bounds-expansion',
16287 type: t.boundsExpansion,
16288 triggersBounds: diff.any
16289 }];
16290 var nodeBorder = [{
16291 name: 'border-color',
16292 type: t.color
16293 }, {
16294 name: 'border-opacity',
16295 type: t.zeroOneNumber
16296 }, {
16297 name: 'border-width',
16298 type: t.size,
16299 triggersBounds: diff.any
16300 }, {
16301 name: 'border-style',
16302 type: t.borderStyle
16303 }];
16304 var backgroundImage = [{
16305 name: 'background-image',
16306 type: t.urls
16307 }, {
16308 name: 'background-image-crossorigin',
16309 type: t.bgCrossOrigin
16310 }, {
16311 name: 'background-image-opacity',
16312 type: t.zeroOneNumbers
16313 }, {
16314 name: 'background-position-x',
16315 type: t.bgPos
16316 }, {
16317 name: 'background-position-y',
16318 type: t.bgPos
16319 }, {
16320 name: 'background-width-relative-to',
16321 type: t.bgRelativeTo
16322 }, {
16323 name: 'background-height-relative-to',
16324 type: t.bgRelativeTo
16325 }, {
16326 name: 'background-repeat',
16327 type: t.bgRepeat
16328 }, {
16329 name: 'background-fit',
16330 type: t.bgFit
16331 }, {
16332 name: 'background-clip',
16333 type: t.bgClip
16334 }, {
16335 name: 'background-width',
16336 type: t.bgWH
16337 }, {
16338 name: 'background-height',
16339 type: t.bgWH
16340 }, {
16341 name: 'background-offset-x',
16342 type: t.bgPos
16343 }, {
16344 name: 'background-offset-y',
16345 type: t.bgPos
16346 }];
16347 var compound = [{
16348 name: 'position',
16349 type: t.position,
16350 triggersBounds: diff.any
16351 }, {
16352 name: 'compound-sizing-wrt-labels',
16353 type: t.compoundIncludeLabels,
16354 triggersBounds: diff.any
16355 }, {
16356 name: 'min-width',
16357 type: t.size,
16358 triggersBounds: diff.any
16359 }, {
16360 name: 'min-width-bias-left',
16361 type: t.sizeMaybePercent,
16362 triggersBounds: diff.any
16363 }, {
16364 name: 'min-width-bias-right',
16365 type: t.sizeMaybePercent,
16366 triggersBounds: diff.any
16367 }, {
16368 name: 'min-height',
16369 type: t.size,
16370 triggersBounds: diff.any
16371 }, {
16372 name: 'min-height-bias-top',
16373 type: t.sizeMaybePercent,
16374 triggersBounds: diff.any
16375 }, {
16376 name: 'min-height-bias-bottom',
16377 type: t.sizeMaybePercent,
16378 triggersBounds: diff.any
16379 }];
16380 var edgeLine = [{
16381 name: 'line-style',
16382 type: t.lineStyle
16383 }, {
16384 name: 'line-color',
16385 type: t.color
16386 }, {
16387 name: 'line-fill',
16388 type: t.fill
16389 }, {
16390 name: 'line-cap',
16391 type: t.lineCap
16392 }, {
16393 name: 'line-dash-pattern',
16394 type: t.numbers
16395 }, {
16396 name: 'line-dash-offset',
16397 type: t.number
16398 }, {
16399 name: 'line-gradient-stop-colors',
16400 type: t.colors
16401 }, {
16402 name: 'line-gradient-stop-positions',
16403 type: t.percentages
16404 }, {
16405 name: 'curve-style',
16406 type: t.curveStyle,
16407 triggersBounds: diff.any,
16408 triggersBoundsOfParallelBeziers: true
16409 }, {
16410 name: 'haystack-radius',
16411 type: t.zeroOneNumber,
16412 triggersBounds: diff.any
16413 }, {
16414 name: 'source-endpoint',
16415 type: t.edgeEndpoint,
16416 triggersBounds: diff.any
16417 }, {
16418 name: 'target-endpoint',
16419 type: t.edgeEndpoint,
16420 triggersBounds: diff.any
16421 }, {
16422 name: 'control-point-step-size',
16423 type: t.size,
16424 triggersBounds: diff.any
16425 }, {
16426 name: 'control-point-distances',
16427 type: t.bidirectionalSizes,
16428 triggersBounds: diff.any
16429 }, {
16430 name: 'control-point-weights',
16431 type: t.numbers,
16432 triggersBounds: diff.any
16433 }, {
16434 name: 'segment-distances',
16435 type: t.bidirectionalSizes,
16436 triggersBounds: diff.any
16437 }, {
16438 name: 'segment-weights',
16439 type: t.numbers,
16440 triggersBounds: diff.any
16441 }, {
16442 name: 'taxi-turn',
16443 type: t.sizeMaybePercent,
16444 triggersBounds: diff.any
16445 }, {
16446 name: 'taxi-turn-min-distance',
16447 type: t.size,
16448 triggersBounds: diff.any
16449 }, {
16450 name: 'taxi-direction',
16451 type: t.axisDirection,
16452 triggersBounds: diff.any
16453 }, {
16454 name: 'edge-distances',
16455 type: t.edgeDistances,
16456 triggersBounds: diff.any
16457 }, {
16458 name: 'arrow-scale',
16459 type: t.positiveNumber,
16460 triggersBounds: diff.any
16461 }, {
16462 name: 'loop-direction',
16463 type: t.angle,
16464 triggersBounds: diff.any
16465 }, {
16466 name: 'loop-sweep',
16467 type: t.angle,
16468 triggersBounds: diff.any
16469 }, {
16470 name: 'source-distance-from-node',
16471 type: t.size,
16472 triggersBounds: diff.any
16473 }, {
16474 name: 'target-distance-from-node',
16475 type: t.size,
16476 triggersBounds: diff.any
16477 }];
16478 var ghost = [{
16479 name: 'ghost',
16480 type: t.bool,
16481 triggersBounds: diff.any
16482 }, {
16483 name: 'ghost-offset-x',
16484 type: t.bidirectionalSize,
16485 triggersBounds: diff.any
16486 }, {
16487 name: 'ghost-offset-y',
16488 type: t.bidirectionalSize,
16489 triggersBounds: diff.any
16490 }, {
16491 name: 'ghost-opacity',
16492 type: t.zeroOneNumber
16493 }];
16494 var core = [{
16495 name: 'selection-box-color',
16496 type: t.color
16497 }, {
16498 name: 'selection-box-opacity',
16499 type: t.zeroOneNumber
16500 }, {
16501 name: 'selection-box-border-color',
16502 type: t.color
16503 }, {
16504 name: 'selection-box-border-width',
16505 type: t.size
16506 }, {
16507 name: 'active-bg-color',
16508 type: t.color
16509 }, {
16510 name: 'active-bg-opacity',
16511 type: t.zeroOneNumber
16512 }, {
16513 name: 'active-bg-size',
16514 type: t.size
16515 }, {
16516 name: 'outside-texture-bg-color',
16517 type: t.color
16518 }, {
16519 name: 'outside-texture-bg-opacity',
16520 type: t.zeroOneNumber
16521 }]; // pie backgrounds for nodes
16522
16523 var pie = [];
16524 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16525
16526 pie.push({
16527 name: 'pie-size',
16528 type: t.sizeMaybePercent
16529 });
16530
16531 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16532 pie.push({
16533 name: 'pie-' + i + '-background-color',
16534 type: t.color
16535 });
16536 pie.push({
16537 name: 'pie-' + i + '-background-size',
16538 type: t.percent
16539 });
16540 pie.push({
16541 name: 'pie-' + i + '-background-opacity',
16542 type: t.zeroOneNumber
16543 });
16544 } // edge arrows
16545
16546
16547 var edgeArrow = [];
16548 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16549 [{
16550 name: 'arrow-shape',
16551 type: t.arrowShape,
16552 triggersBounds: diff.any
16553 }, {
16554 name: 'arrow-color',
16555 type: t.color
16556 }, {
16557 name: 'arrow-fill',
16558 type: t.arrowFill
16559 }].forEach(function (prop) {
16560 arrowPrefixes.forEach(function (prefix) {
16561 var name = prefix + '-' + prop.name;
16562 var type = prop.type,
16563 triggersBounds = prop.triggersBounds;
16564 edgeArrow.push({
16565 name: name,
16566 type: type,
16567 triggersBounds: triggersBounds
16568 });
16569 });
16570 }, {});
16571 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16572 var propGroups = styfn$6.propertyGroups = {
16573 // common to all eles
16574 behavior: behavior,
16575 transition: transition,
16576 visibility: visibility,
16577 overlay: overlay,
16578 ghost: ghost,
16579 // labels
16580 commonLabel: commonLabel,
16581 labelDimensions: labelDimensions,
16582 mainLabel: mainLabel,
16583 sourceLabel: sourceLabel,
16584 targetLabel: targetLabel,
16585 // node props
16586 nodeBody: nodeBody,
16587 nodeBorder: nodeBorder,
16588 backgroundImage: backgroundImage,
16589 pie: pie,
16590 compound: compound,
16591 // edge props
16592 edgeLine: edgeLine,
16593 edgeArrow: edgeArrow,
16594 core: core
16595 };
16596 var propGroupNames = styfn$6.propertyGroupNames = {};
16597 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16598 propGroupKeys.forEach(function (key) {
16599 propGroupNames[key] = propGroups[key].map(function (prop) {
16600 return prop.name;
16601 });
16602 propGroups[key].forEach(function (prop) {
16603 return prop.groupKey = key;
16604 });
16605 }); // define aliases
16606
16607 var aliases = styfn$6.aliases = [{
16608 name: 'content',
16609 pointsTo: 'label'
16610 }, {
16611 name: 'control-point-distance',
16612 pointsTo: 'control-point-distances'
16613 }, {
16614 name: 'control-point-weight',
16615 pointsTo: 'control-point-weights'
16616 }, {
16617 name: 'edge-text-rotation',
16618 pointsTo: 'text-rotation'
16619 }, {
16620 name: 'padding-left',
16621 pointsTo: 'padding'
16622 }, {
16623 name: 'padding-right',
16624 pointsTo: 'padding'
16625 }, {
16626 name: 'padding-top',
16627 pointsTo: 'padding'
16628 }, {
16629 name: 'padding-bottom',
16630 pointsTo: 'padding'
16631 }]; // list of property names
16632
16633 styfn$6.propertyNames = props.map(function (p) {
16634 return p.name;
16635 }); // allow access of properties by name ( e.g. style.properties.height )
16636
16637 for (var _i = 0; _i < props.length; _i++) {
16638 var prop = props[_i];
16639 props[prop.name] = prop; // allow lookup by name
16640 } // map aliases
16641
16642
16643 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
16644 var alias = aliases[_i2];
16645 var pointsToProp = props[alias.pointsTo];
16646 var aliasProp = {
16647 name: alias.name,
16648 alias: true,
16649 pointsTo: pointsToProp
16650 }; // add alias prop for parsing
16651
16652 props.push(aliasProp);
16653 props[alias.name] = aliasProp; // allow lookup by name
16654 }
16655})();
16656
16657styfn$6.getDefaultProperty = function (name) {
16658 return this.getDefaultProperties()[name];
16659};
16660
16661styfn$6.getDefaultProperties = function () {
16662 var _p = this._private;
16663
16664 if (_p.defaultProperties != null) {
16665 return _p.defaultProperties;
16666 }
16667
16668 var rawProps = extend({
16669 // core props
16670 'selection-box-color': '#ddd',
16671 'selection-box-opacity': 0.65,
16672 'selection-box-border-color': '#aaa',
16673 'selection-box-border-width': 1,
16674 'active-bg-color': 'black',
16675 'active-bg-opacity': 0.15,
16676 'active-bg-size': 30,
16677 'outside-texture-bg-color': '#000',
16678 'outside-texture-bg-opacity': 0.125,
16679 // common node/edge props
16680 'events': 'yes',
16681 'text-events': 'no',
16682 'text-valign': 'top',
16683 'text-halign': 'center',
16684 'text-justification': 'auto',
16685 'line-height': 1,
16686 'color': '#000',
16687 'text-outline-color': '#000',
16688 'text-outline-width': 0,
16689 'text-outline-opacity': 1,
16690 'text-opacity': 1,
16691 'text-decoration': 'none',
16692 'text-transform': 'none',
16693 'text-wrap': 'none',
16694 'text-overflow-wrap': 'whitespace',
16695 'text-max-width': 9999,
16696 'text-background-color': '#000',
16697 'text-background-opacity': 0,
16698 'text-background-shape': 'rectangle',
16699 'text-background-padding': 0,
16700 'text-border-opacity': 0,
16701 'text-border-width': 0,
16702 'text-border-style': 'solid',
16703 'text-border-color': '#000',
16704 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
16705 'font-style': 'normal',
16706 'font-weight': 'normal',
16707 'font-size': 16,
16708 'min-zoomed-font-size': 0,
16709 'text-rotation': 'none',
16710 'source-text-rotation': 'none',
16711 'target-text-rotation': 'none',
16712 'visibility': 'visible',
16713 'display': 'element',
16714 'opacity': 1,
16715 'z-compound-depth': 'auto',
16716 'z-index-compare': 'auto',
16717 'z-index': 0,
16718 'label': '',
16719 'text-margin-x': 0,
16720 'text-margin-y': 0,
16721 'source-label': '',
16722 'source-text-offset': 0,
16723 'source-text-margin-x': 0,
16724 'source-text-margin-y': 0,
16725 'target-label': '',
16726 'target-text-offset': 0,
16727 'target-text-margin-x': 0,
16728 'target-text-margin-y': 0,
16729 'overlay-opacity': 0,
16730 'overlay-color': '#000',
16731 'overlay-padding': 10,
16732 'transition-property': 'none',
16733 'transition-duration': 0,
16734 'transition-delay': 0,
16735 'transition-timing-function': 'linear',
16736 // node props
16737 'background-blacken': 0,
16738 'background-color': '#999',
16739 'background-fill': 'solid',
16740 'background-opacity': 1,
16741 'background-image': 'none',
16742 'background-image-crossorigin': 'anonymous',
16743 'background-image-opacity': 1,
16744 'background-position-x': '50%',
16745 'background-position-y': '50%',
16746 'background-offset-x': 0,
16747 'background-offset-y': 0,
16748 'background-width-relative-to': 'include-padding',
16749 'background-height-relative-to': 'include-padding',
16750 'background-repeat': 'no-repeat',
16751 'background-fit': 'none',
16752 'background-clip': 'node',
16753 'background-width': 'auto',
16754 'background-height': 'auto',
16755 'border-color': '#000',
16756 'border-opacity': 1,
16757 'border-width': 0,
16758 'border-style': 'solid',
16759 'height': 30,
16760 'width': 30,
16761 'shape': 'ellipse',
16762 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
16763 'bounds-expansion': 0,
16764 // node gradient
16765 'background-gradient-direction': 'to-bottom',
16766 'background-gradient-stop-colors': '#999',
16767 'background-gradient-stop-positions': '0%',
16768 // ghost props
16769 'ghost': 'no',
16770 'ghost-offset-y': 0,
16771 'ghost-offset-x': 0,
16772 'ghost-opacity': 0,
16773 // compound props
16774 'padding': 0,
16775 'padding-relative-to': 'width',
16776 'position': 'origin',
16777 'compound-sizing-wrt-labels': 'include',
16778 'min-width': 0,
16779 'min-width-bias-left': 0,
16780 'min-width-bias-right': 0,
16781 'min-height': 0,
16782 'min-height-bias-top': 0,
16783 'min-height-bias-bottom': 0
16784 }, {
16785 // node pie bg
16786 'pie-size': '100%'
16787 }, [{
16788 name: 'pie-{{i}}-background-color',
16789 value: 'black'
16790 }, {
16791 name: 'pie-{{i}}-background-size',
16792 value: '0%'
16793 }, {
16794 name: 'pie-{{i}}-background-opacity',
16795 value: 1
16796 }].reduce(function (css, prop) {
16797 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16798 var name = prop.name.replace('{{i}}', i);
16799 var val = prop.value;
16800 css[name] = val;
16801 }
16802
16803 return css;
16804 }, {}), {
16805 // edge props
16806 'line-style': 'solid',
16807 'line-color': '#999',
16808 'line-fill': 'solid',
16809 'line-cap': 'butt',
16810 'line-gradient-stop-colors': '#999',
16811 'line-gradient-stop-positions': '0%',
16812 'control-point-step-size': 40,
16813 'control-point-weights': 0.5,
16814 'segment-weights': 0.5,
16815 'segment-distances': 20,
16816 'taxi-turn': '50%',
16817 'taxi-turn-min-distance': 10,
16818 'taxi-direction': 'auto',
16819 'edge-distances': 'intersection',
16820 'curve-style': 'haystack',
16821 'haystack-radius': 0,
16822 'arrow-scale': 1,
16823 'loop-direction': '-45deg',
16824 'loop-sweep': '-90deg',
16825 'source-distance-from-node': 0,
16826 'target-distance-from-node': 0,
16827 'source-endpoint': 'outside-to-node',
16828 'target-endpoint': 'outside-to-node',
16829 'line-dash-pattern': [6, 3],
16830 'line-dash-offset': 0
16831 }, [{
16832 name: 'arrow-shape',
16833 value: 'none'
16834 }, {
16835 name: 'arrow-color',
16836 value: '#999'
16837 }, {
16838 name: 'arrow-fill',
16839 value: 'filled'
16840 }].reduce(function (css, prop) {
16841 styfn$6.arrowPrefixes.forEach(function (prefix) {
16842 var name = prefix + '-' + prop.name;
16843 var val = prop.value;
16844 css[name] = val;
16845 });
16846 return css;
16847 }, {}));
16848 var parsedProps = {};
16849
16850 for (var i = 0; i < this.properties.length; i++) {
16851 var prop = this.properties[i];
16852
16853 if (prop.pointsTo) {
16854 continue;
16855 }
16856
16857 var name = prop.name;
16858 var val = rawProps[name];
16859 var parsedProp = this.parse(name, val);
16860 parsedProps[name] = parsedProp;
16861 }
16862
16863 _p.defaultProperties = parsedProps;
16864 return _p.defaultProperties;
16865};
16866
16867styfn$6.addDefaultStylesheet = function () {
16868 this.selector(':parent').css({
16869 'shape': 'rectangle',
16870 'padding': 10,
16871 'background-color': '#eee',
16872 'border-color': '#ccc',
16873 'border-width': 1
16874 }).selector('edge').css({
16875 'width': 3
16876 }).selector(':loop').css({
16877 'curve-style': 'bezier'
16878 }).selector('edge:compound').css({
16879 'curve-style': 'bezier',
16880 'source-endpoint': 'outside-to-line',
16881 'target-endpoint': 'outside-to-line'
16882 }).selector(':selected').css({
16883 'background-color': '#0169D9',
16884 'line-color': '#0169D9',
16885 'source-arrow-color': '#0169D9',
16886 'target-arrow-color': '#0169D9',
16887 'mid-source-arrow-color': '#0169D9',
16888 'mid-target-arrow-color': '#0169D9'
16889 }).selector(':parent:selected').css({
16890 'background-color': '#CCE1F9',
16891 'border-color': '#aec8e5'
16892 }).selector(':active').css({
16893 'overlay-color': 'black',
16894 'overlay-padding': 10,
16895 'overlay-opacity': 0.25
16896 });
16897 this.defaultLength = this.length;
16898};
16899
16900var styfn$7 = {}; // a caching layer for property parsing
16901
16902styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
16903 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
16904
16905 if (fn(value)) {
16906 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
16907 }
16908
16909 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
16910 var bypassKey = propIsBypass ? 't' : 'f';
16911 var valueKey = '' + value;
16912 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
16913 var propCache = self.propCache = self.propCache || [];
16914 var ret;
16915
16916 if (!(ret = propCache[argHash])) {
16917 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
16918 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
16919 // - mappings can't be shared b/c mappings are per-element
16920
16921
16922 if (propIsBypass || propIsFlat === 'mapping') {
16923 // need a copy since props are mutated later in their lifecycles
16924 ret = copy(ret);
16925
16926 if (ret) {
16927 ret.value = copy(ret.value); // because it could be an array, e.g. colour
16928 }
16929 }
16930
16931 return ret;
16932};
16933
16934styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
16935 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
16936
16937 if (!prop && value != null) {
16938 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
16939 }
16940
16941 return prop;
16942}; // parse a property; return null on invalid; return parsed property otherwise
16943// fields :
16944// - name : the name of the property
16945// - value : the parsed, native-typed value of the property
16946// - strValue : a string value that represents the property value in valid css
16947// - bypass : true iff the property is a bypass property
16948
16949
16950styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
16951 var self = this;
16952 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
16953
16954 var property = self.properties[name];
16955 var passedValue = value;
16956 var types = self.types;
16957
16958 if (!property) {
16959 return null;
16960 } // return null on property of unknown name
16961
16962
16963 if (value === undefined) {
16964 return null;
16965 } // can't assign undefined
16966 // the property may be an alias
16967
16968
16969 if (property.alias) {
16970 property = property.pointsTo;
16971 name = property.name;
16972 }
16973
16974 var valueIsString = string(value);
16975
16976 if (valueIsString) {
16977 // trim the value to make parsing easier
16978 value = value.trim();
16979 }
16980
16981 var type = property.type;
16982
16983 if (!type) {
16984 return null;
16985 } // no type, no luck
16986 // check if bypass is null or empty string (i.e. indication to delete bypass property)
16987
16988
16989 if (propIsBypass && (value === '' || value === null)) {
16990 return {
16991 name: name,
16992 value: value,
16993 bypass: true,
16994 deleteBypass: true
16995 };
16996 } // check if value is a function used as a mapper
16997
16998
16999 if (fn(value)) {
17000 return {
17001 name: name,
17002 value: value,
17003 strValue: 'fn',
17004 mapped: types.fn,
17005 bypass: propIsBypass
17006 };
17007 } // check if value is mapped
17008
17009
17010 var data, mapData;
17011
17012 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))) {
17013 if (propIsBypass) {
17014 return false;
17015 } // mappers not allowed in bypass
17016
17017
17018 var mapped = types.data;
17019 return {
17020 name: name,
17021 value: data,
17022 strValue: '' + value,
17023 mapped: mapped,
17024 field: data[1],
17025 bypass: propIsBypass
17026 };
17027 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17028 if (propIsBypass) {
17029 return false;
17030 } // mappers not allowed in bypass
17031
17032
17033 if (type.multiple) {
17034 return false;
17035 } // impossible to map to num
17036
17037
17038 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17039
17040 if (!(type.color || type.number)) {
17041 return false;
17042 }
17043
17044 var valueMin = this.parse(name, mapData[4]); // parse to validate
17045
17046 if (!valueMin || valueMin.mapped) {
17047 return false;
17048 } // can't be invalid or mapped
17049
17050
17051 var valueMax = this.parse(name, mapData[5]); // parse to validate
17052
17053 if (!valueMax || valueMax.mapped) {
17054 return false;
17055 } // can't be invalid or mapped
17056 // check if valueMin and valueMax are the same
17057
17058
17059 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17060 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17061 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17062 } else if (type.color) {
17063 var c1 = valueMin.value;
17064 var c2 = valueMax.value;
17065 var same = c1[0] === c2[0] // red
17066 && c1[1] === c2[1] // green
17067 && c1[2] === c2[2] // blue
17068 && ( // optional alpha
17069 c1[3] === c2[3] // same alpha outright
17070 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17071 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17072 );
17073
17074 if (same) {
17075 return false;
17076 } // can't make a mapper without a range
17077
17078 }
17079
17080 return {
17081 name: name,
17082 value: mapData,
17083 strValue: '' + value,
17084 mapped: _mapped,
17085 field: mapData[1],
17086 fieldMin: parseFloat(mapData[2]),
17087 // min & max are numeric
17088 fieldMax: parseFloat(mapData[3]),
17089 valueMin: valueMin.value,
17090 valueMax: valueMax.value,
17091 bypass: propIsBypass
17092 };
17093 }
17094
17095 if (type.multiple && propIsFlat !== 'multiple') {
17096 var vals;
17097
17098 if (valueIsString) {
17099 vals = value.split(/\s+/);
17100 } else if (array(value)) {
17101 vals = value;
17102 } else {
17103 vals = [value];
17104 }
17105
17106 if (type.evenMultiple && vals.length % 2 !== 0) {
17107 return null;
17108 }
17109
17110 var valArr = [];
17111 var unitsArr = [];
17112 var pfValArr = [];
17113 var strVal = '';
17114 var hasEnum = false;
17115
17116 for (var i = 0; i < vals.length; i++) {
17117 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17118 hasEnum = hasEnum || string(p.value);
17119 valArr.push(p.value);
17120 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17121 unitsArr.push(p.units);
17122 strVal += (i > 0 ? ' ' : '') + p.strValue;
17123 }
17124
17125 if (type.validate && !type.validate(valArr, unitsArr)) {
17126 return null;
17127 }
17128
17129 if (type.singleEnum && hasEnum) {
17130 if (valArr.length === 1 && string(valArr[0])) {
17131 return {
17132 name: name,
17133 value: valArr[0],
17134 strValue: valArr[0],
17135 bypass: propIsBypass
17136 };
17137 } else {
17138 return null;
17139 }
17140 }
17141
17142 return {
17143 name: name,
17144 value: valArr,
17145 pfValue: pfValArr,
17146 strValue: strVal,
17147 bypass: propIsBypass,
17148 units: unitsArr
17149 };
17150 } // several types also allow enums
17151
17152
17153 var checkEnums = function checkEnums() {
17154 for (var _i = 0; _i < type.enums.length; _i++) {
17155 var en = type.enums[_i];
17156
17157 if (en === value) {
17158 return {
17159 name: name,
17160 value: value,
17161 strValue: '' + value,
17162 bypass: propIsBypass
17163 };
17164 }
17165 }
17166
17167 return null;
17168 }; // check the type and return the appropriate object
17169
17170
17171 if (type.number) {
17172 var units;
17173 var implicitUnits = 'px'; // not set => px
17174
17175 if (type.units) {
17176 // use specified units if set
17177 units = type.units;
17178 }
17179
17180 if (type.implicitUnits) {
17181 implicitUnits = type.implicitUnits;
17182 }
17183
17184 if (!type.unitless) {
17185 if (valueIsString) {
17186 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17187
17188 if (units) {
17189 unitsRegex = units;
17190 } // only allow explicit units if so set
17191
17192
17193 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17194
17195 if (match) {
17196 value = match[1];
17197 units = match[2] || implicitUnits;
17198 }
17199 } else if (!units || type.implicitUnits) {
17200 units = implicitUnits; // implicitly px if unspecified
17201 }
17202 }
17203
17204 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17205
17206 if (isNaN(value) && type.enums === undefined) {
17207 return null;
17208 } // check if this number type also accepts special keywords in place of numbers
17209 // (i.e. `left`, `auto`, etc)
17210
17211
17212 if (isNaN(value) && type.enums !== undefined) {
17213 value = passedValue;
17214 return checkEnums();
17215 } // check if value must be an integer
17216
17217
17218 if (type.integer && !integer(value)) {
17219 return null;
17220 } // check value is within range
17221
17222
17223 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17224 return null;
17225 }
17226
17227 var ret = {
17228 name: name,
17229 value: value,
17230 strValue: '' + value + (units ? units : ''),
17231 units: units,
17232 bypass: propIsBypass
17233 }; // normalise value in pixels
17234
17235 if (type.unitless || units !== 'px' && units !== 'em') {
17236 ret.pfValue = value;
17237 } else {
17238 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17239 } // normalise value in ms
17240
17241
17242 if (units === 'ms' || units === 's') {
17243 ret.pfValue = units === 'ms' ? value : 1000 * value;
17244 } // normalise value in rad
17245
17246
17247 if (units === 'deg' || units === 'rad') {
17248 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17249 } // normalize value in %
17250
17251
17252 if (units === '%') {
17253 ret.pfValue = value / 100;
17254 }
17255
17256 return ret;
17257 } else if (type.propList) {
17258 var props = [];
17259 var propsStr = '' + value;
17260
17261 if (propsStr === 'none') ; else {
17262 // go over each prop
17263 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17264
17265 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17266 var propName = propsSplit[_i2].trim();
17267
17268 if (self.properties[propName]) {
17269 props.push(propName);
17270 } else {
17271 warn('`' + propName + '` is not a valid property name');
17272 }
17273 }
17274
17275 if (props.length === 0) {
17276 return null;
17277 }
17278 }
17279
17280 return {
17281 name: name,
17282 value: props,
17283 strValue: props.length === 0 ? 'none' : props.join(' '),
17284 bypass: propIsBypass
17285 };
17286 } else if (type.color) {
17287 var tuple = color2tuple(value);
17288
17289 if (!tuple) {
17290 return null;
17291 }
17292
17293 return {
17294 name: name,
17295 value: tuple,
17296 pfValue: tuple,
17297 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17298 // n.b. no spaces b/c of multiple support
17299 bypass: propIsBypass
17300 };
17301 } else if (type.regex || type.regexes) {
17302 // first check enums
17303 if (type.enums) {
17304 var enumProp = checkEnums();
17305
17306 if (enumProp) {
17307 return enumProp;
17308 }
17309 }
17310
17311 var regexes = type.regexes ? type.regexes : [type.regex];
17312
17313 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17314 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17315
17316 var m = regex.exec(value);
17317
17318 if (m) {
17319 // regex matches
17320 return {
17321 name: name,
17322 value: type.singleRegexMatchValue ? m[1] : m,
17323 strValue: '' + value,
17324 bypass: propIsBypass
17325 };
17326 }
17327 }
17328
17329 return null; // didn't match any
17330 } else if (type.string) {
17331 // just return
17332 return {
17333 name: name,
17334 value: '' + value,
17335 strValue: '' + value,
17336 bypass: propIsBypass
17337 };
17338 } else if (type.enums) {
17339 // check enums last because it's a combo type in others
17340 return checkEnums();
17341 } else {
17342 return null; // not a type we can handle
17343 }
17344};
17345
17346var Style = function Style(cy) {
17347 if (!(this instanceof Style)) {
17348 return new Style(cy);
17349 }
17350
17351 if (!core(cy)) {
17352 error('A style must have a core reference');
17353 return;
17354 }
17355
17356 this._private = {
17357 cy: cy,
17358 coreStyle: {}
17359 };
17360 this.length = 0;
17361 this.resetToDefault();
17362};
17363
17364var styfn$8 = Style.prototype;
17365
17366styfn$8.instanceString = function () {
17367 return 'style';
17368}; // remove all contexts
17369
17370
17371styfn$8.clear = function () {
17372 for (var i = 0; i < this.length; i++) {
17373 this[i] = undefined;
17374 }
17375
17376 this.length = 0;
17377 var _p = this._private;
17378 _p.newStyle = true;
17379 return this; // chaining
17380};
17381
17382styfn$8.resetToDefault = function () {
17383 this.clear();
17384 this.addDefaultStylesheet();
17385 return this;
17386}; // builds a style object for the 'core' selector
17387
17388
17389styfn$8.core = function (propName) {
17390 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17391}; // create a new context from the specified selector string and switch to that context
17392
17393
17394styfn$8.selector = function (selectorStr) {
17395 // 'core' is a special case and does not need a selector
17396 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17397 var i = this.length++; // new context means new index
17398
17399 this[i] = {
17400 selector: selector,
17401 properties: [],
17402 mappedProperties: [],
17403 index: i
17404 };
17405 return this; // chaining
17406}; // add one or many css rules to the current context
17407
17408
17409styfn$8.css = function () {
17410 var self = this;
17411 var args = arguments;
17412
17413 if (args.length === 1) {
17414 var map = args[0];
17415
17416 for (var i = 0; i < self.properties.length; i++) {
17417 var prop = self.properties[i];
17418 var mapVal = map[prop.name];
17419
17420 if (mapVal === undefined) {
17421 mapVal = map[dash2camel(prop.name)];
17422 }
17423
17424 if (mapVal !== undefined) {
17425 this.cssRule(prop.name, mapVal);
17426 }
17427 }
17428 } else if (args.length === 2) {
17429 this.cssRule(args[0], args[1]);
17430 } // do nothing if args are invalid
17431
17432
17433 return this; // chaining
17434};
17435
17436styfn$8.style = styfn$8.css; // add a single css rule to the current context
17437
17438styfn$8.cssRule = function (name, value) {
17439 // name-value pair
17440 var property = this.parse(name, value); // add property to current context if valid
17441
17442 if (property) {
17443 var i = this.length - 1;
17444 this[i].properties.push(property);
17445 this[i].properties[property.name] = property; // allow access by name as well
17446
17447 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17448 this._private.hasPie = true;
17449 }
17450
17451 if (property.mapped) {
17452 this[i].mappedProperties.push(property);
17453 } // add to core style if necessary
17454
17455
17456 var currentSelectorIsCore = !this[i].selector;
17457
17458 if (currentSelectorIsCore) {
17459 this._private.coreStyle[property.name] = property;
17460 }
17461 }
17462
17463 return this; // chaining
17464};
17465
17466styfn$8.append = function (style) {
17467 if (stylesheet(style)) {
17468 style.appendToStyle(this);
17469 } else if (array(style)) {
17470 this.appendFromJson(style);
17471 } else if (string(style)) {
17472 this.appendFromString(style);
17473 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17474
17475
17476 return this;
17477}; // static function
17478
17479
17480Style.fromJson = function (cy, json) {
17481 var style = new Style(cy);
17482 style.fromJson(json);
17483 return style;
17484};
17485
17486Style.fromString = function (cy, string) {
17487 return new Style(cy).fromString(string);
17488};
17489
17490[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17491 extend(styfn$8, props);
17492});
17493Style.types = styfn$8.types;
17494Style.properties = styfn$8.properties;
17495Style.propertyGroups = styfn$8.propertyGroups;
17496Style.propertyGroupNames = styfn$8.propertyGroupNames;
17497Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17498
17499var corefn$7 = {
17500 style: function style(newStyle) {
17501 if (newStyle) {
17502 var s = this.setStyle(newStyle);
17503 s.update();
17504 }
17505
17506 return this._private.style;
17507 },
17508 setStyle: function setStyle(style) {
17509 var _p = this._private;
17510
17511 if (stylesheet(style)) {
17512 _p.style = style.generateStyle(this);
17513 } else if (array(style)) {
17514 _p.style = Style.fromJson(this, style);
17515 } else if (string(style)) {
17516 _p.style = Style.fromString(this, style);
17517 } else {
17518 _p.style = Style(this);
17519 }
17520
17521 return _p.style;
17522 }
17523};
17524
17525var defaultSelectionType = 'single';
17526var corefn$8 = {
17527 autolock: function autolock(bool) {
17528 if (bool !== undefined) {
17529 this._private.autolock = bool ? true : false;
17530 } else {
17531 return this._private.autolock;
17532 }
17533
17534 return this; // chaining
17535 },
17536 autoungrabify: function autoungrabify(bool) {
17537 if (bool !== undefined) {
17538 this._private.autoungrabify = bool ? true : false;
17539 } else {
17540 return this._private.autoungrabify;
17541 }
17542
17543 return this; // chaining
17544 },
17545 autounselectify: function autounselectify(bool) {
17546 if (bool !== undefined) {
17547 this._private.autounselectify = bool ? true : false;
17548 } else {
17549 return this._private.autounselectify;
17550 }
17551
17552 return this; // chaining
17553 },
17554 selectionType: function selectionType(selType) {
17555 var _p = this._private;
17556
17557 if (_p.selectionType == null) {
17558 _p.selectionType = defaultSelectionType;
17559 }
17560
17561 if (selType !== undefined) {
17562 if (selType === 'additive' || selType === 'single') {
17563 _p.selectionType = selType;
17564 }
17565 } else {
17566 return _p.selectionType;
17567 }
17568
17569 return this;
17570 },
17571 panningEnabled: function panningEnabled(bool) {
17572 if (bool !== undefined) {
17573 this._private.panningEnabled = bool ? true : false;
17574 } else {
17575 return this._private.panningEnabled;
17576 }
17577
17578 return this; // chaining
17579 },
17580 userPanningEnabled: function userPanningEnabled(bool) {
17581 if (bool !== undefined) {
17582 this._private.userPanningEnabled = bool ? true : false;
17583 } else {
17584 return this._private.userPanningEnabled;
17585 }
17586
17587 return this; // chaining
17588 },
17589 zoomingEnabled: function zoomingEnabled(bool) {
17590 if (bool !== undefined) {
17591 this._private.zoomingEnabled = bool ? true : false;
17592 } else {
17593 return this._private.zoomingEnabled;
17594 }
17595
17596 return this; // chaining
17597 },
17598 userZoomingEnabled: function userZoomingEnabled(bool) {
17599 if (bool !== undefined) {
17600 this._private.userZoomingEnabled = bool ? true : false;
17601 } else {
17602 return this._private.userZoomingEnabled;
17603 }
17604
17605 return this; // chaining
17606 },
17607 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17608 if (bool !== undefined) {
17609 this._private.boxSelectionEnabled = bool ? true : false;
17610 } else {
17611 return this._private.boxSelectionEnabled;
17612 }
17613
17614 return this; // chaining
17615 },
17616 pan: function pan() {
17617 var args = arguments;
17618 var pan = this._private.pan;
17619 var dim, val, dims, x, y;
17620
17621 switch (args.length) {
17622 case 0:
17623 // .pan()
17624 return pan;
17625
17626 case 1:
17627 if (string(args[0])) {
17628 // .pan('x')
17629 dim = args[0];
17630 return pan[dim];
17631 } else if (plainObject(args[0])) {
17632 // .pan({ x: 0, y: 100 })
17633 if (!this._private.panningEnabled) {
17634 return this;
17635 }
17636
17637 dims = args[0];
17638 x = dims.x;
17639 y = dims.y;
17640
17641 if (number(x)) {
17642 pan.x = x;
17643 }
17644
17645 if (number(y)) {
17646 pan.y = y;
17647 }
17648
17649 this.emit('pan viewport');
17650 }
17651
17652 break;
17653
17654 case 2:
17655 // .pan('x', 100)
17656 if (!this._private.panningEnabled) {
17657 return this;
17658 }
17659
17660 dim = args[0];
17661 val = args[1];
17662
17663 if ((dim === 'x' || dim === 'y') && number(val)) {
17664 pan[dim] = val;
17665 }
17666
17667 this.emit('pan viewport');
17668 break;
17669
17670 default:
17671 break;
17672 // invalid
17673 }
17674
17675 this.notify('viewport');
17676 return this; // chaining
17677 },
17678 panBy: function panBy(arg0, arg1) {
17679 var args = arguments;
17680 var pan = this._private.pan;
17681 var dim, val, dims, x, y;
17682
17683 if (!this._private.panningEnabled) {
17684 return this;
17685 }
17686
17687 switch (args.length) {
17688 case 1:
17689 if (plainObject(arg0)) {
17690 // .panBy({ x: 0, y: 100 })
17691 dims = args[0];
17692 x = dims.x;
17693 y = dims.y;
17694
17695 if (number(x)) {
17696 pan.x += x;
17697 }
17698
17699 if (number(y)) {
17700 pan.y += y;
17701 }
17702
17703 this.emit('pan viewport');
17704 }
17705
17706 break;
17707
17708 case 2:
17709 // .panBy('x', 100)
17710 dim = arg0;
17711 val = arg1;
17712
17713 if ((dim === 'x' || dim === 'y') && number(val)) {
17714 pan[dim] += val;
17715 }
17716
17717 this.emit('pan viewport');
17718 break;
17719
17720 default:
17721 break;
17722 // invalid
17723 }
17724
17725 this.notify('viewport');
17726 return this; // chaining
17727 },
17728 fit: function fit(elements, padding) {
17729 var viewportState = this.getFitViewport(elements, padding);
17730
17731 if (viewportState) {
17732 var _p = this._private;
17733 _p.zoom = viewportState.zoom;
17734 _p.pan = viewportState.pan;
17735 this.emit('pan zoom viewport');
17736 this.notify('viewport');
17737 }
17738
17739 return this; // chaining
17740 },
17741 getFitViewport: function getFitViewport(elements, padding) {
17742 if (number(elements) && padding === undefined) {
17743 // elements is optional
17744 padding = elements;
17745 elements = undefined;
17746 }
17747
17748 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
17749 return;
17750 }
17751
17752 var bb;
17753
17754 if (string(elements)) {
17755 var sel = elements;
17756 elements = this.$(sel);
17757 } else if (boundingBox(elements)) {
17758 // assume bb
17759 var bbe = elements;
17760 bb = {
17761 x1: bbe.x1,
17762 y1: bbe.y1,
17763 x2: bbe.x2,
17764 y2: bbe.y2
17765 };
17766 bb.w = bb.x2 - bb.x1;
17767 bb.h = bb.y2 - bb.y1;
17768 } else if (!elementOrCollection(elements)) {
17769 elements = this.mutableElements();
17770 }
17771
17772 if (elementOrCollection(elements) && elements.empty()) {
17773 return;
17774 } // can't fit to nothing
17775
17776
17777 bb = bb || elements.boundingBox();
17778 var w = this.width();
17779 var h = this.height();
17780 var zoom;
17781 padding = number(padding) ? padding : 0;
17782
17783 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
17784 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
17785
17786 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
17787 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
17788 var pan = {
17789 // now pan to middle
17790 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
17791 y: (h - zoom * (bb.y1 + bb.y2)) / 2
17792 };
17793 return {
17794 zoom: zoom,
17795 pan: pan
17796 };
17797 }
17798
17799 return;
17800 },
17801 zoomRange: function zoomRange(min, max) {
17802 var _p = this._private;
17803
17804 if (max == null) {
17805 var opts = min;
17806 min = opts.min;
17807 max = opts.max;
17808 }
17809
17810 if (number(min) && number(max) && min <= max) {
17811 _p.minZoom = min;
17812 _p.maxZoom = max;
17813 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
17814 _p.minZoom = min;
17815 } else if (number(max) && min === undefined && max >= _p.minZoom) {
17816 _p.maxZoom = max;
17817 }
17818
17819 return this;
17820 },
17821 minZoom: function minZoom(zoom) {
17822 if (zoom === undefined) {
17823 return this._private.minZoom;
17824 } else {
17825 return this.zoomRange({
17826 min: zoom
17827 });
17828 }
17829 },
17830 maxZoom: function maxZoom(zoom) {
17831 if (zoom === undefined) {
17832 return this._private.maxZoom;
17833 } else {
17834 return this.zoomRange({
17835 max: zoom
17836 });
17837 }
17838 },
17839 getZoomedViewport: function getZoomedViewport(params) {
17840 var _p = this._private;
17841 var currentPan = _p.pan;
17842 var currentZoom = _p.zoom;
17843 var pos; // in rendered px
17844
17845 var zoom;
17846 var bail = false;
17847
17848 if (!_p.zoomingEnabled) {
17849 // zooming disabled
17850 bail = true;
17851 }
17852
17853 if (number(params)) {
17854 // then set the zoom
17855 zoom = params;
17856 } else if (plainObject(params)) {
17857 // then zoom about a point
17858 zoom = params.level;
17859
17860 if (params.position != null) {
17861 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
17862 } else if (params.renderedPosition != null) {
17863 pos = params.renderedPosition;
17864 }
17865
17866 if (pos != null && !_p.panningEnabled) {
17867 // panning disabled
17868 bail = true;
17869 }
17870 } // crop zoom
17871
17872
17873 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
17874 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
17875
17876 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
17877 return null;
17878 }
17879
17880 if (pos != null) {
17881 // set zoom about position
17882 var pan1 = currentPan;
17883 var zoom1 = currentZoom;
17884 var zoom2 = zoom;
17885 var pan2 = {
17886 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
17887 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
17888 };
17889 return {
17890 zoomed: true,
17891 panned: true,
17892 zoom: zoom2,
17893 pan: pan2
17894 };
17895 } else {
17896 // just set the zoom
17897 return {
17898 zoomed: true,
17899 panned: false,
17900 zoom: zoom,
17901 pan: currentPan
17902 };
17903 }
17904 },
17905 zoom: function zoom(params) {
17906 if (params === undefined) {
17907 // get
17908 return this._private.zoom;
17909 } else {
17910 // set
17911 var vp = this.getZoomedViewport(params);
17912 var _p = this._private;
17913
17914 if (vp == null || !vp.zoomed) {
17915 return this;
17916 }
17917
17918 _p.zoom = vp.zoom;
17919
17920 if (vp.panned) {
17921 _p.pan.x = vp.pan.x;
17922 _p.pan.y = vp.pan.y;
17923 }
17924
17925 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
17926 this.notify('viewport');
17927 return this; // chaining
17928 }
17929 },
17930 viewport: function viewport(opts) {
17931 var _p = this._private;
17932 var zoomDefd = true;
17933 var panDefd = true;
17934 var events = []; // to trigger
17935
17936 var zoomFailed = false;
17937 var panFailed = false;
17938
17939 if (!opts) {
17940 return this;
17941 }
17942
17943 if (!number(opts.zoom)) {
17944 zoomDefd = false;
17945 }
17946
17947 if (!plainObject(opts.pan)) {
17948 panDefd = false;
17949 }
17950
17951 if (!zoomDefd && !panDefd) {
17952 return this;
17953 }
17954
17955 if (zoomDefd) {
17956 var z = opts.zoom;
17957
17958 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
17959 zoomFailed = true;
17960 } else {
17961 _p.zoom = z;
17962 events.push('zoom');
17963 }
17964 }
17965
17966 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
17967 var p = opts.pan;
17968
17969 if (number(p.x)) {
17970 _p.pan.x = p.x;
17971 panFailed = false;
17972 }
17973
17974 if (number(p.y)) {
17975 _p.pan.y = p.y;
17976 panFailed = false;
17977 }
17978
17979 if (!panFailed) {
17980 events.push('pan');
17981 }
17982 }
17983
17984 if (events.length > 0) {
17985 events.push('viewport');
17986 this.emit(events.join(' '));
17987 this.notify('viewport');
17988 }
17989
17990 return this; // chaining
17991 },
17992 center: function center(elements) {
17993 var pan = this.getCenterPan(elements);
17994
17995 if (pan) {
17996 this._private.pan = pan;
17997 this.emit('pan viewport');
17998 this.notify('viewport');
17999 }
18000
18001 return this; // chaining
18002 },
18003 getCenterPan: function getCenterPan(elements, zoom) {
18004 if (!this._private.panningEnabled) {
18005 return;
18006 }
18007
18008 if (string(elements)) {
18009 var selector = elements;
18010 elements = this.mutableElements().filter(selector);
18011 } else if (!elementOrCollection(elements)) {
18012 elements = this.mutableElements();
18013 }
18014
18015 if (elements.length === 0) {
18016 return;
18017 } // can't centre pan to nothing
18018
18019
18020 var bb = elements.boundingBox();
18021 var w = this.width();
18022 var h = this.height();
18023 zoom = zoom === undefined ? this._private.zoom : zoom;
18024 var pan = {
18025 // middle
18026 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18027 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18028 };
18029 return pan;
18030 },
18031 reset: function reset() {
18032 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18033 return this;
18034 }
18035
18036 this.viewport({
18037 pan: {
18038 x: 0,
18039 y: 0
18040 },
18041 zoom: 1
18042 });
18043 return this; // chaining
18044 },
18045 invalidateSize: function invalidateSize() {
18046 this._private.sizeCache = null;
18047 },
18048 size: function size() {
18049 var _p = this._private;
18050 var container = _p.container;
18051 return _p.sizeCache = _p.sizeCache || (container ? function () {
18052 var style = window$1.getComputedStyle(container);
18053
18054 var val = function val(name) {
18055 return parseFloat(style.getPropertyValue(name));
18056 };
18057
18058 return {
18059 width: container.clientWidth - val('padding-left') - val('padding-right'),
18060 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18061 };
18062 }() : {
18063 // fallback if no container (not 0 b/c can be used for dividing etc)
18064 width: 1,
18065 height: 1
18066 });
18067 },
18068 width: function width() {
18069 return this.size().width;
18070 },
18071 height: function height() {
18072 return this.size().height;
18073 },
18074 extent: function extent() {
18075 var pan = this._private.pan;
18076 var zoom = this._private.zoom;
18077 var rb = this.renderedExtent();
18078 var b = {
18079 x1: (rb.x1 - pan.x) / zoom,
18080 x2: (rb.x2 - pan.x) / zoom,
18081 y1: (rb.y1 - pan.y) / zoom,
18082 y2: (rb.y2 - pan.y) / zoom
18083 };
18084 b.w = b.x2 - b.x1;
18085 b.h = b.y2 - b.y1;
18086 return b;
18087 },
18088 renderedExtent: function renderedExtent() {
18089 var width = this.width();
18090 var height = this.height();
18091 return {
18092 x1: 0,
18093 y1: 0,
18094 x2: width,
18095 y2: height,
18096 w: width,
18097 h: height
18098 };
18099 }
18100}; // aliases
18101
18102corefn$8.centre = corefn$8.center; // backwards compatibility
18103
18104corefn$8.autolockNodes = corefn$8.autolock;
18105corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18106
18107var fn$6 = {
18108 data: define$3.data({
18109 field: 'data',
18110 bindingEvent: 'data',
18111 allowBinding: true,
18112 allowSetting: true,
18113 settingEvent: 'data',
18114 settingTriggersEvent: true,
18115 triggerFnName: 'trigger',
18116 allowGetting: true
18117 }),
18118 removeData: define$3.removeData({
18119 field: 'data',
18120 event: 'data',
18121 triggerFnName: 'trigger',
18122 triggerEvent: true
18123 }),
18124 scratch: define$3.data({
18125 field: 'scratch',
18126 bindingEvent: 'scratch',
18127 allowBinding: true,
18128 allowSetting: true,
18129 settingEvent: 'scratch',
18130 settingTriggersEvent: true,
18131 triggerFnName: 'trigger',
18132 allowGetting: true
18133 }),
18134 removeScratch: define$3.removeData({
18135 field: 'scratch',
18136 event: 'scratch',
18137 triggerFnName: 'trigger',
18138 triggerEvent: true
18139 })
18140}; // aliases
18141
18142fn$6.attr = fn$6.data;
18143fn$6.removeAttr = fn$6.removeData;
18144
18145var Core = function Core(opts) {
18146 var cy = this;
18147 opts = extend({}, opts);
18148 var container = opts.container; // allow for passing a wrapped jquery object
18149 // e.g. cytoscape({ container: $('#cy') })
18150
18151 if (container && !htmlElement(container) && htmlElement(container[0])) {
18152 container = container[0];
18153 }
18154
18155 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18156
18157 reg = reg || {};
18158
18159 if (reg && reg.cy) {
18160 reg.cy.destroy();
18161 reg = {}; // old instance => replace reg completely
18162 }
18163
18164 var readies = reg.readies = reg.readies || [];
18165
18166 if (container) {
18167 container._cyreg = reg;
18168 } // make sure container assoc'd reg points to this cy
18169
18170
18171 reg.cy = cy;
18172 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18173 var options = opts;
18174 options.layout = extend({
18175 name: head ? 'grid' : 'null'
18176 }, options.layout);
18177 options.renderer = extend({
18178 name: head ? 'canvas' : 'null'
18179 }, options.renderer);
18180
18181 var defVal = function defVal(def, val, altVal) {
18182 if (val !== undefined) {
18183 return val;
18184 } else if (altVal !== undefined) {
18185 return altVal;
18186 } else {
18187 return def;
18188 }
18189 };
18190
18191 var _p = this._private = {
18192 container: container,
18193 // html dom ele container
18194 ready: false,
18195 // whether ready has been triggered
18196 options: options,
18197 // cached options
18198 elements: new Collection(this),
18199 // elements in the graph
18200 listeners: [],
18201 // list of listeners
18202 aniEles: new Collection(this),
18203 // elements being animated
18204 data: {},
18205 // data for the core
18206 scratch: {},
18207 // scratch object for core
18208 layout: null,
18209 renderer: null,
18210 destroyed: false,
18211 // whether destroy was called
18212 notificationsEnabled: true,
18213 // whether notifications are sent to the renderer
18214 minZoom: 1e-50,
18215 maxZoom: 1e50,
18216 zoomingEnabled: defVal(true, options.zoomingEnabled),
18217 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18218 panningEnabled: defVal(true, options.panningEnabled),
18219 userPanningEnabled: defVal(true, options.userPanningEnabled),
18220 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18221 autolock: defVal(false, options.autolock, options.autolockNodes),
18222 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18223 autounselectify: defVal(false, options.autounselectify),
18224 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18225 zoom: number(options.zoom) ? options.zoom : 1,
18226 pan: {
18227 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18228 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18229 },
18230 animation: {
18231 // object for currently-running animations
18232 current: [],
18233 queue: []
18234 },
18235 hasCompoundNodes: false
18236 };
18237
18238 this.createEmitter(); // set selection type
18239
18240 this.selectionType(options.selectionType); // init zoom bounds
18241
18242 this.zoomRange({
18243 min: options.minZoom,
18244 max: options.maxZoom
18245 });
18246
18247 var loadExtData = function loadExtData(extData, next) {
18248 var anyIsPromise = extData.some(promise);
18249
18250 if (anyIsPromise) {
18251 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18252 } else {
18253 next(extData); // exec synchronously for convenience
18254 }
18255 }; // start with the default stylesheet so we have something before loading an external stylesheet
18256
18257
18258 if (_p.styleEnabled) {
18259 cy.setStyle([]);
18260 } // create the renderer
18261
18262
18263 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18264
18265 cy.initRenderer(rendererOptions);
18266
18267 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18268 cy.notifications(false); // remove old elements
18269
18270 var oldEles = cy.mutableElements();
18271
18272 if (oldEles.length > 0) {
18273 oldEles.remove();
18274 }
18275
18276 if (elements != null) {
18277 if (plainObject(elements) || array(elements)) {
18278 cy.add(elements);
18279 }
18280 }
18281
18282 cy.one('layoutready', function (e) {
18283 cy.notifications(true);
18284 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18285
18286 cy.one('load', onload);
18287 cy.emitAndNotify('load');
18288 }).one('layoutstop', function () {
18289 cy.one('done', ondone);
18290 cy.emit('done');
18291 });
18292 var layoutOpts = extend({}, cy._private.options.layout);
18293 layoutOpts.eles = cy.elements();
18294 cy.layout(layoutOpts).run();
18295 };
18296
18297 loadExtData([options.style, options.elements], function (thens) {
18298 var initStyle = thens[0];
18299 var initEles = thens[1]; // init style
18300
18301 if (_p.styleEnabled) {
18302 cy.style().append(initStyle);
18303 } // initial load
18304
18305
18306 setElesAndLayout(initEles, function () {
18307 // onready
18308 cy.startAnimationLoop();
18309 _p.ready = true; // if a ready callback is specified as an option, the bind it
18310
18311 if (fn(options.ready)) {
18312 cy.on('ready', options.ready);
18313 } // bind all the ready handlers registered before creating this instance
18314
18315
18316 for (var i = 0; i < readies.length; i++) {
18317 var fn$1 = readies[i];
18318 cy.on('ready', fn$1);
18319 }
18320
18321 if (reg) {
18322 reg.readies = [];
18323 } // 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
18324
18325
18326 cy.emit('ready');
18327 }, options.done);
18328 });
18329};
18330
18331var corefn$9 = Core.prototype; // short alias
18332
18333extend(corefn$9, {
18334 instanceString: function instanceString() {
18335 return 'core';
18336 },
18337 isReady: function isReady() {
18338 return this._private.ready;
18339 },
18340 destroyed: function destroyed() {
18341 return this._private.destroyed;
18342 },
18343 ready: function ready(fn) {
18344 if (this.isReady()) {
18345 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18346 } else {
18347 this.on('ready', fn);
18348 }
18349
18350 return this;
18351 },
18352 destroy: function destroy() {
18353 var cy = this;
18354 if (cy.destroyed()) return;
18355 cy.stopAnimationLoop();
18356 cy.destroyRenderer();
18357 this.emit('destroy');
18358 cy._private.destroyed = true;
18359 return cy;
18360 },
18361 hasElementWithId: function hasElementWithId(id) {
18362 return this._private.elements.hasElementWithId(id);
18363 },
18364 getElementById: function getElementById(id) {
18365 return this._private.elements.getElementById(id);
18366 },
18367 hasCompoundNodes: function hasCompoundNodes() {
18368 return this._private.hasCompoundNodes;
18369 },
18370 headless: function headless() {
18371 return this._private.renderer.isHeadless();
18372 },
18373 styleEnabled: function styleEnabled() {
18374 return this._private.styleEnabled;
18375 },
18376 addToPool: function addToPool(eles) {
18377 this._private.elements.merge(eles);
18378
18379 return this; // chaining
18380 },
18381 removeFromPool: function removeFromPool(eles) {
18382 this._private.elements.unmerge(eles);
18383
18384 return this;
18385 },
18386 container: function container() {
18387 return this._private.container || null;
18388 },
18389 mount: function mount(container) {
18390 if (container == null) {
18391 return;
18392 }
18393
18394 var cy = this;
18395 var _p = cy._private;
18396 var options = _p.options;
18397
18398 if (!htmlElement(container) && htmlElement(container[0])) {
18399 container = container[0];
18400 }
18401
18402 cy.stopAnimationLoop();
18403 cy.destroyRenderer();
18404 _p.container = container;
18405 _p.styleEnabled = true;
18406 cy.invalidateSize();
18407 cy.initRenderer(extend({}, options, options.renderer, {
18408 // allow custom renderer name to be re-used, otherwise use canvas
18409 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18410 }));
18411 cy.startAnimationLoop();
18412 cy.style(options.style);
18413 cy.emit('mount');
18414 return cy;
18415 },
18416 unmount: function unmount() {
18417 var cy = this;
18418 cy.stopAnimationLoop();
18419 cy.destroyRenderer();
18420 cy.initRenderer({
18421 name: 'null'
18422 });
18423 cy.emit('unmount');
18424 return cy;
18425 },
18426 options: function options() {
18427 return copy(this._private.options);
18428 },
18429 json: function json(obj) {
18430 var cy = this;
18431 var _p = cy._private;
18432 var eles = cy.mutableElements();
18433
18434 var getFreshRef = function getFreshRef(ele) {
18435 return cy.getElementById(ele.id());
18436 };
18437
18438 if (plainObject(obj)) {
18439 // set
18440 cy.startBatch();
18441
18442 if (obj.elements) {
18443 var idInJson = {};
18444
18445 var updateEles = function updateEles(jsons, gr) {
18446 var toAdd = [];
18447 var toMod = [];
18448
18449 for (var i = 0; i < jsons.length; i++) {
18450 var json = jsons[i];
18451 var id = '' + json.data.id; // id must be string
18452
18453 var ele = cy.getElementById(id);
18454 idInJson[id] = true;
18455
18456 if (ele.length !== 0) {
18457 // existing element should be updated
18458 toMod.push({
18459 ele: ele,
18460 json: json
18461 });
18462 } else {
18463 // otherwise should be added
18464 if (gr) {
18465 json.group = gr;
18466 toAdd.push(json);
18467 } else {
18468 toAdd.push(json);
18469 }
18470 }
18471 }
18472
18473 cy.add(toAdd);
18474
18475 for (var _i = 0; _i < toMod.length; _i++) {
18476 var _toMod$_i = toMod[_i],
18477 _ele = _toMod$_i.ele,
18478 _json = _toMod$_i.json;
18479
18480 _ele.json(_json);
18481 }
18482 };
18483
18484 if (array(obj.elements)) {
18485 // elements: []
18486 updateEles(obj.elements);
18487 } else {
18488 // elements: { nodes: [], edges: [] }
18489 var grs = ['nodes', 'edges'];
18490
18491 for (var i = 0; i < grs.length; i++) {
18492 var gr = grs[i];
18493 var elements = obj.elements[gr];
18494
18495 if (array(elements)) {
18496 updateEles(elements, gr);
18497 }
18498 }
18499 }
18500
18501 var parentsToRemove = cy.collection();
18502 eles.filter(function (ele) {
18503 return !idInJson[ele.id()];
18504 }).forEach(function (ele) {
18505 if (ele.isParent()) {
18506 parentsToRemove.merge(ele);
18507 } else {
18508 ele.remove();
18509 }
18510 }); // so that children are not removed w/parent
18511
18512 parentsToRemove.forEach(function (ele) {
18513 return ele.children().move({
18514 parent: null
18515 });
18516 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18517
18518 parentsToRemove.forEach(function (ele) {
18519 return getFreshRef(ele).remove();
18520 });
18521 }
18522
18523 if (obj.style) {
18524 cy.style(obj.style);
18525 }
18526
18527 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18528 cy.zoom(obj.zoom);
18529 }
18530
18531 if (obj.pan) {
18532 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18533 cy.pan(obj.pan);
18534 }
18535 }
18536
18537 if (obj.data) {
18538 cy.data(obj.data);
18539 }
18540
18541 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
18542
18543 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18544 var f = fields[_i2];
18545
18546 if (obj[f] != null) {
18547 cy[f](obj[f]);
18548 }
18549 }
18550
18551 cy.endBatch();
18552 return this; // chaining
18553 } else {
18554 // get
18555 var flat = !!obj;
18556 var json = {};
18557
18558 if (flat) {
18559 json.elements = this.elements().map(function (ele) {
18560 return ele.json();
18561 });
18562 } else {
18563 json.elements = {};
18564 eles.forEach(function (ele) {
18565 var group = ele.group();
18566
18567 if (!json.elements[group]) {
18568 json.elements[group] = [];
18569 }
18570
18571 json.elements[group].push(ele.json());
18572 });
18573 }
18574
18575 if (this._private.styleEnabled) {
18576 json.style = cy.style().json();
18577 }
18578
18579 json.data = copy(cy.data());
18580 var options = _p.options;
18581 json.zoomingEnabled = _p.zoomingEnabled;
18582 json.userZoomingEnabled = _p.userZoomingEnabled;
18583 json.zoom = _p.zoom;
18584 json.minZoom = _p.minZoom;
18585 json.maxZoom = _p.maxZoom;
18586 json.panningEnabled = _p.panningEnabled;
18587 json.userPanningEnabled = _p.userPanningEnabled;
18588 json.pan = copy(_p.pan);
18589 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18590 json.renderer = copy(options.renderer);
18591 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18592 json.textureOnViewport = options.textureOnViewport;
18593 json.wheelSensitivity = options.wheelSensitivity;
18594 json.motionBlur = options.motionBlur;
18595 return json;
18596 }
18597 }
18598});
18599corefn$9.$id = corefn$9.getElementById;
18600[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18601 extend(corefn$9, props);
18602});
18603
18604/* eslint-disable no-unused-vars */
18605
18606var defaults$9 = {
18607 fit: true,
18608 // whether to fit the viewport to the graph
18609 directed: false,
18610 // whether the tree is directed downwards (or edges can point in any direction if false)
18611 padding: 30,
18612 // padding on fit
18613 circle: false,
18614 // put depths in concentric circles if true, put depths top down if false
18615 grid: false,
18616 // whether to create an even grid into which the DAG is placed (circle:false only)
18617 spacingFactor: 1.75,
18618 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
18619 boundingBox: undefined,
18620 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
18621 avoidOverlap: true,
18622 // prevents node overlap, may overflow boundingBox if not enough space
18623 nodeDimensionsIncludeLabels: false,
18624 // Excludes the label when calculating node bounding boxes for the layout algorithm
18625 roots: undefined,
18626 // the roots of the trees
18627 maximal: false,
18628 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
18629 animate: false,
18630 // whether to transition the node positions
18631 animationDuration: 500,
18632 // duration of animation in ms if enabled
18633 animationEasing: undefined,
18634 // easing of animation if enabled,
18635 animateFilter: function animateFilter(node, i) {
18636 return true;
18637 },
18638 // 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
18639 ready: undefined,
18640 // callback on layoutready
18641 stop: undefined,
18642 // callback on layoutstop
18643 transform: function transform(node, position) {
18644 return position;
18645 } // transform a given node position. Useful for changing flow direction in discrete layouts
18646
18647};
18648/* eslint-enable */
18649
18650var getInfo = function getInfo(ele) {
18651 return ele.scratch('breadthfirst');
18652};
18653
18654var setInfo = function setInfo(ele, obj) {
18655 return ele.scratch('breadthfirst', obj);
18656};
18657
18658function BreadthFirstLayout(options) {
18659 this.options = extend({}, defaults$9, options);
18660}
18661
18662BreadthFirstLayout.prototype.run = function () {
18663 var params = this.options;
18664 var options = params;
18665 var cy = params.cy;
18666 var eles = options.eles;
18667 var nodes = eles.nodes().filter(function (n) {
18668 return !n.isParent();
18669 });
18670 var graph = eles;
18671 var directed = options.directed;
18672 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
18673
18674 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
18675 x1: 0,
18676 y1: 0,
18677 w: cy.width(),
18678 h: cy.height()
18679 });
18680 var roots;
18681
18682 if (elementOrCollection(options.roots)) {
18683 roots = options.roots;
18684 } else if (array(options.roots)) {
18685 var rootsArray = [];
18686
18687 for (var i = 0; i < options.roots.length; i++) {
18688 var id = options.roots[i];
18689 var ele = cy.getElementById(id);
18690 rootsArray.push(ele);
18691 }
18692
18693 roots = cy.collection(rootsArray);
18694 } else if (string(options.roots)) {
18695 roots = cy.$(options.roots);
18696 } else {
18697 if (directed) {
18698 roots = nodes.roots();
18699 } else {
18700 var components = eles.components();
18701 roots = cy.collection();
18702
18703 var _loop = function _loop(_i) {
18704 var comp = components[_i];
18705 var maxDegree = comp.maxDegree(false);
18706 var compRoots = comp.filter(function (ele) {
18707 return ele.degree(false) === maxDegree;
18708 });
18709 roots = roots.add(compRoots);
18710 };
18711
18712 for (var _i = 0; _i < components.length; _i++) {
18713 _loop(_i);
18714 }
18715 }
18716 }
18717
18718 var depths = [];
18719 var foundByBfs = {};
18720
18721 var addToDepth = function addToDepth(ele, d) {
18722 if (depths[d] == null) {
18723 depths[d] = [];
18724 }
18725
18726 var i = depths[d].length;
18727 depths[d].push(ele);
18728 setInfo(ele, {
18729 index: i,
18730 depth: d
18731 });
18732 };
18733
18734 var changeDepth = function changeDepth(ele, newDepth) {
18735 var _getInfo = getInfo(ele),
18736 depth = _getInfo.depth,
18737 index = _getInfo.index;
18738
18739 depths[depth][index] = null;
18740 addToDepth(ele, newDepth);
18741 }; // find the depths of the nodes
18742
18743
18744 graph.bfs({
18745 roots: roots,
18746 directed: options.directed,
18747 visit: function visit(node, edge, pNode, i, depth) {
18748 var ele = node[0];
18749 var id = ele.id();
18750 addToDepth(ele, depth);
18751 foundByBfs[id] = true;
18752 }
18753 }); // check for nodes not found by bfs
18754
18755 var orphanNodes = [];
18756
18757 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
18758 var _ele = nodes[_i2];
18759
18760 if (foundByBfs[_ele.id()]) {
18761 continue;
18762 } else {
18763 orphanNodes.push(_ele);
18764 }
18765 } // assign the nodes a depth and index
18766
18767
18768 var assignDepthsAt = function assignDepthsAt(i) {
18769 var eles = depths[i];
18770
18771 for (var j = 0; j < eles.length; j++) {
18772 var _ele2 = eles[j];
18773
18774 if (_ele2 == null) {
18775 eles.splice(j, 1);
18776 j--;
18777 continue;
18778 }
18779
18780 setInfo(_ele2, {
18781 depth: i,
18782 index: j
18783 });
18784 }
18785 };
18786
18787 var assignDepths = function assignDepths() {
18788 for (var _i3 = 0; _i3 < depths.length; _i3++) {
18789 assignDepthsAt(_i3);
18790 }
18791 };
18792
18793 var adjustMaximally = function adjustMaximally(ele, shifted) {
18794 var eInfo = getInfo(ele);
18795 var incomers = ele.incomers().filter(function (el) {
18796 return el.isNode() && eles.has(el);
18797 });
18798 var maxDepth = -1;
18799 var id = ele.id();
18800
18801 for (var k = 0; k < incomers.length; k++) {
18802 var incmr = incomers[k];
18803 var iInfo = getInfo(incmr);
18804 maxDepth = Math.max(maxDepth, iInfo.depth);
18805 }
18806
18807 if (eInfo.depth <= maxDepth) {
18808 if (shifted[id]) {
18809 return null;
18810 }
18811
18812 changeDepth(ele, maxDepth + 1);
18813 shifted[id] = true;
18814 return true;
18815 }
18816
18817 return false;
18818 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
18819
18820
18821 if (directed && maximal) {
18822 var Q = [];
18823 var shifted = {};
18824
18825 var enqueue = function enqueue(n) {
18826 return Q.push(n);
18827 };
18828
18829 var dequeue = function dequeue() {
18830 return Q.shift();
18831 };
18832
18833 nodes.forEach(function (n) {
18834 return Q.push(n);
18835 });
18836
18837 while (Q.length > 0) {
18838 var _ele3 = dequeue();
18839
18840 var didShift = adjustMaximally(_ele3, shifted);
18841
18842 if (didShift) {
18843 _ele3.outgoers().filter(function (el) {
18844 return el.isNode() && eles.has(el);
18845 }).forEach(enqueue);
18846 } else if (didShift === null) {
18847 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
18848 break; // exit on failure
18849 }
18850 }
18851 }
18852
18853 assignDepths(); // clear holes
18854 // find min distance we need to leave between nodes
18855
18856 var minDistance = 0;
18857
18858 if (options.avoidOverlap) {
18859 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
18860 var n = nodes[_i4];
18861 var nbb = n.layoutDimensions(options);
18862 var w = nbb.w;
18863 var h = nbb.h;
18864 minDistance = Math.max(minDistance, w, h);
18865 }
18866 } // get the weighted percent for an element based on its connectivity to other levels
18867
18868
18869 var cachedWeightedPercent = {};
18870
18871 var getWeightedPercent = function getWeightedPercent(ele) {
18872 if (cachedWeightedPercent[ele.id()]) {
18873 return cachedWeightedPercent[ele.id()];
18874 }
18875
18876 var eleDepth = getInfo(ele).depth;
18877 var neighbors = ele.neighborhood();
18878 var percent = 0;
18879 var samples = 0;
18880
18881 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
18882 var neighbor = neighbors[_i5];
18883
18884 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
18885 continue;
18886 }
18887
18888 var bf = getInfo(neighbor);
18889 var index = bf.index;
18890 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
18891
18892 if (index == null || depth == null) {
18893 continue;
18894 }
18895
18896 var nDepth = depths[depth].length;
18897
18898 if (depth < eleDepth) {
18899 // only get influenced by elements above
18900 percent += index / nDepth;
18901 samples++;
18902 }
18903 }
18904
18905 samples = Math.max(1, samples);
18906 percent = percent / samples;
18907
18908 if (samples === 0) {
18909 // put lone nodes at the start
18910 percent = 0;
18911 }
18912
18913 cachedWeightedPercent[ele.id()] = percent;
18914 return percent;
18915 }; // rearrange the indices in each depth level based on connectivity
18916
18917
18918 var sortFn = function sortFn(a, b) {
18919 var apct = getWeightedPercent(a);
18920 var bpct = getWeightedPercent(b);
18921 var diff = apct - bpct;
18922
18923 if (diff === 0) {
18924 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
18925 } else {
18926 return diff;
18927 }
18928 }; // sort each level to make connected nodes closer
18929
18930
18931 for (var _i6 = 0; _i6 < depths.length; _i6++) {
18932 depths[_i6].sort(sortFn);
18933
18934 assignDepthsAt(_i6);
18935 } // assign orphan nodes to a new top-level depth
18936
18937
18938 var orphanDepth = [];
18939
18940 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
18941 orphanDepth.push(orphanNodes[_i7]);
18942 }
18943
18944 depths.unshift(orphanDepth);
18945 assignDepths();
18946 var biggestDepthSize = 0;
18947
18948 for (var _i8 = 0; _i8 < depths.length; _i8++) {
18949 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
18950 }
18951
18952 var center = {
18953 x: bb.x1 + bb.w / 2,
18954 y: bb.x1 + bb.h / 2
18955 };
18956 var maxDepthSize = depths.reduce(function (max, eles) {
18957 return Math.max(max, eles.length);
18958 }, 0);
18959
18960 var getPosition = function getPosition(ele) {
18961 var _getInfo2 = getInfo(ele),
18962 depth = _getInfo2.depth,
18963 index = _getInfo2.index;
18964
18965 var depthSize = depths[depth].length;
18966 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
18967 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
18968 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
18969 radiusStepSize = Math.max(radiusStepSize, minDistance);
18970
18971 if (!options.circle) {
18972 var epos = {
18973 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
18974 y: (depth + 1) * distanceY
18975 };
18976 return epos;
18977 } else {
18978 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
18979 var theta = 2 * Math.PI / depths[depth].length * index;
18980
18981 if (depth === 0 && depths[0].length === 1) {
18982 radius = 1;
18983 }
18984
18985 return {
18986 x: center.x + radius * Math.cos(theta),
18987 y: center.y + radius * Math.sin(theta)
18988 };
18989 }
18990 };
18991
18992 nodes.layoutPositions(this, options, getPosition);
18993 return this; // chaining
18994};
18995
18996var defaults$a = {
18997 fit: true,
18998 // whether to fit the viewport to the graph
18999 padding: 30,
19000 // the padding on fit
19001 boundingBox: undefined,
19002 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19003 avoidOverlap: true,
19004 // prevents node overlap, may overflow boundingBox and radius if not enough space
19005 nodeDimensionsIncludeLabels: false,
19006 // Excludes the label when calculating node bounding boxes for the layout algorithm
19007 spacingFactor: undefined,
19008 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19009 radius: undefined,
19010 // the radius of the circle
19011 startAngle: 3 / 2 * Math.PI,
19012 // where nodes start in radians
19013 sweep: undefined,
19014 // how many radians should be between the first and last node (defaults to full circle)
19015 clockwise: true,
19016 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19017 sort: undefined,
19018 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19019 animate: false,
19020 // whether to transition the node positions
19021 animationDuration: 500,
19022 // duration of animation in ms if enabled
19023 animationEasing: undefined,
19024 // easing of animation if enabled
19025 animateFilter: function animateFilter(node, i) {
19026 return true;
19027 },
19028 // 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
19029 ready: undefined,
19030 // callback on layoutready
19031 stop: undefined,
19032 // callback on layoutstop
19033 transform: function transform(node, position) {
19034 return position;
19035 } // transform a given node position. Useful for changing flow direction in discrete layouts
19036
19037};
19038
19039function CircleLayout(options) {
19040 this.options = extend({}, defaults$a, options);
19041}
19042
19043CircleLayout.prototype.run = function () {
19044 var params = this.options;
19045 var options = params;
19046 var cy = params.cy;
19047 var eles = options.eles;
19048 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19049 var nodes = eles.nodes().not(':parent');
19050
19051 if (options.sort) {
19052 nodes = nodes.sort(options.sort);
19053 }
19054
19055 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19056 x1: 0,
19057 y1: 0,
19058 w: cy.width(),
19059 h: cy.height()
19060 });
19061 var center = {
19062 x: bb.x1 + bb.w / 2,
19063 y: bb.y1 + bb.h / 2
19064 };
19065 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19066 var dTheta = sweep / Math.max(1, nodes.length - 1);
19067 var r;
19068 var minDistance = 0;
19069
19070 for (var i = 0; i < nodes.length; i++) {
19071 var n = nodes[i];
19072 var nbb = n.layoutDimensions(options);
19073 var w = nbb.w;
19074 var h = nbb.h;
19075 minDistance = Math.max(minDistance, w, h);
19076 }
19077
19078 if (number(options.radius)) {
19079 r = options.radius;
19080 } else if (nodes.length <= 1) {
19081 r = 0;
19082 } else {
19083 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19084 } // calculate the radius
19085
19086
19087 if (nodes.length > 1 && options.avoidOverlap) {
19088 // but only if more than one node (can't overlap)
19089 minDistance *= 1.75; // just to have some nice spacing
19090
19091 var dcos = Math.cos(dTheta) - Math.cos(0);
19092 var dsin = Math.sin(dTheta) - Math.sin(0);
19093 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19094
19095 r = Math.max(rMin, r);
19096 }
19097
19098 var getPos = function getPos(ele, i) {
19099 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19100 var rx = r * Math.cos(theta);
19101 var ry = r * Math.sin(theta);
19102 var pos = {
19103 x: center.x + rx,
19104 y: center.y + ry
19105 };
19106 return pos;
19107 };
19108
19109 nodes.layoutPositions(this, options, getPos);
19110 return this; // chaining
19111};
19112
19113var defaults$b = {
19114 fit: true,
19115 // whether to fit the viewport to the graph
19116 padding: 30,
19117 // the padding on fit
19118 startAngle: 3 / 2 * Math.PI,
19119 // where nodes start in radians
19120 sweep: undefined,
19121 // how many radians should be between the first and last node (defaults to full circle)
19122 clockwise: true,
19123 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19124 equidistant: false,
19125 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19126 minNodeSpacing: 10,
19127 // min spacing between outside of nodes (used for radius adjustment)
19128 boundingBox: undefined,
19129 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19130 avoidOverlap: true,
19131 // prevents node overlap, may overflow boundingBox if not enough space
19132 nodeDimensionsIncludeLabels: false,
19133 // Excludes the label when calculating node bounding boxes for the layout algorithm
19134 height: undefined,
19135 // height of layout area (overrides container height)
19136 width: undefined,
19137 // width of layout area (overrides container width)
19138 spacingFactor: undefined,
19139 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19140 concentric: function concentric(node) {
19141 // returns numeric value for each node, placing higher nodes in levels towards the centre
19142 return node.degree();
19143 },
19144 levelWidth: function levelWidth(nodes) {
19145 // the letiation of concentric values in each level
19146 return nodes.maxDegree() / 4;
19147 },
19148 animate: false,
19149 // whether to transition the node positions
19150 animationDuration: 500,
19151 // duration of animation in ms if enabled
19152 animationEasing: undefined,
19153 // easing of animation if enabled
19154 animateFilter: function animateFilter(node, i) {
19155 return true;
19156 },
19157 // 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
19158 ready: undefined,
19159 // callback on layoutready
19160 stop: undefined,
19161 // callback on layoutstop
19162 transform: function transform(node, position) {
19163 return position;
19164 } // transform a given node position. Useful for changing flow direction in discrete layouts
19165
19166};
19167
19168function ConcentricLayout(options) {
19169 this.options = extend({}, defaults$b, options);
19170}
19171
19172ConcentricLayout.prototype.run = function () {
19173 var params = this.options;
19174 var options = params;
19175 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19176 var cy = params.cy;
19177 var eles = options.eles;
19178 var nodes = eles.nodes().not(':parent');
19179 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19180 x1: 0,
19181 y1: 0,
19182 w: cy.width(),
19183 h: cy.height()
19184 });
19185 var center = {
19186 x: bb.x1 + bb.w / 2,
19187 y: bb.y1 + bb.h / 2
19188 };
19189 var nodeValues = []; // { node, value }
19190
19191 var maxNodeSize = 0;
19192
19193 for (var i = 0; i < nodes.length; i++) {
19194 var node = nodes[i];
19195 var value = void 0; // calculate the node value
19196
19197 value = options.concentric(node);
19198 nodeValues.push({
19199 value: value,
19200 node: node
19201 }); // for style mapping
19202
19203 node._private.scratch.concentric = value;
19204 } // in case we used the `concentric` in style
19205
19206
19207 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19208
19209 for (var _i = 0; _i < nodes.length; _i++) {
19210 var _node = nodes[_i];
19211
19212 var nbb = _node.layoutDimensions(options);
19213
19214 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19215 } // sort node values in descreasing order
19216
19217
19218 nodeValues.sort(function (a, b) {
19219 return b.value - a.value;
19220 });
19221 var levelWidth = options.levelWidth(nodes); // put the values into levels
19222
19223 var levels = [[]];
19224 var currentLevel = levels[0];
19225
19226 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19227 var val = nodeValues[_i2];
19228
19229 if (currentLevel.length > 0) {
19230 var diff = Math.abs(currentLevel[0].value - val.value);
19231
19232 if (diff >= levelWidth) {
19233 currentLevel = [];
19234 levels.push(currentLevel);
19235 }
19236 }
19237
19238 currentLevel.push(val);
19239 } // create positions from levels
19240
19241
19242 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19243
19244 if (!options.avoidOverlap) {
19245 // then strictly constrain to bb
19246 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19247 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19248 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19249 minDist = Math.min(minDist, rStep);
19250 } // find the metrics for each level
19251
19252
19253 var r = 0;
19254
19255 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19256 var level = levels[_i3];
19257 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19258 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19259
19260 if (level.length > 1 && options.avoidOverlap) {
19261 // but only if more than one node (can't overlap)
19262 var dcos = Math.cos(dTheta) - Math.cos(0);
19263 var dsin = Math.sin(dTheta) - Math.sin(0);
19264 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19265
19266 r = Math.max(rMin, r);
19267 }
19268
19269 level.r = r;
19270 r += minDist;
19271 }
19272
19273 if (options.equidistant) {
19274 var rDeltaMax = 0;
19275 var _r = 0;
19276
19277 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19278 var _level = levels[_i4];
19279 var rDelta = _level.r - _r;
19280 rDeltaMax = Math.max(rDeltaMax, rDelta);
19281 }
19282
19283 _r = 0;
19284
19285 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19286 var _level2 = levels[_i5];
19287
19288 if (_i5 === 0) {
19289 _r = _level2.r;
19290 }
19291
19292 _level2.r = _r;
19293 _r += rDeltaMax;
19294 }
19295 } // calculate the node positions
19296
19297
19298 var pos = {}; // id => position
19299
19300 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19301 var _level3 = levels[_i6];
19302 var _dTheta = _level3.dTheta;
19303 var _r2 = _level3.r;
19304
19305 for (var j = 0; j < _level3.length; j++) {
19306 var _val = _level3[j];
19307 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19308 var p = {
19309 x: center.x + _r2 * Math.cos(theta),
19310 y: center.y + _r2 * Math.sin(theta)
19311 };
19312 pos[_val.node.id()] = p;
19313 }
19314 } // position the nodes
19315
19316
19317 nodes.layoutPositions(this, options, function (ele) {
19318 var id = ele.id();
19319 return pos[id];
19320 });
19321 return this; // chaining
19322};
19323
19324/*
19325The CoSE layout was written by Gerardo Huck.
19326https://www.linkedin.com/in/gerardohuck/
19327
19328Based on the following article:
19329http://dl.acm.org/citation.cfm?id=1498047
19330
19331Modifications tracked on Github.
19332*/
19333var DEBUG;
19334/**
19335 * @brief : default layout options
19336 */
19337
19338var defaults$c = {
19339 // Called on `layoutready`
19340 ready: function ready() {},
19341 // Called on `layoutstop`
19342 stop: function stop() {},
19343 // Whether to animate while running the layout
19344 // true : Animate continuously as the layout is running
19345 // false : Just show the end result
19346 // 'end' : Animate with the end result, from the initial positions to the end positions
19347 animate: true,
19348 // Easing of the animation for animate:'end'
19349 animationEasing: undefined,
19350 // The duration of the animation for animate:'end'
19351 animationDuration: undefined,
19352 // A function that determines whether the node should be animated
19353 // All nodes animated by default on animate enabled
19354 // Non-animated nodes are positioned immediately when the layout starts
19355 animateFilter: function animateFilter(node, i) {
19356 return true;
19357 },
19358 // The layout animates only after this many milliseconds for animate:true
19359 // (prevents flashing on fast runs)
19360 animationThreshold: 250,
19361 // Number of iterations between consecutive screen positions update
19362 refresh: 20,
19363 // Whether to fit the network view after when done
19364 fit: true,
19365 // Padding on fit
19366 padding: 30,
19367 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19368 boundingBox: undefined,
19369 // Excludes the label when calculating node bounding boxes for the layout algorithm
19370 nodeDimensionsIncludeLabels: false,
19371 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19372 randomize: false,
19373 // Extra spacing between components in non-compound graphs
19374 componentSpacing: 40,
19375 // Node repulsion (non overlapping) multiplier
19376 nodeRepulsion: function nodeRepulsion(node) {
19377 return 2048;
19378 },
19379 // Node repulsion (overlapping) multiplier
19380 nodeOverlap: 4,
19381 // Ideal edge (non nested) length
19382 idealEdgeLength: function idealEdgeLength(edge) {
19383 return 32;
19384 },
19385 // Divisor to compute edge forces
19386 edgeElasticity: function edgeElasticity(edge) {
19387 return 32;
19388 },
19389 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19390 nestingFactor: 1.2,
19391 // Gravity force (constant)
19392 gravity: 1,
19393 // Maximum number of iterations to perform
19394 numIter: 1000,
19395 // Initial temperature (maximum node displacement)
19396 initialTemp: 1000,
19397 // Cooling factor (how the temperature is reduced between consecutive iterations
19398 coolingFactor: 0.99,
19399 // Lower temperature threshold (below this point the layout will end)
19400 minTemp: 1.0
19401};
19402/**
19403 * @brief : constructor
19404 * @arg options : object containing layout options
19405 */
19406
19407function CoseLayout(options) {
19408 this.options = extend({}, defaults$c, options);
19409 this.options.layout = this;
19410}
19411/**
19412 * @brief : runs the layout
19413 */
19414
19415
19416CoseLayout.prototype.run = function () {
19417 var options = this.options;
19418 var cy = options.cy;
19419 var layout = this;
19420 layout.stopped = false;
19421
19422 if (options.animate === true || options.animate === false) {
19423 layout.emit({
19424 type: 'layoutstart',
19425 layout: layout
19426 });
19427 } // Set DEBUG - Global variable
19428
19429
19430 if (true === options.debug) {
19431 DEBUG = true;
19432 } else {
19433 DEBUG = false;
19434 } // Initialize layout info
19435
19436
19437 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19438
19439 if (DEBUG) {
19440 printLayoutInfo(layoutInfo);
19441 } // If required, randomize node positions
19442
19443
19444 if (options.randomize) {
19445 randomizePositions(layoutInfo);
19446 }
19447
19448 var startTime = performanceNow();
19449
19450 var refresh = function refresh() {
19451 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19452
19453 if (true === options.fit) {
19454 cy.fit(options.padding);
19455 }
19456 };
19457
19458 var mainLoop = function mainLoop(i) {
19459 if (layout.stopped || i >= options.numIter) {
19460 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19461 return false;
19462 } // Do one step in the phisical simulation
19463
19464
19465 step$1(layoutInfo, options); // Update temperature
19466
19467 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19468
19469 if (layoutInfo.temperature < options.minTemp) {
19470 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19471 return false;
19472 }
19473
19474 return true;
19475 };
19476
19477 var done = function done() {
19478 if (options.animate === true || options.animate === false) {
19479 refresh(); // Layout has finished
19480
19481 layout.one('layoutstop', options.stop);
19482 layout.emit({
19483 type: 'layoutstop',
19484 layout: layout
19485 });
19486 } else {
19487 var nodes = options.eles.nodes();
19488 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19489 nodes.layoutPositions(layout, options, getScaledPos);
19490 }
19491 };
19492
19493 var i = 0;
19494 var loopRet = true;
19495
19496 if (options.animate === true) {
19497 var frame = function frame() {
19498 var f = 0;
19499
19500 while (loopRet && f < options.refresh) {
19501 loopRet = mainLoop(i);
19502 i++;
19503 f++;
19504 }
19505
19506 if (!loopRet) {
19507 // it's done
19508 separateComponents(layoutInfo, options);
19509 done();
19510 } else {
19511 var now = performanceNow();
19512
19513 if (now - startTime >= options.animationThreshold) {
19514 refresh();
19515 }
19516
19517 requestAnimationFrame(frame);
19518 }
19519 };
19520
19521 frame();
19522 } else {
19523 while (loopRet) {
19524 loopRet = mainLoop(i);
19525 i++;
19526 }
19527
19528 separateComponents(layoutInfo, options);
19529 done();
19530 }
19531
19532 return this; // chaining
19533};
19534/**
19535 * @brief : called on continuous layouts to stop them before they finish
19536 */
19537
19538
19539CoseLayout.prototype.stop = function () {
19540 this.stopped = true;
19541
19542 if (this.thread) {
19543 this.thread.stop();
19544 }
19545
19546 this.emit('layoutstop');
19547 return this; // chaining
19548};
19549
19550CoseLayout.prototype.destroy = function () {
19551 if (this.thread) {
19552 this.thread.stop();
19553 }
19554
19555 return this; // chaining
19556};
19557/**
19558 * @brief : Creates an object which is contains all the data
19559 * used in the layout process
19560 * @arg cy : cytoscape.js object
19561 * @return : layoutInfo object initialized
19562 */
19563
19564
19565var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19566 // Shortcut
19567 var edges = options.eles.edges();
19568 var nodes = options.eles.nodes();
19569 var layoutInfo = {
19570 isCompound: cy.hasCompoundNodes(),
19571 layoutNodes: [],
19572 idToIndex: {},
19573 nodeSize: nodes.size(),
19574 graphSet: [],
19575 indexToGraph: [],
19576 layoutEdges: [],
19577 edgeSize: edges.size(),
19578 temperature: options.initialTemp,
19579 clientWidth: cy.width(),
19580 clientHeight: cy.width(),
19581 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19582 x1: 0,
19583 y1: 0,
19584 w: cy.width(),
19585 h: cy.height()
19586 })
19587 };
19588 var components = options.eles.components();
19589 var id2cmptId = {};
19590
19591 for (var i = 0; i < components.length; i++) {
19592 var component = components[i];
19593
19594 for (var j = 0; j < component.length; j++) {
19595 var node = component[j];
19596 id2cmptId[node.id()] = i;
19597 }
19598 } // Iterate over all nodes, creating layout nodes
19599
19600
19601 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19602 var n = nodes[i];
19603 var nbb = n.layoutDimensions(options);
19604 var tempNode = {};
19605 tempNode.isLocked = n.locked();
19606 tempNode.id = n.data('id');
19607 tempNode.parentId = n.data('parent');
19608 tempNode.cmptId = id2cmptId[n.id()];
19609 tempNode.children = [];
19610 tempNode.positionX = n.position('x');
19611 tempNode.positionY = n.position('y');
19612 tempNode.offsetX = 0;
19613 tempNode.offsetY = 0;
19614 tempNode.height = nbb.w;
19615 tempNode.width = nbb.h;
19616 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
19617 tempNode.minX = tempNode.positionX - tempNode.width / 2;
19618 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
19619 tempNode.minY = tempNode.positionY - tempNode.height / 2;
19620 tempNode.padLeft = parseFloat(n.style('padding'));
19621 tempNode.padRight = parseFloat(n.style('padding'));
19622 tempNode.padTop = parseFloat(n.style('padding'));
19623 tempNode.padBottom = parseFloat(n.style('padding')); // forces
19624
19625 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
19626
19627 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
19628
19629 layoutInfo.idToIndex[tempNode.id] = i;
19630 } // Inline implementation of a queue, used for traversing the graph in BFS order
19631
19632
19633 var queue = [];
19634 var start = 0; // Points to the start the queue
19635
19636 var end = -1; // Points to the end of the queue
19637
19638 var tempGraph = []; // Second pass to add child information and
19639 // initialize queue for hierarchical traversal
19640
19641 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19642 var n = layoutInfo.layoutNodes[i];
19643 var p_id = n.parentId; // Check if node n has a parent node
19644
19645 if (null != p_id) {
19646 // Add node Id to parent's list of children
19647 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
19648 } else {
19649 // If a node doesn't have a parent, then it's in the root graph
19650 queue[++end] = n.id;
19651 tempGraph.push(n.id);
19652 }
19653 } // Add root graph to graphSet
19654
19655
19656 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
19657
19658 while (start <= end) {
19659 // Get the node to visit and remove it from queue
19660 var node_id = queue[start++];
19661 var node_ix = layoutInfo.idToIndex[node_id];
19662 var node = layoutInfo.layoutNodes[node_ix];
19663 var children = node.children;
19664
19665 if (children.length > 0) {
19666 // Add children nodes as a new graph to graph set
19667 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
19668
19669 for (var i = 0; i < children.length; i++) {
19670 queue[++end] = children[i];
19671 }
19672 }
19673 } // Create indexToGraph map
19674
19675
19676 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19677 var graph = layoutInfo.graphSet[i];
19678
19679 for (var j = 0; j < graph.length; j++) {
19680 var index = layoutInfo.idToIndex[graph[j]];
19681 layoutInfo.indexToGraph[index] = i;
19682 }
19683 } // Iterate over all edges, creating Layout Edges
19684
19685
19686 for (var i = 0; i < layoutInfo.edgeSize; i++) {
19687 var e = edges[i];
19688 var tempEdge = {};
19689 tempEdge.id = e.data('id');
19690 tempEdge.sourceId = e.data('source');
19691 tempEdge.targetId = e.data('target'); // Compute ideal length
19692
19693 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
19694 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
19695
19696 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
19697 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
19698 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
19699 var targetGraph = layoutInfo.indexToGraph[targetIx];
19700
19701 if (sourceGraph != targetGraph) {
19702 // Find lowest common graph ancestor
19703 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
19704
19705 var lcaGraph = layoutInfo.graphSet[lca];
19706 var depth = 0; // Source depth
19707
19708 var tempNode = layoutInfo.layoutNodes[sourceIx];
19709
19710 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19711 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19712 depth++;
19713 } // Target depth
19714
19715
19716 tempNode = layoutInfo.layoutNodes[targetIx];
19717
19718 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19719 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19720 depth++;
19721 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
19722 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
19723 // ". Depth: " + depth);
19724 // Update idealLength
19725
19726
19727 idealLength *= depth * options.nestingFactor;
19728 }
19729
19730 tempEdge.idealLength = idealLength;
19731 tempEdge.elasticity = elasticity;
19732 layoutInfo.layoutEdges.push(tempEdge);
19733 } // Finally, return layoutInfo object
19734
19735
19736 return layoutInfo;
19737};
19738/**
19739 * @brief : This function finds the index of the lowest common
19740 * graph ancestor between 2 nodes in the subtree
19741 * (from the graph hierarchy induced tree) whose
19742 * root is graphIx
19743 *
19744 * @arg node1: node1's ID
19745 * @arg node2: node2's ID
19746 * @arg layoutInfo: layoutInfo object
19747 *
19748 */
19749
19750
19751var findLCA = function findLCA(node1, node2, layoutInfo) {
19752 // Find their common ancester, starting from the root graph
19753 var res = findLCA_aux(node1, node2, 0, layoutInfo);
19754
19755 if (2 > res.count) {
19756 // If aux function couldn't find the common ancester,
19757 // then it is the root graph
19758 return 0;
19759 } else {
19760 return res.graph;
19761 }
19762};
19763/**
19764 * @brief : Auxiliary function used for LCA computation
19765 *
19766 * @arg node1 : node1's ID
19767 * @arg node2 : node2's ID
19768 * @arg graphIx : subgraph index
19769 * @arg layoutInfo : layoutInfo object
19770 *
19771 * @return : object of the form {count: X, graph: Y}, where:
19772 * X is the number of ancesters (max: 2) found in
19773 * graphIx (and it's subgraphs),
19774 * Y is the graph index of the lowest graph containing
19775 * all X nodes
19776 */
19777
19778
19779var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
19780 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
19781
19782 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
19783 return {
19784 count: 2,
19785 graph: graphIx
19786 };
19787 } // Make recursive calls for all subgraphs
19788
19789
19790 var c = 0;
19791
19792 for (var i = 0; i < graph.length; i++) {
19793 var nodeId = graph[i];
19794 var nodeIx = layoutInfo.idToIndex[nodeId];
19795 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
19796
19797 if (0 === children.length) {
19798 continue;
19799 }
19800
19801 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
19802 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
19803
19804 if (0 === result.count) {
19805 // Neither node1 nor node2 are present in this subgraph
19806 continue;
19807 } else if (1 === result.count) {
19808 // One of (node1, node2) is present in this subgraph
19809 c++;
19810
19811 if (2 === c) {
19812 // We've already found both nodes, no need to keep searching
19813 break;
19814 }
19815 } else {
19816 // Both nodes are present in this subgraph
19817 return result;
19818 }
19819 }
19820
19821 return {
19822 count: c,
19823 graph: graphIx
19824 };
19825};
19826/**
19827 * @brief: printsLayoutInfo into js console
19828 * Only used for debbuging
19829 */
19830
19831
19832if (false) {
19833 var printLayoutInfo;
19834}
19835/**
19836 * @brief : Randomizes the position of all nodes
19837 */
19838
19839
19840var randomizePositions = function randomizePositions(layoutInfo, cy) {
19841 var width = layoutInfo.clientWidth;
19842 var height = layoutInfo.clientHeight;
19843
19844 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19845 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
19846
19847 if (0 === n.children.length && !n.isLocked) {
19848 n.positionX = Math.random() * width;
19849 n.positionY = Math.random() * height;
19850 }
19851 }
19852};
19853
19854var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
19855 var bb = layoutInfo.boundingBox;
19856 var coseBB = {
19857 x1: Infinity,
19858 x2: -Infinity,
19859 y1: Infinity,
19860 y2: -Infinity
19861 };
19862
19863 if (options.boundingBox) {
19864 nodes.forEach(function (node) {
19865 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
19866 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
19867 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
19868 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
19869 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
19870 });
19871 coseBB.w = coseBB.x2 - coseBB.x1;
19872 coseBB.h = coseBB.y2 - coseBB.y1;
19873 }
19874
19875 return function (ele, i) {
19876 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
19877
19878 if (options.boundingBox) {
19879 // then add extra bounding box constraint
19880 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
19881 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
19882 return {
19883 x: bb.x1 + pctX * bb.w,
19884 y: bb.y1 + pctY * bb.h
19885 };
19886 } else {
19887 return {
19888 x: lnode.positionX,
19889 y: lnode.positionY
19890 };
19891 }
19892 };
19893};
19894/**
19895 * @brief : Updates the positions of nodes in the network
19896 * @arg layoutInfo : LayoutInfo object
19897 * @arg cy : Cytoscape object
19898 * @arg options : Layout options
19899 */
19900
19901
19902var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
19903 // var s = 'Refreshing positions';
19904 // logDebug(s);
19905 var layout = options.layout;
19906 var nodes = options.eles.nodes();
19907 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19908 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
19909
19910 if (true !== layoutInfo.ready) {
19911 // s = 'Triggering layoutready';
19912 // logDebug(s);
19913 layoutInfo.ready = true;
19914 layout.one('layoutready', options.ready);
19915 layout.emit({
19916 type: 'layoutready',
19917 layout: this
19918 });
19919 }
19920};
19921/**
19922 * @brief : Logs a debug message in JS console, if DEBUG is ON
19923 */
19924// var logDebug = function(text) {
19925// if (DEBUG) {
19926// console.debug(text);
19927// }
19928// };
19929
19930/**
19931 * @brief : Performs one iteration of the physical simulation
19932 * @arg layoutInfo : LayoutInfo object already initialized
19933 * @arg cy : Cytoscape object
19934 * @arg options : Layout options
19935 */
19936
19937
19938var step$1 = function step(layoutInfo, options, _step) {
19939 // var s = "\n\n###############################";
19940 // s += "\nSTEP: " + step;
19941 // s += "\n###############################\n";
19942 // logDebug(s);
19943 // Calculate node repulsions
19944 calculateNodeForces(layoutInfo, options); // Calculate edge forces
19945
19946 calculateEdgeForces(layoutInfo); // Calculate gravity forces
19947
19948 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
19949
19950 propagateForces(layoutInfo); // Update positions based on calculated forces
19951
19952 updatePositions(layoutInfo);
19953};
19954/**
19955 * @brief : Computes the node repulsion forces
19956 */
19957
19958
19959var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
19960 // Go through each of the graphs in graphSet
19961 // Nodes only repel each other if they belong to the same graph
19962 // var s = 'calculateNodeForces';
19963 // logDebug(s);
19964 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19965 var graph = layoutInfo.graphSet[i];
19966 var numNodes = graph.length; // s = "Set: " + graph.toString();
19967 // logDebug(s);
19968 // Now get all the pairs of nodes
19969 // Only get each pair once, (A, B) = (B, A)
19970
19971 for (var j = 0; j < numNodes; j++) {
19972 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
19973
19974 for (var k = j + 1; k < numNodes; k++) {
19975 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
19976 nodeRepulsion(node1, node2, layoutInfo, options);
19977 }
19978 }
19979 }
19980};
19981
19982var randomDistance = function randomDistance(max) {
19983 return -max + 2 * max * Math.random();
19984};
19985/**
19986 * @brief : Compute the node repulsion forces between a pair of nodes
19987 */
19988
19989
19990var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
19991 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
19992 var cmptId1 = node1.cmptId;
19993 var cmptId2 = node2.cmptId;
19994
19995 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
19996 return;
19997 } // Get direction of line connecting both node centers
19998
19999
20000 var directionX = node2.positionX - node1.positionX;
20001 var directionY = node2.positionY - node1.positionY;
20002 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20003 // If both centers are the same, apply a random force
20004
20005 if (0 === directionX && 0 === directionY) {
20006 directionX = randomDistance(maxRandDist);
20007 directionY = randomDistance(maxRandDist);
20008 }
20009
20010 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20011
20012 if (overlap > 0) {
20013 // s += "\nNodes DO overlap.";
20014 // s += "\nOverlap: " + overlap;
20015 // If nodes overlap, repulsion force is proportional
20016 // to the overlap
20017 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20018
20019 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20020
20021 var forceX = force * directionX / distance;
20022 var forceY = force * directionY / distance;
20023 } else {
20024 // s += "\nNodes do NOT overlap.";
20025 // If there's no overlap, force is inversely proportional
20026 // to squared distance
20027 // Get clipping points for both nodes
20028 var point1 = findClippingPoint(node1, directionX, directionY);
20029 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20030
20031 var distanceX = point2.x - point1.x;
20032 var distanceY = point2.y - point1.y;
20033 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20034 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20035 // Compute the module and components of the force vector
20036
20037 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20038 var forceX = force * distanceX / distance;
20039 var forceY = force * distanceY / distance;
20040 } // Apply force
20041
20042
20043 if (!node1.isLocked) {
20044 node1.offsetX -= forceX;
20045 node1.offsetY -= forceY;
20046 }
20047
20048 if (!node2.isLocked) {
20049 node2.offsetX += forceX;
20050 node2.offsetY += forceY;
20051 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20052 // logDebug(s);
20053
20054
20055 return;
20056};
20057/**
20058 * @brief : Determines whether two nodes overlap or not
20059 * @return : Amount of overlapping (0 => no overlap)
20060 */
20061
20062
20063var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20064 if (dX > 0) {
20065 var overlapX = node1.maxX - node2.minX;
20066 } else {
20067 var overlapX = node2.maxX - node1.minX;
20068 }
20069
20070 if (dY > 0) {
20071 var overlapY = node1.maxY - node2.minY;
20072 } else {
20073 var overlapY = node2.maxY - node1.minY;
20074 }
20075
20076 if (overlapX >= 0 && overlapY >= 0) {
20077 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20078 } else {
20079 return 0;
20080 }
20081};
20082/**
20083 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20084 * the rectangular bounding box of it's source/target node
20085 */
20086
20087
20088var findClippingPoint = function findClippingPoint(node, dX, dY) {
20089 // Shorcuts
20090 var X = node.positionX;
20091 var Y = node.positionY;
20092 var H = node.height || 1;
20093 var W = node.width || 1;
20094 var dirSlope = dY / dX;
20095 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20096 // " . Height: " + H + ", Width: " + W +
20097 // "\nDirection " + dX + ", " + dY;
20098 //
20099 // Compute intersection
20100
20101 var res = {}; // Case: Vertical direction (up)
20102
20103 if (0 === dX && 0 < dY) {
20104 res.x = X; // s += "\nUp direction";
20105
20106 res.y = Y + H / 2;
20107 return res;
20108 } // Case: Vertical direction (down)
20109
20110
20111 if (0 === dX && 0 > dY) {
20112 res.x = X;
20113 res.y = Y + H / 2; // s += "\nDown direction";
20114
20115 return res;
20116 } // Case: Intersects the right border
20117
20118
20119 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20120 res.x = X + W / 2;
20121 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20122
20123 return res;
20124 } // Case: Intersects the left border
20125
20126
20127 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20128 res.x = X - W / 2;
20129 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20130
20131 return res;
20132 } // Case: Intersects the top border
20133
20134
20135 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20136 res.x = X + H * dX / 2 / dY;
20137 res.y = Y + H / 2; // s += "\nTop border";
20138
20139 return res;
20140 } // Case: Intersects the bottom border
20141
20142
20143 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20144 res.x = X - H * dX / 2 / dY;
20145 res.y = Y - H / 2; // s += "\nBottom border";
20146
20147 return res;
20148 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20149 // logDebug(s);
20150
20151
20152 return res;
20153};
20154/**
20155 * @brief : Calculates all edge forces
20156 */
20157
20158
20159var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20160 // Iterate over all edges
20161 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20162 // Get edge, source & target nodes
20163 var edge = layoutInfo.layoutEdges[i];
20164 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20165 var source = layoutInfo.layoutNodes[sourceIx];
20166 var targetIx = layoutInfo.idToIndex[edge.targetId];
20167 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20168
20169 var directionX = target.positionX - source.positionX;
20170 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20171 // A random force has already been applied as node repulsion
20172
20173 if (0 === directionX && 0 === directionY) {
20174 continue;
20175 } // Get clipping points for both nodes
20176
20177
20178 var point1 = findClippingPoint(source, directionX, directionY);
20179 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20180 var lx = point2.x - point1.x;
20181 var ly = point2.y - point1.y;
20182 var l = Math.sqrt(lx * lx + ly * ly);
20183 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20184
20185 if (0 !== l) {
20186 var forceX = force * lx / l;
20187 var forceY = force * ly / l;
20188 } else {
20189 var forceX = 0;
20190 var forceY = 0;
20191 } // Add this force to target and source nodes
20192
20193
20194 if (!source.isLocked) {
20195 source.offsetX += forceX;
20196 source.offsetY += forceY;
20197 }
20198
20199 if (!target.isLocked) {
20200 target.offsetX -= forceX;
20201 target.offsetY -= forceY;
20202 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20203 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20204 // logDebug(s);
20205
20206 }
20207};
20208/**
20209 * @brief : Computes gravity forces for all nodes
20210 */
20211
20212
20213var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20214 var distThreshold = 1; // var s = 'calculateGravityForces';
20215 // logDebug(s);
20216
20217 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20218 var graph = layoutInfo.graphSet[i];
20219 var numNodes = graph.length; // s = "Set: " + graph.toString();
20220 // logDebug(s);
20221 // Compute graph center
20222
20223 if (0 === i) {
20224 var centerX = layoutInfo.clientHeight / 2;
20225 var centerY = layoutInfo.clientWidth / 2;
20226 } else {
20227 // Get Parent node for this graph, and use its position as center
20228 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20229 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20230 var centerX = parent.positionX;
20231 var centerY = parent.positionY;
20232 } // s = "Center found at: " + centerX + ", " + centerY;
20233 // logDebug(s);
20234 // Apply force to all nodes in graph
20235
20236
20237 for (var j = 0; j < numNodes; j++) {
20238 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20239
20240 if (node.isLocked) {
20241 continue;
20242 }
20243
20244 var dx = centerX - node.positionX;
20245 var dy = centerY - node.positionY;
20246 var d = Math.sqrt(dx * dx + dy * dy);
20247
20248 if (d > distThreshold) {
20249 var fx = options.gravity * dx / d;
20250 var fy = options.gravity * dy / d;
20251 node.offsetX += fx;
20252 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20253 } // s += ": skypped since it's too close to center";
20254 // logDebug(s);
20255
20256 }
20257 }
20258};
20259/**
20260 * @brief : This function propagates the existing offsets from
20261 * parent nodes to its descendents.
20262 * @arg layoutInfo : layoutInfo Object
20263 * @arg cy : cytoscape Object
20264 * @arg options : Layout options
20265 */
20266
20267
20268var propagateForces = function propagateForces(layoutInfo, options) {
20269 // Inline implementation of a queue, used for traversing the graph in BFS order
20270 var queue = [];
20271 var start = 0; // Points to the start the queue
20272
20273 var end = -1; // Points to the end of the queue
20274 // logDebug('propagateForces');
20275 // Start by visiting the nodes in the root graph
20276
20277 queue.push.apply(queue, layoutInfo.graphSet[0]);
20278 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20279
20280 while (start <= end) {
20281 // Get the node to visit and remove it from queue
20282 var nodeId = queue[start++];
20283 var nodeIndex = layoutInfo.idToIndex[nodeId];
20284 var node = layoutInfo.layoutNodes[nodeIndex];
20285 var children = node.children; // We only need to process the node if it's compound
20286
20287 if (0 < children.length && !node.isLocked) {
20288 var offX = node.offsetX;
20289 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20290 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20291 // s += "\n Children: " + children.toString();
20292 // logDebug(s);
20293
20294 for (var i = 0; i < children.length; i++) {
20295 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20296
20297 childNode.offsetX += offX;
20298 childNode.offsetY += offY; // Add children to queue to be visited
20299
20300 queue[++end] = children[i];
20301 } // Reset parent offsets
20302
20303
20304 node.offsetX = 0;
20305 node.offsetY = 0;
20306 }
20307 }
20308};
20309/**
20310 * @brief : Updates the layout model positions, based on
20311 * the accumulated forces
20312 */
20313
20314
20315var updatePositions = function updatePositions(layoutInfo, options) {
20316 // var s = 'Updating positions';
20317 // logDebug(s);
20318 // Reset boundaries for compound nodes
20319 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20320 var n = layoutInfo.layoutNodes[i];
20321
20322 if (0 < n.children.length) {
20323 // logDebug("Resetting boundaries of compound node: " + n.id);
20324 n.maxX = undefined;
20325 n.minX = undefined;
20326 n.maxY = undefined;
20327 n.minY = undefined;
20328 }
20329 }
20330
20331 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20332 var n = layoutInfo.layoutNodes[i];
20333
20334 if (0 < n.children.length || n.isLocked) {
20335 // No need to set compound or locked node position
20336 // logDebug("Skipping position update of node: " + n.id);
20337 continue;
20338 } // s = "Node: " + n.id + " Previous position: (" +
20339 // n.positionX + ", " + n.positionY + ").";
20340 // Limit displacement in order to improve stability
20341
20342
20343 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20344 n.positionX += tempForce.x;
20345 n.positionY += tempForce.y;
20346 n.offsetX = 0;
20347 n.offsetY = 0;
20348 n.minX = n.positionX - n.width;
20349 n.maxX = n.positionX + n.width;
20350 n.minY = n.positionY - n.height;
20351 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20352 // logDebug(s);
20353 // Update ancestry boudaries
20354
20355 updateAncestryBoundaries(n, layoutInfo);
20356 } // Update size, position of compund nodes
20357
20358
20359 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20360 var n = layoutInfo.layoutNodes[i];
20361
20362 if (0 < n.children.length && !n.isLocked) {
20363 n.positionX = (n.maxX + n.minX) / 2;
20364 n.positionY = (n.maxY + n.minY) / 2;
20365 n.width = n.maxX - n.minX;
20366 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20367 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20368 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20369 // logDebug(s);
20370 }
20371 }
20372};
20373/**
20374 * @brief : Limits a force (forceX, forceY) to be not
20375 * greater (in modulo) than max.
20376 8 Preserves force direction.
20377 */
20378
20379
20380var limitForce = function limitForce(forceX, forceY, max) {
20381 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20382 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20383
20384 if (force > max) {
20385 var res = {
20386 x: max * forceX / force,
20387 y: max * forceY / force
20388 };
20389 } else {
20390 var res = {
20391 x: forceX,
20392 y: forceY
20393 };
20394 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20395 // logDebug(s);
20396
20397
20398 return res;
20399};
20400/**
20401 * @brief : Function used for keeping track of compound node
20402 * sizes, since they should bound all their subnodes.
20403 */
20404
20405
20406var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20407 // var s = "Propagating new position/size of node " + node.id;
20408 var parentId = node.parentId;
20409
20410 if (null == parentId) {
20411 // If there's no parent, we are done
20412 // s += ". No parent node.";
20413 // logDebug(s);
20414 return;
20415 } // Get Parent Node
20416
20417
20418 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20419 var flag = false; // MaxX
20420
20421 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20422 p.maxX = node.maxX + p.padRight;
20423 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20424 } // MinX
20425
20426
20427 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20428 p.minX = node.minX - p.padLeft;
20429 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20430 } // MaxY
20431
20432
20433 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20434 p.maxY = node.maxY + p.padBottom;
20435 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20436 } // MinY
20437
20438
20439 if (null == p.minY || node.minY - p.padTop < p.minY) {
20440 p.minY = node.minY - p.padTop;
20441 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20442 } // If updated boundaries, propagate changes upward
20443
20444
20445 if (flag) {
20446 // logDebug(s);
20447 return updateAncestryBoundaries(p, layoutInfo);
20448 } // s += ". No changes in boundaries/position of parent node " + p.id;
20449 // logDebug(s);
20450
20451
20452 return;
20453};
20454
20455var separateComponents = function separateComponents(layoutInfo, options) {
20456 var nodes = layoutInfo.layoutNodes;
20457 var components = [];
20458
20459 for (var i = 0; i < nodes.length; i++) {
20460 var node = nodes[i];
20461 var cid = node.cmptId;
20462 var component = components[cid] = components[cid] || [];
20463 component.push(node);
20464 }
20465
20466 var totalA = 0;
20467
20468 for (var i = 0; i < components.length; i++) {
20469 var c = components[i];
20470
20471 if (!c) {
20472 continue;
20473 }
20474
20475 c.x1 = Infinity;
20476 c.x2 = -Infinity;
20477 c.y1 = Infinity;
20478 c.y2 = -Infinity;
20479
20480 for (var j = 0; j < c.length; j++) {
20481 var n = c[j];
20482 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20483 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20484 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20485 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20486 }
20487
20488 c.w = c.x2 - c.x1;
20489 c.h = c.y2 - c.y1;
20490 totalA += c.w * c.h;
20491 }
20492
20493 components.sort(function (c1, c2) {
20494 return c2.w * c2.h - c1.w * c1.h;
20495 });
20496 var x = 0;
20497 var y = 0;
20498 var usedW = 0;
20499 var rowH = 0;
20500 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20501
20502 for (var i = 0; i < components.length; i++) {
20503 var c = components[i];
20504
20505 if (!c) {
20506 continue;
20507 }
20508
20509 for (var j = 0; j < c.length; j++) {
20510 var n = c[j];
20511
20512 if (!n.isLocked) {
20513 n.positionX += x - c.x1;
20514 n.positionY += y - c.y1;
20515 }
20516 }
20517
20518 x += c.w + options.componentSpacing;
20519 usedW += c.w + options.componentSpacing;
20520 rowH = Math.max(rowH, c.h);
20521
20522 if (usedW > maxRowW) {
20523 y += rowH + options.componentSpacing;
20524 x = 0;
20525 usedW = 0;
20526 rowH = 0;
20527 }
20528 }
20529};
20530
20531var defaults$d = {
20532 fit: true,
20533 // whether to fit the viewport to the graph
20534 padding: 30,
20535 // padding used on fit
20536 boundingBox: undefined,
20537 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20538 avoidOverlap: true,
20539 // prevents node overlap, may overflow boundingBox if not enough space
20540 avoidOverlapPadding: 10,
20541 // extra spacing around nodes when avoidOverlap: true
20542 nodeDimensionsIncludeLabels: false,
20543 // Excludes the label when calculating node bounding boxes for the layout algorithm
20544 spacingFactor: undefined,
20545 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20546 condense: false,
20547 // uses all available space on false, uses minimal space on true
20548 rows: undefined,
20549 // force num of rows in the grid
20550 cols: undefined,
20551 // force num of columns in the grid
20552 position: function position(node) {},
20553 // returns { row, col } for element
20554 sort: undefined,
20555 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20556 animate: false,
20557 // whether to transition the node positions
20558 animationDuration: 500,
20559 // duration of animation in ms if enabled
20560 animationEasing: undefined,
20561 // easing of animation if enabled
20562 animateFilter: function animateFilter(node, i) {
20563 return true;
20564 },
20565 // 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
20566 ready: undefined,
20567 // callback on layoutready
20568 stop: undefined,
20569 // callback on layoutstop
20570 transform: function transform(node, position) {
20571 return position;
20572 } // transform a given node position. Useful for changing flow direction in discrete layouts
20573
20574};
20575
20576function GridLayout(options) {
20577 this.options = extend({}, defaults$d, options);
20578}
20579
20580GridLayout.prototype.run = function () {
20581 var params = this.options;
20582 var options = params;
20583 var cy = params.cy;
20584 var eles = options.eles;
20585 var nodes = eles.nodes().not(':parent');
20586
20587 if (options.sort) {
20588 nodes = nodes.sort(options.sort);
20589 }
20590
20591 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20592 x1: 0,
20593 y1: 0,
20594 w: cy.width(),
20595 h: cy.height()
20596 });
20597
20598 if (bb.h === 0 || bb.w === 0) {
20599 nodes.layoutPositions(this, options, function (ele) {
20600 return {
20601 x: bb.x1,
20602 y: bb.y1
20603 };
20604 });
20605 } else {
20606 // width/height * splits^2 = cells where splits is number of times to split width
20607 var cells = nodes.size();
20608 var splits = Math.sqrt(cells * bb.h / bb.w);
20609 var rows = Math.round(splits);
20610 var cols = Math.round(bb.w / bb.h * splits);
20611
20612 var small = function small(val) {
20613 if (val == null) {
20614 return Math.min(rows, cols);
20615 } else {
20616 var min = Math.min(rows, cols);
20617
20618 if (min == rows) {
20619 rows = val;
20620 } else {
20621 cols = val;
20622 }
20623 }
20624 };
20625
20626 var large = function large(val) {
20627 if (val == null) {
20628 return Math.max(rows, cols);
20629 } else {
20630 var max = Math.max(rows, cols);
20631
20632 if (max == rows) {
20633 rows = val;
20634 } else {
20635 cols = val;
20636 }
20637 }
20638 };
20639
20640 var oRows = options.rows;
20641 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
20642
20643 if (oRows != null && oCols != null) {
20644 rows = oRows;
20645 cols = oCols;
20646 } else if (oRows != null && oCols == null) {
20647 rows = oRows;
20648 cols = Math.ceil(cells / rows);
20649 } else if (oRows == null && oCols != null) {
20650 cols = oCols;
20651 rows = Math.ceil(cells / cols);
20652 } // otherwise use the automatic values and adjust accordingly
20653 // if rounding was up, see if we can reduce rows or columns
20654 else if (cols * rows > cells) {
20655 var sm = small();
20656 var lg = large(); // reducing the small side takes away the most cells, so try it first
20657
20658 if ((sm - 1) * lg >= cells) {
20659 small(sm - 1);
20660 } else if ((lg - 1) * sm >= cells) {
20661 large(lg - 1);
20662 }
20663 } else {
20664 // if rounding was too low, add rows or columns
20665 while (cols * rows < cells) {
20666 var _sm = small();
20667
20668 var _lg = large(); // try to add to larger side first (adds less in multiplication)
20669
20670
20671 if ((_lg + 1) * _sm >= cells) {
20672 large(_lg + 1);
20673 } else {
20674 small(_sm + 1);
20675 }
20676 }
20677 }
20678
20679 var cellWidth = bb.w / cols;
20680 var cellHeight = bb.h / rows;
20681
20682 if (options.condense) {
20683 cellWidth = 0;
20684 cellHeight = 0;
20685 }
20686
20687 if (options.avoidOverlap) {
20688 for (var i = 0; i < nodes.length; i++) {
20689 var node = nodes[i];
20690 var pos = node._private.position;
20691
20692 if (pos.x == null || pos.y == null) {
20693 // for bb
20694 pos.x = 0;
20695 pos.y = 0;
20696 }
20697
20698 var nbb = node.layoutDimensions(options);
20699 var p = options.avoidOverlapPadding;
20700 var w = nbb.w + p;
20701 var h = nbb.h + p;
20702 cellWidth = Math.max(cellWidth, w);
20703 cellHeight = Math.max(cellHeight, h);
20704 }
20705 }
20706
20707 var cellUsed = {}; // e.g. 'c-0-2' => true
20708
20709 var used = function used(row, col) {
20710 return cellUsed['c-' + row + '-' + col] ? true : false;
20711 };
20712
20713 var use = function use(row, col) {
20714 cellUsed['c-' + row + '-' + col] = true;
20715 }; // to keep track of current cell position
20716
20717
20718 var row = 0;
20719 var col = 0;
20720
20721 var moveToNextCell = function moveToNextCell() {
20722 col++;
20723
20724 if (col >= cols) {
20725 col = 0;
20726 row++;
20727 }
20728 }; // get a cache of all the manual positions
20729
20730
20731 var id2manPos = {};
20732
20733 for (var _i = 0; _i < nodes.length; _i++) {
20734 var _node = nodes[_i];
20735 var rcPos = options.position(_node);
20736
20737 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
20738 // must have at least row or col def'd
20739 var _pos = {
20740 row: rcPos.row,
20741 col: rcPos.col
20742 };
20743
20744 if (_pos.col === undefined) {
20745 // find unused col
20746 _pos.col = 0;
20747
20748 while (used(_pos.row, _pos.col)) {
20749 _pos.col++;
20750 }
20751 } else if (_pos.row === undefined) {
20752 // find unused row
20753 _pos.row = 0;
20754
20755 while (used(_pos.row, _pos.col)) {
20756 _pos.row++;
20757 }
20758 }
20759
20760 id2manPos[_node.id()] = _pos;
20761 use(_pos.row, _pos.col);
20762 }
20763 }
20764
20765 var getPos = function getPos(element, i) {
20766 var x, y;
20767
20768 if (element.locked() || element.isParent()) {
20769 return false;
20770 } // see if we have a manual position set
20771
20772
20773 var rcPos = id2manPos[element.id()];
20774
20775 if (rcPos) {
20776 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
20777 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
20778 } else {
20779 // otherwise set automatically
20780 while (used(row, col)) {
20781 moveToNextCell();
20782 }
20783
20784 x = col * cellWidth + cellWidth / 2 + bb.x1;
20785 y = row * cellHeight + cellHeight / 2 + bb.y1;
20786 use(row, col);
20787 moveToNextCell();
20788 }
20789
20790 return {
20791 x: x,
20792 y: y
20793 };
20794 };
20795
20796 nodes.layoutPositions(this, options, getPos);
20797 }
20798
20799 return this; // chaining
20800};
20801
20802var defaults$e = {
20803 ready: function ready() {},
20804 // on layoutready
20805 stop: function stop() {} // on layoutstop
20806
20807}; // constructor
20808// options : object containing layout options
20809
20810function NullLayout(options) {
20811 this.options = extend({}, defaults$e, options);
20812} // runs the layout
20813
20814
20815NullLayout.prototype.run = function () {
20816 var options = this.options;
20817 var eles = options.eles; // elements to consider in the layout
20818
20819 var layout = this; // cy is automatically populated for us in the constructor
20820 // (disable eslint for next line as this serves as example layout code to external developers)
20821 // eslint-disable-next-line no-unused-vars
20822
20823 var cy = options.cy;
20824 layout.emit('layoutstart'); // puts all nodes at (0, 0)
20825 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
20826
20827 eles.nodes().positions(function () {
20828 return {
20829 x: 0,
20830 y: 0
20831 };
20832 }); // trigger layoutready when each node has had its position set at least once
20833
20834 layout.one('layoutready', options.ready);
20835 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
20836
20837 layout.one('layoutstop', options.stop);
20838 layout.emit('layoutstop');
20839 return this; // chaining
20840}; // called on continuous layouts to stop them before they finish
20841
20842
20843NullLayout.prototype.stop = function () {
20844 return this; // chaining
20845};
20846
20847var defaults$f = {
20848 positions: undefined,
20849 // map of (node id) => (position obj); or function(node){ return somPos; }
20850 zoom: undefined,
20851 // the zoom level to set (prob want fit = false if set)
20852 pan: undefined,
20853 // the pan level to set (prob want fit = false if set)
20854 fit: true,
20855 // whether to fit to viewport
20856 padding: 30,
20857 // padding on fit
20858 animate: false,
20859 // whether to transition the node positions
20860 animationDuration: 500,
20861 // duration of animation in ms if enabled
20862 animationEasing: undefined,
20863 // easing of animation if enabled
20864 animateFilter: function animateFilter(node, i) {
20865 return true;
20866 },
20867 // 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
20868 ready: undefined,
20869 // callback on layoutready
20870 stop: undefined,
20871 // callback on layoutstop
20872 transform: function transform(node, position) {
20873 return position;
20874 } // transform a given node position. Useful for changing flow direction in discrete layouts
20875
20876};
20877
20878function PresetLayout(options) {
20879 this.options = extend({}, defaults$f, options);
20880}
20881
20882PresetLayout.prototype.run = function () {
20883 var options = this.options;
20884 var eles = options.eles;
20885 var nodes = eles.nodes();
20886 var posIsFn = fn(options.positions);
20887
20888 function getPosition(node) {
20889 if (options.positions == null) {
20890 return copyPosition(node.position());
20891 }
20892
20893 if (posIsFn) {
20894 return options.positions(node);
20895 }
20896
20897 var pos = options.positions[node._private.data.id];
20898
20899 if (pos == null) {
20900 return null;
20901 }
20902
20903 return pos;
20904 }
20905
20906 nodes.layoutPositions(this, options, function (node, i) {
20907 var position = getPosition(node);
20908
20909 if (node.locked() || position == null) {
20910 return false;
20911 }
20912
20913 return position;
20914 });
20915 return this; // chaining
20916};
20917
20918var defaults$g = {
20919 fit: true,
20920 // whether to fit to viewport
20921 padding: 30,
20922 // fit padding
20923 boundingBox: undefined,
20924 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20925 animate: false,
20926 // whether to transition the node positions
20927 animationDuration: 500,
20928 // duration of animation in ms if enabled
20929 animationEasing: undefined,
20930 // easing of animation if enabled
20931 animateFilter: function animateFilter(node, i) {
20932 return true;
20933 },
20934 // 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
20935 ready: undefined,
20936 // callback on layoutready
20937 stop: undefined,
20938 // callback on layoutstop
20939 transform: function transform(node, position) {
20940 return position;
20941 } // transform a given node position. Useful for changing flow direction in discrete layouts
20942
20943};
20944
20945function RandomLayout(options) {
20946 this.options = extend({}, defaults$g, options);
20947}
20948
20949RandomLayout.prototype.run = function () {
20950 var options = this.options;
20951 var cy = options.cy;
20952 var eles = options.eles;
20953 var nodes = eles.nodes().not(':parent');
20954 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20955 x1: 0,
20956 y1: 0,
20957 w: cy.width(),
20958 h: cy.height()
20959 });
20960
20961 var getPos = function getPos(node, i) {
20962 return {
20963 x: bb.x1 + Math.round(Math.random() * bb.w),
20964 y: bb.y1 + Math.round(Math.random() * bb.h)
20965 };
20966 };
20967
20968 nodes.layoutPositions(this, options, getPos);
20969 return this; // chaining
20970};
20971
20972var layout = [{
20973 name: 'breadthfirst',
20974 impl: BreadthFirstLayout
20975}, {
20976 name: 'circle',
20977 impl: CircleLayout
20978}, {
20979 name: 'concentric',
20980 impl: ConcentricLayout
20981}, {
20982 name: 'cose',
20983 impl: CoseLayout
20984}, {
20985 name: 'grid',
20986 impl: GridLayout
20987}, {
20988 name: 'null',
20989 impl: NullLayout
20990}, {
20991 name: 'preset',
20992 impl: PresetLayout
20993}, {
20994 name: 'random',
20995 impl: RandomLayout
20996}];
20997
20998function NullRenderer(options) {
20999 this.options = options;
21000 this.notifications = 0; // for testing
21001}
21002
21003var noop$1 = function noop() {};
21004
21005var throwImgErr = function throwImgErr() {
21006 throw new Error('A headless instance can not render images');
21007};
21008
21009NullRenderer.prototype = {
21010 recalculateRenderedStyle: noop$1,
21011 notify: function notify() {
21012 this.notifications++;
21013 },
21014 init: noop$1,
21015 isHeadless: function isHeadless() {
21016 return true;
21017 },
21018 png: throwImgErr,
21019 jpg: throwImgErr
21020};
21021
21022var BRp = {};
21023BRp.arrowShapeWidth = 0.3;
21024
21025BRp.registerArrowShapes = function () {
21026 var arrowShapes = this.arrowShapes = {};
21027 var renderer = this; // Contract for arrow shapes:
21028 // 0, 0 is arrow tip
21029 // (0, 1) is direction towards node
21030 // (1, 0) is right
21031 //
21032 // functional api:
21033 // collide: check x, y in shape
21034 // roughCollide: called before collide, no false negatives
21035 // draw: draw
21036 // spacing: dist(arrowTip, nodeBoundary)
21037 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21038
21039 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21040 var x1 = translation.x - size / 2 - padding;
21041 var x2 = translation.x + size / 2 + padding;
21042 var y1 = translation.y - size / 2 - padding;
21043 var y2 = translation.y + size / 2 + padding;
21044 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21045 return inside;
21046 };
21047
21048 var transform = function transform(x, y, size, angle, translation) {
21049 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21050 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21051 var xScaled = xRotated * size;
21052 var yScaled = yRotated * size;
21053 var xTranslated = xScaled + translation.x;
21054 var yTranslated = yScaled + translation.y;
21055 return {
21056 x: xTranslated,
21057 y: yTranslated
21058 };
21059 };
21060
21061 var transformPoints = function transformPoints(pts, size, angle, translation) {
21062 var retPts = [];
21063
21064 for (var i = 0; i < pts.length; i += 2) {
21065 var x = pts[i];
21066 var y = pts[i + 1];
21067 retPts.push(transform(x, y, size, angle, translation));
21068 }
21069
21070 return retPts;
21071 };
21072
21073 var pointsToArr = function pointsToArr(pts) {
21074 var ret = [];
21075
21076 for (var i = 0; i < pts.length; i++) {
21077 var p = pts[i];
21078 ret.push(p.x, p.y);
21079 }
21080
21081 return ret;
21082 };
21083
21084 var standardGap = function standardGap(edge) {
21085 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21086 };
21087
21088 var defineArrowShape = function defineArrowShape(name, defn) {
21089 if (string(defn)) {
21090 defn = arrowShapes[defn];
21091 }
21092
21093 arrowShapes[name] = extend({
21094 name: name,
21095 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21096 collide: function collide(x, y, size, angle, translation, padding) {
21097 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21098 var inside = pointInsidePolygonPoints(x, y, points);
21099 return inside;
21100 },
21101 roughCollide: bbCollide,
21102 draw: function draw(context, size, angle, translation) {
21103 var points = transformPoints(this.points, size, angle, translation);
21104 renderer.arrowShapeImpl('polygon')(context, points);
21105 },
21106 spacing: function spacing(edge) {
21107 return 0;
21108 },
21109 gap: standardGap
21110 }, defn);
21111 };
21112
21113 defineArrowShape('none', {
21114 collide: falsify,
21115 roughCollide: falsify,
21116 draw: noop,
21117 spacing: zeroify,
21118 gap: zeroify
21119 });
21120 defineArrowShape('triangle', {
21121 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21122 });
21123 defineArrowShape('arrow', 'triangle');
21124 defineArrowShape('triangle-backcurve', {
21125 points: arrowShapes['triangle'].points,
21126 controlPoint: [0, -0.15],
21127 roughCollide: bbCollide,
21128 draw: function draw(context, size, angle, translation, edgeWidth) {
21129 var ptsTrans = transformPoints(this.points, size, angle, translation);
21130 var ctrlPt = this.controlPoint;
21131 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21132 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21133 },
21134 gap: function gap(edge) {
21135 return standardGap(edge) * 0.8;
21136 }
21137 });
21138 defineArrowShape('triangle-tee', {
21139 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21140 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21141 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21142 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21143 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21144 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21145 return inside;
21146 },
21147 draw: function draw(context, size, angle, translation, edgeWidth) {
21148 var triPts = transformPoints(this.points, size, angle, translation);
21149 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21150 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21151 }
21152 });
21153 defineArrowShape('triangle-cross', {
21154 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21155 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21156 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21157 0.15, -0.4],
21158 crossLinePts: function crossLinePts(size, edgeWidth) {
21159 // shift points so that the distance between the cross points matches edge width
21160 var p = this.baseCrossLinePts.slice();
21161 var shiftFactor = edgeWidth / size;
21162 var y0 = 3;
21163 var y1 = 5;
21164 p[y0] = p[y0] - shiftFactor;
21165 p[y1] = p[y1] - shiftFactor;
21166 return p;
21167 },
21168 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21169 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21170 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21171 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21172 return inside;
21173 },
21174 draw: function draw(context, size, angle, translation, edgeWidth) {
21175 var triPts = transformPoints(this.points, size, angle, translation);
21176 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21177 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21178 }
21179 });
21180 defineArrowShape('vee', {
21181 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21182 gap: function gap(edge) {
21183 return standardGap(edge) * 0.525;
21184 }
21185 });
21186 defineArrowShape('circle', {
21187 radius: 0.15,
21188 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21189 var t = translation;
21190 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21191 return inside;
21192 },
21193 draw: function draw(context, size, angle, translation, edgeWidth) {
21194 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21195 },
21196 spacing: function spacing(edge) {
21197 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21198 }
21199 });
21200 defineArrowShape('tee', {
21201 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21202 spacing: function spacing(edge) {
21203 return 1;
21204 },
21205 gap: function gap(edge) {
21206 return 1;
21207 }
21208 });
21209 defineArrowShape('square', {
21210 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21211 });
21212 defineArrowShape('diamond', {
21213 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21214 gap: function gap(edge) {
21215 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21216 }
21217 });
21218 defineArrowShape('chevron', {
21219 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21220 gap: function gap(edge) {
21221 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21222 }
21223 });
21224};
21225
21226var BRp$1 = {}; // Project mouse
21227
21228BRp$1.projectIntoViewport = function (clientX, clientY) {
21229 var cy = this.cy;
21230 var offsets = this.findContainerClientCoords();
21231 var offsetLeft = offsets[0];
21232 var offsetTop = offsets[1];
21233 var scale = offsets[4];
21234 var pan = cy.pan();
21235 var zoom = cy.zoom();
21236 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21237 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21238 return [x, y];
21239};
21240
21241BRp$1.findContainerClientCoords = function () {
21242 if (this.containerBB) {
21243 return this.containerBB;
21244 }
21245
21246 var container = this.container;
21247 var rect = container.getBoundingClientRect();
21248 var style = window$1.getComputedStyle(container);
21249
21250 var styleValue = function styleValue(name) {
21251 return parseFloat(style.getPropertyValue(name));
21252 };
21253
21254 var padding = {
21255 left: styleValue('padding-left'),
21256 right: styleValue('padding-right'),
21257 top: styleValue('padding-top'),
21258 bottom: styleValue('padding-bottom')
21259 };
21260 var border = {
21261 left: styleValue('border-left-width'),
21262 right: styleValue('border-right-width'),
21263 top: styleValue('border-top-width'),
21264 bottom: styleValue('border-bottom-width')
21265 };
21266 var clientWidth = container.clientWidth;
21267 var clientHeight = container.clientHeight;
21268 var paddingHor = padding.left + padding.right;
21269 var paddingVer = padding.top + padding.bottom;
21270 var borderHor = border.left + border.right;
21271 var scale = rect.width / (clientWidth + borderHor);
21272 var unscaledW = clientWidth - paddingHor;
21273 var unscaledH = clientHeight - paddingVer;
21274 var left = rect.left + padding.left + border.left;
21275 var top = rect.top + padding.top + border.top;
21276 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21277};
21278
21279BRp$1.invalidateContainerClientCoordsCache = function () {
21280 this.containerBB = null;
21281};
21282
21283BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21284 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21285};
21286
21287BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21288 var self = this;
21289 var r = this;
21290 var eles = r.getCachedZSortedEles();
21291 var near = []; // 1 node max, 1 edge max
21292
21293 var zoom = r.cy.zoom();
21294 var hasCompounds = r.cy.hasCompoundNodes();
21295 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21296 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21297 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21298 var minSqDist = Infinity;
21299 var nearEdge;
21300 var nearNode;
21301
21302 if (interactiveElementsOnly) {
21303 eles = eles.interactive;
21304 }
21305
21306 function addEle(ele, sqDist) {
21307 if (ele.isNode()) {
21308 if (nearNode) {
21309 return; // can't replace node
21310 } else {
21311 nearNode = ele;
21312 near.push(ele);
21313 }
21314 }
21315
21316 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21317 if (nearEdge) {
21318 // then replace existing edge
21319 // can replace only if same z-index
21320 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) {
21321 for (var i = 0; i < near.length; i++) {
21322 if (near[i].isEdge()) {
21323 near[i] = ele;
21324 nearEdge = ele;
21325 minSqDist = sqDist != null ? sqDist : minSqDist;
21326 break;
21327 }
21328 }
21329 }
21330 } else {
21331 near.push(ele);
21332 nearEdge = ele;
21333 minSqDist = sqDist != null ? sqDist : minSqDist;
21334 }
21335 }
21336 }
21337
21338 function checkNode(node) {
21339 var width = node.outerWidth() + 2 * nodeThreshold;
21340 var height = node.outerHeight() + 2 * nodeThreshold;
21341 var hw = width / 2;
21342 var hh = height / 2;
21343 var pos = node.position();
21344
21345 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21346 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21347 ) {
21348 var shape = r.nodeShapes[self.getNodeShape(node)];
21349
21350 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21351 addEle(node, 0);
21352 return true;
21353 }
21354 }
21355 }
21356
21357 function checkEdge(edge) {
21358 var _p = edge._private;
21359 var rs = _p.rscratch;
21360 var styleWidth = edge.pstyle('width').pfValue;
21361 var scale = edge.pstyle('arrow-scale').value;
21362 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21363
21364 var widthSq = width * width;
21365 var width2 = width * 2;
21366 var src = _p.source;
21367 var tgt = _p.target;
21368 var sqDist;
21369
21370 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21371 var pts = rs.allpts;
21372
21373 for (var i = 0; i + 3 < pts.length; i += 2) {
21374 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]))) {
21375 addEle(edge, sqDist);
21376 return true;
21377 }
21378 }
21379 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21380 var pts = rs.allpts;
21381
21382 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21383 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]))) {
21384 addEle(edge, sqDist);
21385 return true;
21386 }
21387 }
21388 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21389
21390
21391 var src = src || _p.source;
21392 var tgt = tgt || _p.target;
21393 var arSize = self.getArrowWidth(styleWidth, scale);
21394 var arrows = [{
21395 name: 'source',
21396 x: rs.arrowStartX,
21397 y: rs.arrowStartY,
21398 angle: rs.srcArrowAngle
21399 }, {
21400 name: 'target',
21401 x: rs.arrowEndX,
21402 y: rs.arrowEndY,
21403 angle: rs.tgtArrowAngle
21404 }, {
21405 name: 'mid-source',
21406 x: rs.midX,
21407 y: rs.midY,
21408 angle: rs.midsrcArrowAngle
21409 }, {
21410 name: 'mid-target',
21411 x: rs.midX,
21412 y: rs.midY,
21413 angle: rs.midtgtArrowAngle
21414 }];
21415
21416 for (var i = 0; i < arrows.length; i++) {
21417 var ar = arrows[i];
21418 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21419 var edgeWidth = edge.pstyle('width').pfValue;
21420
21421 if (shape.roughCollide(x, y, arSize, ar.angle, {
21422 x: ar.x,
21423 y: ar.y
21424 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21425 x: ar.x,
21426 y: ar.y
21427 }, edgeWidth, edgeThreshold)) {
21428 addEle(edge);
21429 return true;
21430 }
21431 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21432
21433
21434 if (hasCompounds && near.length > 0) {
21435 checkNode(src);
21436 checkNode(tgt);
21437 }
21438 }
21439
21440 function preprop(obj, name, pre) {
21441 return getPrefixedProperty(obj, name, pre);
21442 }
21443
21444 function checkLabel(ele, prefix) {
21445 var _p = ele._private;
21446 var th = labelThreshold;
21447 var prefixDash;
21448
21449 if (prefix) {
21450 prefixDash = prefix + '-';
21451 } else {
21452 prefixDash = '';
21453 }
21454
21455 ele.boundingBox();
21456 var bb = _p.labelBounds[prefix || 'main'];
21457 var text = ele.pstyle(prefixDash + 'label').value;
21458 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21459
21460 if (!eventsEnabled || !text) {
21461 return;
21462 }
21463
21464 var rstyle = _p.rstyle;
21465 var lx = preprop(rstyle, 'labelX', prefix);
21466 var ly = preprop(rstyle, 'labelY', prefix);
21467 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21468 var lx1 = bb.x1 - th;
21469 var lx2 = bb.x2 + th;
21470 var ly1 = bb.y1 - th;
21471 var ly2 = bb.y2 + th;
21472
21473 if (theta) {
21474 var cos = Math.cos(theta);
21475 var sin = Math.sin(theta);
21476
21477 var rotate = function rotate(x, y) {
21478 x = x - lx;
21479 y = y - ly;
21480 return {
21481 x: x * cos - y * sin + lx,
21482 y: x * sin + y * cos + ly
21483 };
21484 };
21485
21486 var px1y1 = rotate(lx1, ly1);
21487 var px1y2 = rotate(lx1, ly2);
21488 var px2y1 = rotate(lx2, ly1);
21489 var px2y2 = rotate(lx2, ly2);
21490 var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
21491
21492 if (pointInsidePolygonPoints(x, y, points)) {
21493 addEle(ele);
21494 return true;
21495 }
21496 } else {
21497 // do a cheaper bb check
21498 if (inBoundingBox(bb, x, y)) {
21499 addEle(ele);
21500 return true;
21501 }
21502 }
21503 }
21504
21505 for (var i = eles.length - 1; i >= 0; i--) {
21506 // reverse order for precedence
21507 var ele = eles[i];
21508
21509 if (ele.isNode()) {
21510 checkNode(ele) || checkLabel(ele);
21511 } else {
21512 // then edge
21513 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21514 }
21515 }
21516
21517 return near;
21518}; // 'Give me everything from this box'
21519
21520
21521BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21522 var eles = this.getCachedZSortedEles().interactive;
21523 var box = [];
21524 var x1c = Math.min(x1, x2);
21525 var x2c = Math.max(x1, x2);
21526 var y1c = Math.min(y1, y2);
21527 var y2c = Math.max(y1, y2);
21528 x1 = x1c;
21529 x2 = x2c;
21530 y1 = y1c;
21531 y2 = y2c;
21532 var boxBb = makeBoundingBox({
21533 x1: x1,
21534 y1: y1,
21535 x2: x2,
21536 y2: y2
21537 });
21538
21539 for (var e = 0; e < eles.length; e++) {
21540 var ele = eles[e];
21541
21542 if (ele.isNode()) {
21543 var node = ele;
21544 var nodeBb = node.boundingBox({
21545 includeNodes: true,
21546 includeEdges: false,
21547 includeLabels: false
21548 });
21549
21550 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21551 box.push(node);
21552 }
21553 } else {
21554 var edge = ele;
21555 var _p = edge._private;
21556 var rs = _p.rscratch;
21557
21558 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21559 continue;
21560 }
21561
21562 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21563 continue;
21564 }
21565
21566 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21567 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21568 var allInside = true;
21569
21570 for (var i = 0; i < pts.length; i++) {
21571 if (!pointInBoundingBox(boxBb, pts[i])) {
21572 allInside = false;
21573 break;
21574 }
21575 }
21576
21577 if (allInside) {
21578 box.push(edge);
21579 }
21580 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
21581 box.push(edge);
21582 }
21583 }
21584 }
21585
21586 return box;
21587};
21588
21589var BRp$2 = {};
21590
21591BRp$2.calculateArrowAngles = function (edge) {
21592 var rs = edge._private.rscratch;
21593 var isHaystack = rs.edgeType === 'haystack';
21594 var isBezier = rs.edgeType === 'bezier';
21595 var isMultibezier = rs.edgeType === 'multibezier';
21596 var isSegments = rs.edgeType === 'segments';
21597 var isCompound = rs.edgeType === 'compound';
21598 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
21599
21600 var dispX, dispY;
21601 var startX, startY, endX, endY, midX, midY;
21602
21603 if (isHaystack) {
21604 startX = rs.haystackPts[0];
21605 startY = rs.haystackPts[1];
21606 endX = rs.haystackPts[2];
21607 endY = rs.haystackPts[3];
21608 } else {
21609 startX = rs.arrowStartX;
21610 startY = rs.arrowStartY;
21611 endX = rs.arrowEndX;
21612 endY = rs.arrowEndY;
21613 }
21614
21615 midX = rs.midX;
21616 midY = rs.midY; // source
21617 //
21618
21619 if (isSegments) {
21620 dispX = startX - rs.segpts[0];
21621 dispY = startY - rs.segpts[1];
21622 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21623 var pts = rs.allpts;
21624 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
21625 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
21626 dispX = startX - bX;
21627 dispY = startY - bY;
21628 } else {
21629 dispX = startX - midX;
21630 dispY = startY - midY;
21631 }
21632
21633 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
21634 //
21635
21636 var midX = rs.midX;
21637 var midY = rs.midY;
21638
21639 if (isHaystack) {
21640 midX = (startX + endX) / 2;
21641 midY = (startY + endY) / 2;
21642 }
21643
21644 dispX = endX - startX;
21645 dispY = endY - startY;
21646
21647 if (isSegments) {
21648 var pts = rs.allpts;
21649
21650 if (pts.length / 2 % 2 === 0) {
21651 var i2 = pts.length / 2;
21652 var i1 = i2 - 2;
21653 dispX = pts[i2] - pts[i1];
21654 dispY = pts[i2 + 1] - pts[i1 + 1];
21655 } else {
21656 var i2 = pts.length / 2 - 1;
21657 var i1 = i2 - 2;
21658 var i3 = i2 + 2;
21659 dispX = pts[i2] - pts[i1];
21660 dispY = pts[i2 + 1] - pts[i1 + 1];
21661 }
21662 } else if (isMultibezier || isCompound || isSelf) {
21663 var pts = rs.allpts;
21664 var cpts = rs.ctrlpts;
21665 var bp0x, bp0y;
21666 var bp1x, bp1y;
21667
21668 if (cpts.length / 2 % 2 === 0) {
21669 var p0 = pts.length / 2 - 1; // startpt
21670
21671 var ic = p0 + 2;
21672 var p1 = ic + 2;
21673 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
21674 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
21675 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
21676 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
21677 } else {
21678 var ic = pts.length / 2 - 1; // ctrpt
21679
21680 var p0 = ic - 2; // startpt
21681
21682 var p1 = ic + 2; // endpt
21683
21684 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
21685 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
21686 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
21687 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
21688 }
21689
21690 dispX = bp1x - bp0x;
21691 dispY = bp1y - bp0y;
21692 }
21693
21694 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
21695 rs.midDispX = dispX;
21696 rs.midDispY = dispY; // mid source
21697 //
21698
21699 dispX *= -1;
21700 dispY *= -1;
21701
21702 if (isSegments) {
21703 var pts = rs.allpts;
21704
21705 if (pts.length / 2 % 2 === 0) ; else {
21706 var i2 = pts.length / 2 - 1;
21707 var i3 = i2 + 2;
21708 dispX = -(pts[i3] - pts[i2]);
21709 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
21710 }
21711 }
21712
21713 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
21714 //
21715
21716 if (isSegments) {
21717 dispX = endX - rs.segpts[rs.segpts.length - 2];
21718 dispY = endY - rs.segpts[rs.segpts.length - 1];
21719 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21720 var pts = rs.allpts;
21721 var l = pts.length;
21722 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
21723 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
21724 dispX = endX - bX;
21725 dispY = endY - bY;
21726 } else {
21727 dispX = endX - midX;
21728 dispY = endY - midY;
21729 }
21730
21731 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
21732};
21733
21734BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
21735 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
21736 var cachedVal = cache[edgeWidth + ', ' + scale];
21737
21738 if (cachedVal) {
21739 return cachedVal;
21740 }
21741
21742 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
21743 cache[edgeWidth + ', ' + scale] = cachedVal;
21744 return cachedVal;
21745};
21746
21747var BRp$3 = {};
21748
21749BRp$3.findHaystackPoints = function (edges) {
21750 for (var i = 0; i < edges.length; i++) {
21751 var edge = edges[i];
21752 var _p = edge._private;
21753 var rs = _p.rscratch;
21754
21755 if (!rs.haystack) {
21756 var angle = Math.random() * 2 * Math.PI;
21757 rs.source = {
21758 x: Math.cos(angle),
21759 y: Math.sin(angle)
21760 };
21761 angle = Math.random() * 2 * Math.PI;
21762 rs.target = {
21763 x: Math.cos(angle),
21764 y: Math.sin(angle)
21765 };
21766 }
21767
21768 var src = _p.source;
21769 var tgt = _p.target;
21770 var srcPos = src.position();
21771 var tgtPos = tgt.position();
21772 var srcW = src.width();
21773 var tgtW = tgt.width();
21774 var srcH = src.height();
21775 var tgtH = tgt.height();
21776 var radius = edge.pstyle('haystack-radius').value;
21777 var halfRadius = radius / 2; // b/c have to half width/height
21778
21779 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];
21780 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
21781 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
21782
21783 rs.edgeType = 'haystack';
21784 rs.haystack = true;
21785 this.storeEdgeProjections(edge);
21786 this.calculateArrowAngles(edge);
21787 this.recalculateEdgeLabelProjections(edge);
21788 this.calculateLabelAngles(edge);
21789 }
21790};
21791
21792BRp$3.findSegmentsPoints = function (edge, pairInfo) {
21793 // Segments (multiple straight lines)
21794 var rs = edge._private.rscratch;
21795 var posPts = pairInfo.posPts,
21796 intersectionPts = pairInfo.intersectionPts,
21797 vectorNormInverse = pairInfo.vectorNormInverse;
21798 var edgeDistances = edge.pstyle('edge-distances').value;
21799 var segmentWs = edge.pstyle('segment-weights');
21800 var segmentDs = edge.pstyle('segment-distances');
21801 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
21802 rs.edgeType = 'segments';
21803 rs.segpts = [];
21804
21805 for (var s = 0; s < segmentsN; s++) {
21806 var w = segmentWs.pfValue[s];
21807 var d = segmentDs.pfValue[s];
21808 var w1 = 1 - w;
21809 var w2 = w;
21810 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
21811 var adjustedMidpt = {
21812 x: midptPts.x1 * w1 + midptPts.x2 * w2,
21813 y: midptPts.y1 * w1 + midptPts.y2 * w2
21814 };
21815 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
21816 }
21817};
21818
21819BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
21820 // Self-edge
21821 var rs = edge._private.rscratch;
21822 var dirCounts = pairInfo.dirCounts,
21823 srcPos = pairInfo.srcPos;
21824 var ctrlptDists = edge.pstyle('control-point-distances');
21825 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
21826 var loopDir = edge.pstyle('loop-direction').pfValue;
21827 var loopSwp = edge.pstyle('loop-sweep').pfValue;
21828 var stepSize = edge.pstyle('control-point-step-size').pfValue;
21829 rs.edgeType = 'self';
21830 var j = i;
21831 var loopDist = stepSize;
21832
21833 if (edgeIsUnbundled) {
21834 j = 0;
21835 loopDist = ctrlptDist;
21836 }
21837
21838 var loopAngle = loopDir - Math.PI / 2;
21839 var outAngle = loopAngle - loopSwp / 2;
21840 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
21841
21842 var dc = String(loopDir + '_' + loopSwp);
21843 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
21844 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)];
21845};
21846
21847BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
21848 // Compound edge
21849 var rs = edge._private.rscratch;
21850 rs.edgeType = 'compound';
21851 var srcPos = pairInfo.srcPos,
21852 tgtPos = pairInfo.tgtPos,
21853 srcW = pairInfo.srcW,
21854 srcH = pairInfo.srcH,
21855 tgtW = pairInfo.tgtW,
21856 tgtH = pairInfo.tgtH;
21857 var stepSize = edge.pstyle('control-point-step-size').pfValue;
21858 var ctrlptDists = edge.pstyle('control-point-distances');
21859 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
21860 var j = i;
21861 var loopDist = stepSize;
21862
21863 if (edgeIsUnbundled) {
21864 j = 0;
21865 loopDist = ctrlptDist;
21866 }
21867
21868 var loopW = 50;
21869 var loopaPos = {
21870 x: srcPos.x - srcW / 2,
21871 y: srcPos.y - srcH / 2
21872 };
21873 var loopbPos = {
21874 x: tgtPos.x - tgtW / 2,
21875 y: tgtPos.y - tgtH / 2
21876 };
21877 var loopPos = {
21878 x: Math.min(loopaPos.x, loopbPos.x),
21879 y: Math.min(loopaPos.y, loopbPos.y)
21880 }; // avoids cases with impossible beziers
21881
21882 var minCompoundStretch = 0.5;
21883 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
21884 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
21885 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];
21886};
21887
21888BRp$3.findStraightEdgePoints = function (edge) {
21889 // Straight edge within bundle
21890 edge._private.rscratch.edgeType = 'straight';
21891};
21892
21893BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
21894 var rs = edge._private.rscratch;
21895 var vectorNormInverse = pairInfo.vectorNormInverse,
21896 posPts = pairInfo.posPts,
21897 intersectionPts = pairInfo.intersectionPts;
21898 var edgeDistances = edge.pstyle('edge-distances').value;
21899 var stepSize = edge.pstyle('control-point-step-size').pfValue;
21900 var ctrlptDists = edge.pstyle('control-point-distances');
21901 var ctrlptWs = edge.pstyle('control-point-weights');
21902 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
21903 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
21904 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
21905
21906 var multi = edgeIsUnbundled;
21907 rs.edgeType = multi ? 'multibezier' : 'bezier';
21908 rs.ctrlpts = [];
21909
21910 for (var b = 0; b < bezierN; b++) {
21911 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
21912 var manctrlptDist = void 0;
21913 var sign = signum(normctrlptDist);
21914
21915 if (multi) {
21916 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
21917
21918 ctrlptWeight = ctrlptWs.value[b];
21919 }
21920
21921 if (edgeIsUnbundled) {
21922 // multi or single unbundled
21923 manctrlptDist = ctrlptDist;
21924 } else {
21925 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
21926 }
21927
21928 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
21929 var w1 = 1 - ctrlptWeight;
21930 var w2 = ctrlptWeight;
21931 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
21932 var adjustedMidpt = {
21933 x: midptPts.x1 * w1 + midptPts.x2 * w2,
21934 y: midptPts.y1 * w1 + midptPts.y2 * w2
21935 };
21936 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
21937 }
21938};
21939
21940BRp$3.findTaxiPoints = function (edge, pairInfo) {
21941 // Taxicab geometry with two turns maximum
21942 var rs = edge._private.rscratch;
21943 rs.edgeType = 'segments';
21944 var VERTICAL = 'vertical';
21945 var HORIZONTAL = 'horizontal';
21946 var LEFTWARD = 'leftward';
21947 var RIGHTWARD = 'rightward';
21948 var DOWNWARD = 'downward';
21949 var UPWARD = 'upward';
21950 var AUTO = 'auto';
21951 var posPts = pairInfo.posPts,
21952 srcW = pairInfo.srcW,
21953 srcH = pairInfo.srcH,
21954 tgtW = pairInfo.tgtW,
21955 tgtH = pairInfo.tgtH;
21956 var edgeDistances = edge.pstyle('edge-distances').value;
21957 var dIncludesNodeBody = edgeDistances !== 'node-position';
21958 var taxiDir = edge.pstyle('taxi-direction').value;
21959 var rawTaxiDir = taxiDir; // unprocessed value
21960
21961 var taxiTurn = edge.pstyle('taxi-turn');
21962 var taxiTurnPfVal = taxiTurn.pfValue;
21963 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
21964 var turnIsPercent = taxiTurn.units === '%';
21965 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
21966 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
21967 var pdx = posPts.x2 - posPts.x1;
21968 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
21969
21970 var subDWH = function subDWH(dxy, dwh) {
21971 if (dxy > 0) {
21972 return Math.max(dxy - dwh, 0);
21973 } else {
21974 return Math.min(dxy + dwh, 0);
21975 }
21976 };
21977
21978 var dx = subDWH(pdx, dw);
21979 var dy = subDWH(pdy, dh);
21980 var isExplicitDir = false;
21981
21982 if (taxiDir === AUTO) {
21983 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
21984 } else if (taxiDir === UPWARD || taxiDir === DOWNWARD) {
21985 taxiDir = VERTICAL;
21986 isExplicitDir = true;
21987 } else if (taxiDir === LEFTWARD || taxiDir === RIGHTWARD) {
21988 taxiDir = HORIZONTAL;
21989 isExplicitDir = true;
21990 }
21991
21992 var isVert = taxiDir === VERTICAL;
21993 var l = isVert ? dy : dx;
21994 var pl = isVert ? pdy : pdx;
21995 var sgnL = signum(pl);
21996 var forcedDir = false;
21997
21998 if (!(isExplicitDir && turnIsPercent) // forcing in this case would cause weird growing in the opposite direction
21999 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22000 sgnL *= -1;
22001 l = sgnL * Math.abs(l);
22002 forcedDir = true;
22003 }
22004
22005 var d = turnIsPercent ? taxiTurnPfVal * l : taxiTurnPfVal * sgnL;
22006
22007 var getIsTooClose = function getIsTooClose(d) {
22008 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22009 };
22010
22011 var isTooCloseSrc = getIsTooClose(d);
22012 var isTooCloseTgt = getIsTooClose(l - d);
22013 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22014
22015 if (isTooClose && !forcedDir) {
22016 // non-ideal routing
22017 if (isVert) {
22018 // vertical fallbacks
22019 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22020 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22021
22022 if (lShapeInsideSrc) {
22023 // horizontal Z-shape (direction not respected)
22024 var x = (posPts.x1 + posPts.x2) / 2;
22025 var y1 = posPts.y1,
22026 y2 = posPts.y2;
22027 rs.segpts = [x, y1, x, y2];
22028 } else if (lShapeInsideTgt) {
22029 // vertical Z-shape (distance not respected)
22030 var y = (posPts.y1 + posPts.y2) / 2;
22031 var x1 = posPts.x1,
22032 x2 = posPts.x2;
22033 rs.segpts = [x1, y, x2, y];
22034 } else {
22035 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22036 rs.segpts = [posPts.x1, posPts.y2];
22037 }
22038 } else {
22039 // horizontal fallbacks
22040 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22041
22042 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22043
22044 if (_lShapeInsideSrc) {
22045 // vertical Z-shape (direction not respected)
22046 var _y = (posPts.y1 + posPts.y2) / 2;
22047
22048 var _x = posPts.x1,
22049 _x2 = posPts.x2;
22050 rs.segpts = [_x, _y, _x2, _y];
22051 } else if (_lShapeInsideTgt) {
22052 // horizontal Z-shape (turn distance not respected)
22053 var _x3 = (posPts.x1 + posPts.x2) / 2;
22054
22055 var _y2 = posPts.y1,
22056 _y3 = posPts.y2;
22057 rs.segpts = [_x3, _y2, _x3, _y3];
22058 } else {
22059 // L-shape (turn distance not respected, but works well for tree siblings)
22060 rs.segpts = [posPts.x2, posPts.y1];
22061 }
22062 }
22063 } else {
22064 // ideal routing
22065 if (isVert) {
22066 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22067
22068 var _x4 = posPts.x1,
22069 _x5 = posPts.x2;
22070 rs.segpts = [_x4, _y4, _x5, _y4];
22071 } else {
22072 // horizontal
22073 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22074
22075 var _y5 = posPts.y1,
22076 _y6 = posPts.y2;
22077 rs.segpts = [_x6, _y5, _x6, _y6];
22078 }
22079 }
22080};
22081
22082BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22083 var rs = edge._private.rscratch; // can only correct beziers for now...
22084
22085 if (rs.edgeType === 'bezier') {
22086 var srcPos = pairInfo.srcPos,
22087 tgtPos = pairInfo.tgtPos,
22088 srcW = pairInfo.srcW,
22089 srcH = pairInfo.srcH,
22090 tgtW = pairInfo.tgtW,
22091 tgtH = pairInfo.tgtH,
22092 srcShape = pairInfo.srcShape,
22093 tgtShape = pairInfo.tgtShape;
22094 var badStart = !number(rs.startX) || !number(rs.startY);
22095 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22096 var badEnd = !number(rs.endX) || !number(rs.endY);
22097 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22098 var minCpADistFactor = 3;
22099 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22100 var minCpADist = minCpADistFactor * arrowW;
22101 var startACpDist = dist({
22102 x: rs.ctrlpts[0],
22103 y: rs.ctrlpts[1]
22104 }, {
22105 x: rs.startX,
22106 y: rs.startY
22107 });
22108 var closeStartACp = startACpDist < minCpADist;
22109 var endACpDist = dist({
22110 x: rs.ctrlpts[0],
22111 y: rs.ctrlpts[1]
22112 }, {
22113 x: rs.endX,
22114 y: rs.endY
22115 });
22116 var closeEndACp = endACpDist < minCpADist;
22117 var overlapping = false;
22118
22119 if (badStart || badAStart || closeStartACp) {
22120 overlapping = true; // project control point along line from src centre to outside the src shape
22121 // (otherwise intersection will yield nothing)
22122
22123 var cpD = {
22124 // delta
22125 x: rs.ctrlpts[0] - srcPos.x,
22126 y: rs.ctrlpts[1] - srcPos.y
22127 };
22128 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22129
22130 var cpM = {
22131 // normalised delta
22132 x: cpD.x / cpL,
22133 y: cpD.y / cpL
22134 };
22135 var radius = Math.max(srcW, srcH);
22136 var cpProj = {
22137 // *2 radius guarantees outside shape
22138 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22139 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22140 };
22141 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22142
22143 if (closeStartACp) {
22144 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22145 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22146 } else {
22147 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22148 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22149 }
22150 }
22151
22152 if (badEnd || badAEnd || closeEndACp) {
22153 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22154 // (otherwise intersection will yield nothing)
22155
22156 var _cpD = {
22157 // delta
22158 x: rs.ctrlpts[0] - tgtPos.x,
22159 y: rs.ctrlpts[1] - tgtPos.y
22160 };
22161
22162 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22163
22164
22165 var _cpM = {
22166 // normalised delta
22167 x: _cpD.x / _cpL,
22168 y: _cpD.y / _cpL
22169 };
22170
22171 var _radius = Math.max(srcW, srcH);
22172
22173 var _cpProj = {
22174 // *2 radius guarantees outside shape
22175 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22176 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22177 };
22178 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22179
22180 if (closeEndACp) {
22181 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22182 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22183 } else {
22184 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22185 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22186 }
22187 }
22188
22189 if (overlapping) {
22190 // recalc endpts
22191 this.findEndpoints(edge);
22192 }
22193 }
22194};
22195
22196BRp$3.storeAllpts = function (edge) {
22197 var rs = edge._private.rscratch;
22198
22199 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22200 rs.allpts = [];
22201 rs.allpts.push(rs.startX, rs.startY);
22202
22203 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22204 // ctrl pt itself
22205 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22206
22207 if (b + 3 < rs.ctrlpts.length) {
22208 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22209 }
22210 }
22211
22212 rs.allpts.push(rs.endX, rs.endY);
22213 var m, mt;
22214
22215 if (rs.ctrlpts.length / 2 % 2 === 0) {
22216 m = rs.allpts.length / 2 - 1;
22217 rs.midX = rs.allpts[m];
22218 rs.midY = rs.allpts[m + 1];
22219 } else {
22220 m = rs.allpts.length / 2 - 3;
22221 mt = 0.5;
22222 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22223 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22224 }
22225 } else if (rs.edgeType === 'straight') {
22226 // need to calc these after endpts
22227 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22228
22229 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22230 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22231 } else if (rs.edgeType === 'segments') {
22232 rs.allpts = [];
22233 rs.allpts.push(rs.startX, rs.startY);
22234 rs.allpts.push.apply(rs.allpts, rs.segpts);
22235 rs.allpts.push(rs.endX, rs.endY);
22236
22237 if (rs.segpts.length % 4 === 0) {
22238 var i2 = rs.segpts.length / 2;
22239 var i1 = i2 - 2;
22240 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22241 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22242 } else {
22243 var _i = rs.segpts.length / 2 - 1;
22244
22245 rs.midX = rs.segpts[_i];
22246 rs.midY = rs.segpts[_i + 1];
22247 }
22248 }
22249};
22250
22251BRp$3.checkForInvalidEdgeWarning = function (edge) {
22252 var rs = edge[0]._private.rscratch;
22253
22254 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22255 rs.loggedErr = false;
22256 } else {
22257 if (!rs.loggedErr) {
22258 rs.loggedErr = true;
22259 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.');
22260 }
22261 }
22262};
22263
22264BRp$3.findEdgeControlPoints = function (edges) {
22265 var _this = this;
22266
22267 if (!edges || edges.length === 0) {
22268 return;
22269 }
22270
22271 var r = this;
22272 var cy = r.cy;
22273 var hasCompounds = cy.hasCompoundNodes();
22274 var hashTable = {
22275 map: new Map$1(),
22276 get: function get(pairId) {
22277 var map2 = this.map.get(pairId[0]);
22278
22279 if (map2 != null) {
22280 return map2.get(pairId[1]);
22281 } else {
22282 return null;
22283 }
22284 },
22285 set: function set(pairId, val) {
22286 var map2 = this.map.get(pairId[0]);
22287
22288 if (map2 == null) {
22289 map2 = new Map$1();
22290 this.map.set(pairId[0], map2);
22291 }
22292
22293 map2.set(pairId[1], val);
22294 }
22295 };
22296 var pairIds = [];
22297 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22298
22299 for (var i = 0; i < edges.length; i++) {
22300 var edge = edges[i];
22301 var _p = edge._private;
22302 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22303 // they shouldn't take up space
22304
22305 if (edge.removed() || !edge.takesUpSpace()) {
22306 continue;
22307 }
22308
22309 if (curveStyle === 'haystack') {
22310 haystackEdges.push(edge);
22311 continue;
22312 }
22313
22314 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
22315 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22316 var src = _p.source;
22317 var tgt = _p.target;
22318 var srcIndex = src.poolIndex();
22319 var tgtIndex = tgt.poolIndex();
22320 var pairId = [srcIndex, tgtIndex].sort();
22321 var tableEntry = hashTable.get(pairId);
22322
22323 if (tableEntry == null) {
22324 tableEntry = {
22325 eles: []
22326 };
22327 hashTable.set(pairId, tableEntry);
22328 pairIds.push(pairId);
22329 }
22330
22331 tableEntry.eles.push(edge);
22332
22333 if (edgeIsUnbundled) {
22334 tableEntry.hasUnbundled = true;
22335 }
22336
22337 if (edgeIsBezier) {
22338 tableEntry.hasBezier = true;
22339 }
22340 } // for each pair (src, tgt), create the ctrl pts
22341 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22342
22343
22344 var _loop = function _loop(p) {
22345 var pairId = pairIds[p];
22346 var pairInfo = hashTable.get(pairId);
22347 var swappedpairInfo = void 0;
22348
22349 if (!pairInfo.hasUnbundled) {
22350 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22351 return e.isBundledBezier();
22352 });
22353 clearArray(pairInfo.eles);
22354 pllEdges.forEach(function (edge) {
22355 return pairInfo.eles.push(edge);
22356 }); // for each pair id, the edges should be sorted by index
22357
22358 pairInfo.eles.sort(function (edge1, edge2) {
22359 return edge1.poolIndex() - edge2.poolIndex();
22360 });
22361 }
22362
22363 var firstEdge = pairInfo.eles[0];
22364 var src = firstEdge.source();
22365 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22366
22367 if (src.poolIndex() > tgt.poolIndex()) {
22368 var temp = src;
22369 src = tgt;
22370 tgt = temp;
22371 }
22372
22373 var srcPos = pairInfo.srcPos = src.position();
22374 var tgtPos = pairInfo.tgtPos = tgt.position();
22375 var srcW = pairInfo.srcW = src.outerWidth();
22376 var srcH = pairInfo.srcH = src.outerHeight();
22377 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22378 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22379
22380 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22381
22382 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22383
22384 pairInfo.dirCounts = {
22385 'north': 0,
22386 'west': 0,
22387 'south': 0,
22388 'east': 0,
22389 'northwest': 0,
22390 'southwest': 0,
22391 'northeast': 0,
22392 'southeast': 0
22393 };
22394
22395 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22396 var _edge = pairInfo.eles[_i2];
22397 var rs = _edge[0]._private.rscratch;
22398
22399 var _curveStyle = _edge.pstyle('curve-style').value;
22400
22401 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22402
22403
22404 var edgeIsSwapped = !src.same(_edge.source());
22405
22406 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22407 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22408
22409 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22410 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22411
22412 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22413 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22414 var intersectionPts = pairInfo.intersectionPts = {
22415 x1: srcOutside[0],
22416 x2: tgtOutside[0],
22417 y1: srcOutside[1],
22418 y2: tgtOutside[1]
22419 };
22420 var posPts = pairInfo.posPts = {
22421 x1: srcPos.x,
22422 x2: tgtPos.x,
22423 y1: srcPos.y,
22424 y2: tgtPos.y
22425 };
22426 var dy = tgtOutside[1] - srcOutside[1];
22427 var dx = tgtOutside[0] - srcOutside[0];
22428 var l = Math.sqrt(dx * dx + dy * dy);
22429 var vector = pairInfo.vector = {
22430 x: dx,
22431 y: dy
22432 };
22433 var vectorNorm = pairInfo.vectorNorm = {
22434 x: vector.x / l,
22435 y: vector.y / l
22436 };
22437 var vectorNormInverse = {
22438 x: -vectorNorm.y,
22439 y: vectorNorm.x
22440 }; // if node shapes overlap, then no ctrl pts to draw
22441
22442 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);
22443 pairInfo.vectorNormInverse = vectorNormInverse;
22444 swappedpairInfo = {
22445 nodesOverlap: pairInfo.nodesOverlap,
22446 dirCounts: pairInfo.dirCounts,
22447 calculatedIntersection: true,
22448 hasBezier: pairInfo.hasBezier,
22449 hasUnbundled: pairInfo.hasUnbundled,
22450 eles: pairInfo.eles,
22451 srcPos: tgtPos,
22452 tgtPos: srcPos,
22453 srcW: tgtW,
22454 srcH: tgtH,
22455 tgtW: srcW,
22456 tgtH: srcH,
22457 srcIntn: tgtIntn,
22458 tgtIntn: srcIntn,
22459 srcShape: tgtShape,
22460 tgtShape: srcShape,
22461 posPts: {
22462 x1: posPts.x2,
22463 y1: posPts.y2,
22464 x2: posPts.x1,
22465 y2: posPts.y1
22466 },
22467 intersectionPts: {
22468 x1: intersectionPts.x2,
22469 y1: intersectionPts.y2,
22470 x2: intersectionPts.x1,
22471 y2: intersectionPts.y1
22472 },
22473 vector: {
22474 x: -vector.x,
22475 y: -vector.y
22476 },
22477 vectorNorm: {
22478 x: -vectorNorm.x,
22479 y: -vectorNorm.y
22480 },
22481 vectorNormInverse: {
22482 x: -vectorNormInverse.x,
22483 y: -vectorNormInverse.y
22484 }
22485 };
22486 }
22487
22488 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22489 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22490 rs.srcIntn = passedPairInfo.srcIntn;
22491 rs.tgtIntn = passedPairInfo.tgtIntn;
22492
22493 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22494 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22495 } else if (src === tgt) {
22496 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22497 } else if (_curveStyle === 'segments') {
22498 _this.findSegmentsPoints(_edge, passedPairInfo);
22499 } else if (_curveStyle === 'taxi') {
22500 _this.findTaxiPoints(_edge, passedPairInfo);
22501 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22502 _this.findStraightEdgePoints(_edge);
22503 } else {
22504 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22505 }
22506
22507 _this.findEndpoints(_edge);
22508
22509 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22510
22511 _this.checkForInvalidEdgeWarning(_edge);
22512
22513 _this.storeAllpts(_edge);
22514
22515 _this.storeEdgeProjections(_edge);
22516
22517 _this.calculateArrowAngles(_edge);
22518
22519 _this.recalculateEdgeLabelProjections(_edge);
22520
22521 _this.calculateLabelAngles(_edge);
22522 } // for pair edges
22523
22524 };
22525
22526 for (var p = 0; p < pairIds.length; p++) {
22527 _loop(p);
22528 } // for pair ids
22529 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22530
22531
22532 this.findHaystackPoints(haystackEdges);
22533};
22534
22535function getPts(pts) {
22536 var retPts = [];
22537
22538 if (pts == null) {
22539 return;
22540 }
22541
22542 for (var i = 0; i < pts.length; i += 2) {
22543 var x = pts[i];
22544 var y = pts[i + 1];
22545 retPts.push({
22546 x: x,
22547 y: y
22548 });
22549 }
22550
22551 return retPts;
22552}
22553
22554BRp$3.getSegmentPoints = function (edge) {
22555 var rs = edge[0]._private.rscratch;
22556 var type = rs.edgeType;
22557
22558 if (type === 'segments') {
22559 this.recalculateRenderedStyle(edge);
22560 return getPts(rs.segpts);
22561 }
22562};
22563
22564BRp$3.getControlPoints = function (edge) {
22565 var rs = edge[0]._private.rscratch;
22566 var type = rs.edgeType;
22567
22568 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
22569 this.recalculateRenderedStyle(edge);
22570 return getPts(rs.ctrlpts);
22571 }
22572};
22573
22574BRp$3.getEdgeMidpoint = function (edge) {
22575 var rs = edge[0]._private.rscratch;
22576 this.recalculateRenderedStyle(edge);
22577 return {
22578 x: rs.midX,
22579 y: rs.midY
22580 };
22581};
22582
22583var BRp$4 = {};
22584
22585BRp$4.manualEndptToPx = function (node, prop) {
22586 var r = this;
22587 var npos = node.position();
22588 var w = node.outerWidth();
22589 var h = node.outerHeight();
22590
22591 if (prop.value.length === 2) {
22592 var p = [prop.pfValue[0], prop.pfValue[1]];
22593
22594 if (prop.units[0] === '%') {
22595 p[0] = p[0] * w;
22596 }
22597
22598 if (prop.units[1] === '%') {
22599 p[1] = p[1] * h;
22600 }
22601
22602 p[0] += npos.x;
22603 p[1] += npos.y;
22604 return p;
22605 } else {
22606 var angle = prop.pfValue[0];
22607 angle = -Math.PI / 2 + angle; // start at 12 o'clock
22608
22609 var l = 2 * Math.max(w, h);
22610 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
22611 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
22612 }
22613};
22614
22615BRp$4.findEndpoints = function (edge) {
22616 var r = this;
22617 var intersect;
22618 var source = edge.source()[0];
22619 var target = edge.target()[0];
22620 var srcPos = source.position();
22621 var tgtPos = target.position();
22622 var tgtArShape = edge.pstyle('target-arrow-shape').value;
22623 var srcArShape = edge.pstyle('source-arrow-shape').value;
22624 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
22625 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
22626 var curveStyle = edge.pstyle('curve-style').value;
22627 var rs = edge._private.rscratch;
22628 var et = rs.edgeType;
22629 var taxi = curveStyle === 'taxi';
22630 var self = et === 'self' || et === 'compound';
22631 var bezier = et === 'bezier' || et === 'multibezier' || self;
22632 var multi = et !== 'bezier';
22633 var lines = et === 'straight' || et === 'segments';
22634 var segments = et === 'segments';
22635 var hasEndpts = bezier || multi || lines;
22636 var overrideEndpts = self || taxi;
22637 var srcManEndpt = edge.pstyle('source-endpoint');
22638 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
22639 var tgtManEndpt = edge.pstyle('target-endpoint');
22640 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
22641 rs.srcManEndpt = srcManEndpt;
22642 rs.tgtManEndpt = tgtManEndpt;
22643 var p1; // last known point of edge on target side
22644
22645 var p2; // last known point of edge on source side
22646
22647 var p1_i; // point to intersect with target shape
22648
22649 var p2_i; // point to intersect with source shape
22650
22651 if (bezier) {
22652 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
22653 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
22654 p1 = cpEnd;
22655 p2 = cpStart;
22656 } else if (lines) {
22657 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
22658 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
22659 p1 = tgtArrowFromPt;
22660 p2 = srcArrowFromPt;
22661 }
22662
22663 if (tgtManEndptVal === 'inside-to-node') {
22664 intersect = [tgtPos.x, tgtPos.y];
22665 } else if (tgtManEndpt.units) {
22666 intersect = this.manualEndptToPx(target, tgtManEndpt);
22667 } else if (tgtManEndptVal === 'outside-to-line') {
22668 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
22669 } else {
22670 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
22671 p1_i = p1;
22672 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
22673 p1_i = [srcPos.x, srcPos.y];
22674 }
22675
22676 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
22677
22678 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
22679 var trs = target._private.rscratch;
22680 var lw = trs.labelWidth;
22681 var lh = trs.labelHeight;
22682 var lx = trs.labelX;
22683 var ly = trs.labelY;
22684 var va = target.pstyle('text-valign').value;
22685
22686 if (va === 'top') {
22687 ly -= lh / 2;
22688 } else if (va === 'bottom') {
22689 ly += lh / 2;
22690 }
22691
22692 var ha = target.pstyle('text-halign').value;
22693
22694 if (ha === 'left') {
22695 lx -= lw / 2;
22696 } else if (ha === 'right') {
22697 lx += lw / 2;
22698 }
22699
22700 var labelIntersect = r.nodeShapes['rectangle'].intersectLine(lx, ly, lw, lh, p1_i[0], p1_i[1], 0);
22701 var refPt = srcPos;
22702 var intSqdist = sqdist(refPt, array2point(intersect));
22703 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
22704
22705 if (labIntSqdist < intSqdist) {
22706 intersect = labelIntersect;
22707 }
22708 }
22709 }
22710
22711 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
22712 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
22713 rs.endX = edgeEnd[0];
22714 rs.endY = edgeEnd[1];
22715 rs.arrowEndX = arrowEnd[0];
22716 rs.arrowEndY = arrowEnd[1];
22717
22718 if (srcManEndptVal === 'inside-to-node') {
22719 intersect = [srcPos.x, srcPos.y];
22720 } else if (srcManEndpt.units) {
22721 intersect = this.manualEndptToPx(source, srcManEndpt);
22722 } else if (srcManEndptVal === 'outside-to-line') {
22723 intersect = rs.srcIntn; // use cached value from ctrlpt calc
22724 } else {
22725 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
22726 p2_i = p2;
22727 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
22728 p2_i = [tgtPos.x, tgtPos.y];
22729 }
22730
22731 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
22732
22733 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
22734 var srs = source._private.rscratch;
22735 var _lw = srs.labelWidth;
22736 var _lh = srs.labelHeight;
22737 var _lx = srs.labelX;
22738 var _ly = srs.labelY;
22739 var _va = source.pstyle('text-valign').value;
22740
22741 if (_va === 'top') {
22742 _ly -= _lh / 2;
22743 } else if (_va === 'bottom') {
22744 _ly += _lh / 2;
22745 }
22746
22747 var _ha = source.pstyle('text-halign').value;
22748
22749 if (_ha === 'left') {
22750 _lx -= _lw / 2;
22751 } else if (_ha === 'right') {
22752 _lx += _lw / 2;
22753 }
22754
22755 var _labelIntersect = r.nodeShapes['rectangle'].intersectLine(_lx, _ly, _lw, _lh, p2_i[0], p2_i[1], 0);
22756
22757 var _refPt = tgtPos;
22758
22759 var _intSqdist = sqdist(_refPt, array2point(intersect));
22760
22761 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
22762
22763 if (_labIntSqdist < _intSqdist) {
22764 intersect = _labelIntersect;
22765 }
22766 }
22767 }
22768
22769 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
22770 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
22771 rs.startX = edgeStart[0];
22772 rs.startY = edgeStart[1];
22773 rs.arrowStartX = arrowStart[0];
22774 rs.arrowStartY = arrowStart[1];
22775
22776 if (hasEndpts) {
22777 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
22778 rs.badLine = true;
22779 } else {
22780 rs.badLine = false;
22781 }
22782 }
22783};
22784
22785BRp$4.getSourceEndpoint = function (edge) {
22786 var rs = edge[0]._private.rscratch;
22787 this.recalculateRenderedStyle(edge);
22788
22789 switch (rs.edgeType) {
22790 case 'haystack':
22791 return {
22792 x: rs.haystackPts[0],
22793 y: rs.haystackPts[1]
22794 };
22795
22796 default:
22797 return {
22798 x: rs.arrowStartX,
22799 y: rs.arrowStartY
22800 };
22801 }
22802};
22803
22804BRp$4.getTargetEndpoint = function (edge) {
22805 var rs = edge[0]._private.rscratch;
22806 this.recalculateRenderedStyle(edge);
22807
22808 switch (rs.edgeType) {
22809 case 'haystack':
22810 return {
22811 x: rs.haystackPts[2],
22812 y: rs.haystackPts[3]
22813 };
22814
22815 default:
22816 return {
22817 x: rs.arrowEndX,
22818 y: rs.arrowEndY
22819 };
22820 }
22821};
22822
22823var BRp$5 = {};
22824
22825function pushBezierPts(r, edge, pts) {
22826 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
22827 return qbezierAt(p1, p2, p3, t);
22828 };
22829
22830 var _p = edge._private;
22831 var bpts = _p.rstyle.bezierPts;
22832
22833 for (var i = 0; i < r.bezierProjPcts.length; i++) {
22834 var p = r.bezierProjPcts[i];
22835 bpts.push({
22836 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
22837 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
22838 });
22839 }
22840}
22841
22842BRp$5.storeEdgeProjections = function (edge) {
22843 var _p = edge._private;
22844 var rs = _p.rscratch;
22845 var et = rs.edgeType; // clear the cached points state
22846
22847 _p.rstyle.bezierPts = null;
22848 _p.rstyle.linePts = null;
22849 _p.rstyle.haystackPts = null;
22850
22851 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
22852 _p.rstyle.bezierPts = [];
22853
22854 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
22855 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
22856 }
22857 } else if (et === 'segments') {
22858 var lpts = _p.rstyle.linePts = [];
22859
22860 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
22861 lpts.push({
22862 x: rs.allpts[i],
22863 y: rs.allpts[i + 1]
22864 });
22865 }
22866 } else if (et === 'haystack') {
22867 var hpts = rs.haystackPts;
22868 _p.rstyle.haystackPts = [{
22869 x: hpts[0],
22870 y: hpts[1]
22871 }, {
22872 x: hpts[2],
22873 y: hpts[3]
22874 }];
22875 }
22876
22877 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22878};
22879
22880BRp$5.recalculateEdgeProjections = function (edges) {
22881 this.findEdgeControlPoints(edges);
22882};
22883
22884var BRp$6 = {};
22885
22886BRp$6.recalculateNodeLabelProjection = function (node) {
22887 var content = node.pstyle('label').strValue;
22888
22889 if (emptyString(content)) {
22890 return;
22891 }
22892
22893 var textX, textY;
22894 var _p = node._private;
22895 var nodeWidth = node.width();
22896 var nodeHeight = node.height();
22897 var padding = node.padding();
22898 var nodePos = node.position();
22899 var textHalign = node.pstyle('text-halign').strValue;
22900 var textValign = node.pstyle('text-valign').strValue;
22901 var rs = _p.rscratch;
22902 var rstyle = _p.rstyle;
22903
22904 switch (textHalign) {
22905 case 'left':
22906 textX = nodePos.x - nodeWidth / 2 - padding;
22907 break;
22908
22909 case 'right':
22910 textX = nodePos.x + nodeWidth / 2 + padding;
22911 break;
22912
22913 default:
22914 // e.g. center
22915 textX = nodePos.x;
22916 }
22917
22918 switch (textValign) {
22919 case 'top':
22920 textY = nodePos.y - nodeHeight / 2 - padding;
22921 break;
22922
22923 case 'bottom':
22924 textY = nodePos.y + nodeHeight / 2 + padding;
22925 break;
22926
22927 default:
22928 // e.g. middle
22929 textY = nodePos.y;
22930 }
22931
22932 rs.labelX = textX;
22933 rs.labelY = textY;
22934 rstyle.labelX = textX;
22935 rstyle.labelY = textY;
22936 this.applyLabelDimensions(node);
22937};
22938
22939var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
22940 var angle = Math.atan(dy / dx);
22941
22942 if (dx === 0 && angle < 0) {
22943 angle = angle * -1;
22944 }
22945
22946 return angle;
22947};
22948
22949var lineAngle = function lineAngle(p0, p1) {
22950 var dx = p1.x - p0.x;
22951 var dy = p1.y - p0.y;
22952 return lineAngleFromDelta(dx, dy);
22953};
22954
22955var bezierAngle = function bezierAngle(p0, p1, p2, t) {
22956 var t0 = bound(0, t - 0.001, 1);
22957 var t1 = bound(0, t + 0.001, 1);
22958 var lp0 = qbezierPtAt(p0, p1, p2, t0);
22959 var lp1 = qbezierPtAt(p0, p1, p2, t1);
22960 return lineAngle(lp0, lp1);
22961};
22962
22963BRp$6.recalculateEdgeLabelProjections = function (edge) {
22964 var p;
22965 var _p = edge._private;
22966 var rs = _p.rscratch;
22967 var r = this;
22968 var content = {
22969 mid: edge.pstyle('label').strValue,
22970 source: edge.pstyle('source-label').strValue,
22971 target: edge.pstyle('target-label').strValue
22972 };
22973
22974 if (content.mid || content.source || content.target) ; else {
22975 return; // no labels => no calcs
22976 } // add center point to style so bounding box calculations can use it
22977 //
22978
22979
22980 p = {
22981 x: rs.midX,
22982 y: rs.midY
22983 };
22984
22985 var setRs = function setRs(propName, prefix, value) {
22986 setPrefixedProperty(_p.rscratch, propName, prefix, value);
22987 setPrefixedProperty(_p.rstyle, propName, prefix, value);
22988 };
22989
22990 setRs('labelX', null, p.x);
22991 setRs('labelY', null, p.y);
22992 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
22993 setRs('labelAutoAngle', null, midAngle);
22994
22995 var createControlPointInfo = function createControlPointInfo() {
22996 if (createControlPointInfo.cache) {
22997 return createControlPointInfo.cache;
22998 } // use cache so only 1x per edge
22999
23000
23001 var ctrlpts = []; // store each ctrlpt info init
23002
23003 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23004 var p0 = {
23005 x: rs.allpts[i],
23006 y: rs.allpts[i + 1]
23007 };
23008 var p1 = {
23009 x: rs.allpts[i + 2],
23010 y: rs.allpts[i + 3]
23011 }; // ctrlpt
23012
23013 var p2 = {
23014 x: rs.allpts[i + 4],
23015 y: rs.allpts[i + 5]
23016 };
23017 ctrlpts.push({
23018 p0: p0,
23019 p1: p1,
23020 p2: p2,
23021 startDist: 0,
23022 length: 0,
23023 segments: []
23024 });
23025 }
23026
23027 var bpts = _p.rstyle.bezierPts;
23028 var nProjs = r.bezierProjPcts.length;
23029
23030 function addSegment(cp, p0, p1, t0, t1) {
23031 var length = dist(p0, p1);
23032 var prevSegment = cp.segments[cp.segments.length - 1];
23033 var segment = {
23034 p0: p0,
23035 p1: p1,
23036 t0: t0,
23037 t1: t1,
23038 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23039 length: length
23040 };
23041 cp.segments.push(segment);
23042 cp.length += length;
23043 } // update each ctrlpt with segment info
23044
23045
23046 for (var _i = 0; _i < ctrlpts.length; _i++) {
23047 var cp = ctrlpts[_i];
23048 var prevCp = ctrlpts[_i - 1];
23049
23050 if (prevCp) {
23051 cp.startDist = prevCp.startDist + prevCp.length;
23052 }
23053
23054 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23055
23056 for (var j = 0; j < nProjs - 1; j++) {
23057 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23058 }
23059
23060 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23061 }
23062
23063 return createControlPointInfo.cache = ctrlpts;
23064 };
23065
23066 var calculateEndProjection = function calculateEndProjection(prefix) {
23067 var angle;
23068 var isSrc = prefix === 'source';
23069
23070 if (!content[prefix]) {
23071 return;
23072 }
23073
23074 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23075
23076 switch (rs.edgeType) {
23077 case 'self':
23078 case 'compound':
23079 case 'bezier':
23080 case 'multibezier':
23081 {
23082 var cps = createControlPointInfo();
23083 var selected;
23084 var startDist = 0;
23085 var totalDist = 0; // find the segment we're on
23086
23087 for (var i = 0; i < cps.length; i++) {
23088 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23089
23090 for (var j = 0; j < _cp.segments.length; j++) {
23091 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23092 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23093 startDist = totalDist;
23094 totalDist += _seg.length;
23095
23096 if (totalDist >= offset || lastSeg) {
23097 selected = {
23098 cp: _cp,
23099 segment: _seg
23100 };
23101 break;
23102 }
23103 }
23104
23105 if (selected) {
23106 break;
23107 }
23108 }
23109
23110 var cp = selected.cp;
23111 var seg = selected.segment;
23112 var tSegment = (offset - startDist) / seg.length;
23113 var segDt = seg.t1 - seg.t0;
23114 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23115 t = bound(0, t, 1);
23116 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23117 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23118 break;
23119 }
23120
23121 case 'straight':
23122 case 'segments':
23123 case 'haystack':
23124 {
23125 var d = 0,
23126 di,
23127 d0;
23128 var p0, p1;
23129 var l = rs.allpts.length;
23130
23131 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23132 if (isSrc) {
23133 p0 = {
23134 x: rs.allpts[_i2],
23135 y: rs.allpts[_i2 + 1]
23136 };
23137 p1 = {
23138 x: rs.allpts[_i2 + 2],
23139 y: rs.allpts[_i2 + 3]
23140 };
23141 } else {
23142 p0 = {
23143 x: rs.allpts[l - 2 - _i2],
23144 y: rs.allpts[l - 1 - _i2]
23145 };
23146 p1 = {
23147 x: rs.allpts[l - 4 - _i2],
23148 y: rs.allpts[l - 3 - _i2]
23149 };
23150 }
23151
23152 di = dist(p0, p1);
23153 d0 = d;
23154 d += di;
23155
23156 if (d >= offset) {
23157 break;
23158 }
23159 }
23160
23161 var pD = offset - d0;
23162
23163 var _t = pD / di;
23164
23165 _t = bound(0, _t, 1);
23166 p = lineAt(p0, p1, _t);
23167 angle = lineAngle(p0, p1);
23168 break;
23169 }
23170 }
23171
23172 setRs('labelX', prefix, p.x);
23173 setRs('labelY', prefix, p.y);
23174 setRs('labelAutoAngle', prefix, angle);
23175 };
23176
23177 calculateEndProjection('source');
23178 calculateEndProjection('target');
23179 this.applyLabelDimensions(edge);
23180};
23181
23182BRp$6.applyLabelDimensions = function (ele) {
23183 this.applyPrefixedLabelDimensions(ele);
23184
23185 if (ele.isEdge()) {
23186 this.applyPrefixedLabelDimensions(ele, 'source');
23187 this.applyPrefixedLabelDimensions(ele, 'target');
23188 }
23189};
23190
23191BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23192 var _p = ele._private;
23193 var text = this.getLabelText(ele, prefix);
23194 var labelDims = this.calculateLabelDimensions(ele, text);
23195 var lineHeight = ele.pstyle('line-height').pfValue;
23196 var textWrap = ele.pstyle('text-wrap').strValue;
23197 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23198 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23199 var normPerLineHeight = labelDims.height / numLines;
23200 var labelLineHeight = normPerLineHeight * lineHeight;
23201 var width = labelDims.width;
23202 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23203 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23204 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23205 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23206 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23207 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23208};
23209
23210BRp$6.getLabelText = function (ele, prefix) {
23211 var _p = ele._private;
23212 var pfd = prefix ? prefix + '-' : '';
23213 var text = ele.pstyle(pfd + 'label').strValue;
23214 var textTransform = ele.pstyle('text-transform').value;
23215
23216 var rscratch = function rscratch(propName, value) {
23217 if (value) {
23218 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23219 return value;
23220 } else {
23221 return getPrefixedProperty(_p.rscratch, propName, prefix);
23222 }
23223 }; // for empty text, skip all processing
23224
23225
23226 if (!text) {
23227 return '';
23228 }
23229
23230 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23231 text = text.toUpperCase();
23232 } else if (textTransform == 'lowercase') {
23233 text = text.toLowerCase();
23234 }
23235
23236 var wrapStyle = ele.pstyle('text-wrap').value;
23237
23238 if (wrapStyle === 'wrap') {
23239 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23240
23241 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23242 return rscratch('labelWrapCachedText');
23243 }
23244
23245 var zwsp = "\u200B";
23246 var lines = text.split('\n');
23247 var maxW = ele.pstyle('text-max-width').pfValue;
23248 var overflow = ele.pstyle('text-overflow-wrap').value;
23249 var overflowAny = overflow === 'anywhere';
23250 var wrappedLines = [];
23251 var wordsRegex = /[\s\u200b]+/;
23252 var wordSeparator = overflowAny ? '' : ' ';
23253
23254 for (var l = 0; l < lines.length; l++) {
23255 var line = lines[l];
23256 var lineDims = this.calculateLabelDimensions(ele, line);
23257 var lineW = lineDims.width;
23258
23259 if (overflowAny) {
23260 var processedLine = line.split('').join(zwsp);
23261 line = processedLine;
23262 }
23263
23264 if (lineW > maxW) {
23265 // line is too long
23266 var words = line.split(wordsRegex);
23267 var subline = '';
23268
23269 for (var w = 0; w < words.length; w++) {
23270 var word = words[w];
23271 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23272 var testDims = this.calculateLabelDimensions(ele, testLine);
23273 var testW = testDims.width;
23274
23275 if (testW <= maxW) {
23276 // word fits on current line
23277 subline += word + wordSeparator;
23278 } else {
23279 // word starts new line
23280 if (subline) {
23281 wrappedLines.push(subline);
23282 }
23283
23284 subline = word + wordSeparator;
23285 }
23286 } // if there's remaining text, put it in a wrapped line
23287
23288
23289 if (!subline.match(/^[\s\u200b]+$/)) {
23290 wrappedLines.push(subline);
23291 }
23292 } else {
23293 // line is already short enough
23294 wrappedLines.push(line);
23295 }
23296 } // for
23297
23298
23299 rscratch('labelWrapCachedLines', wrappedLines);
23300 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23301 rscratch('labelWrapKey', labelKey);
23302 } else if (wrapStyle === 'ellipsis') {
23303 var _maxW = ele.pstyle('text-max-width').pfValue;
23304 var ellipsized = '';
23305 var ellipsis = "\u2026";
23306 var incLastCh = false;
23307
23308 for (var i = 0; i < text.length; i++) {
23309 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23310
23311 if (widthWithNextCh > _maxW) {
23312 break;
23313 }
23314
23315 ellipsized += text[i];
23316
23317 if (i === text.length - 1) {
23318 incLastCh = true;
23319 }
23320 }
23321
23322 if (!incLastCh) {
23323 ellipsized += ellipsis;
23324 }
23325
23326 return ellipsized;
23327 } // if ellipsize
23328
23329
23330 return text;
23331};
23332
23333BRp$6.getLabelJustification = function (ele) {
23334 var justification = ele.pstyle('text-justification').strValue;
23335 var textHalign = ele.pstyle('text-halign').strValue;
23336
23337 if (justification === 'auto') {
23338 if (ele.isNode()) {
23339 switch (textHalign) {
23340 case 'left':
23341 return 'right';
23342
23343 case 'right':
23344 return 'left';
23345
23346 default:
23347 return 'center';
23348 }
23349 } else {
23350 return 'center';
23351 }
23352 } else {
23353 return justification;
23354 }
23355};
23356
23357BRp$6.calculateLabelDimensions = function (ele, text) {
23358 var r = this;
23359 var cacheKey = hashString(text, ele._private.labelDimsKey);
23360 var cache = r.labelDimCache || (r.labelDimCache = []);
23361 var existingVal = cache[cacheKey];
23362
23363 if (existingVal != null) {
23364 return existingVal;
23365 }
23366
23367 var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text
23368
23369 var fStyle = ele.pstyle('font-style').strValue;
23370 var size = sizeMult * ele.pstyle('font-size').pfValue + 'px';
23371 var family = ele.pstyle('font-family').strValue;
23372 var weight = ele.pstyle('font-weight').strValue;
23373 var div = this.labelCalcDiv;
23374
23375 if (!div) {
23376 div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef
23377
23378 document.body.appendChild(div); // eslint-disable-line no-undef
23379 }
23380
23381 var ds = div.style; // from ele style
23382
23383 ds.fontFamily = family;
23384 ds.fontStyle = fStyle;
23385 ds.fontSize = size;
23386 ds.fontWeight = weight; // forced style
23387
23388 ds.position = 'absolute';
23389 ds.left = '-9999px';
23390 ds.top = '-9999px';
23391 ds.zIndex = '-1';
23392 ds.visibility = 'hidden';
23393 ds.pointerEvents = 'none';
23394 ds.padding = '0';
23395 ds.lineHeight = '1';
23396
23397 if (ele.pstyle('text-wrap').value === 'wrap') {
23398 ds.whiteSpace = 'pre'; // so newlines are taken into account
23399 } else {
23400 ds.whiteSpace = 'normal';
23401 } // put label content in div
23402
23403
23404 div.textContent = text;
23405 return cache[cacheKey] = {
23406 width: Math.ceil(div.clientWidth / sizeMult),
23407 height: Math.ceil(div.clientHeight / sizeMult)
23408 };
23409};
23410
23411BRp$6.calculateLabelAngle = function (ele, prefix) {
23412 var _p = ele._private;
23413 var rs = _p.rscratch;
23414 var isEdge = ele.isEdge();
23415 var prefixDash = prefix ? prefix + '-' : '';
23416 var rot = ele.pstyle(prefixDash + 'text-rotation');
23417 var rotStr = rot.strValue;
23418
23419 if (rotStr === 'none') {
23420 return 0;
23421 } else if (isEdge && rotStr === 'autorotate') {
23422 return rs.labelAutoAngle;
23423 } else if (rotStr === 'autorotate') {
23424 return 0;
23425 } else {
23426 return rot.pfValue;
23427 }
23428};
23429
23430BRp$6.calculateLabelAngles = function (ele) {
23431 var r = this;
23432 var isEdge = ele.isEdge();
23433 var _p = ele._private;
23434 var rs = _p.rscratch;
23435 rs.labelAngle = r.calculateLabelAngle(ele);
23436
23437 if (isEdge) {
23438 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23439 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23440 }
23441};
23442
23443var BRp$7 = {};
23444var TOO_SMALL_CUT_RECT = 28;
23445var warnedCutRect = false;
23446
23447BRp$7.getNodeShape = function (node) {
23448 var r = this;
23449 var shape = node.pstyle('shape').value;
23450
23451 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23452 if (!warnedCutRect) {
23453 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23454 warnedCutRect = true;
23455 }
23456
23457 return 'rectangle';
23458 }
23459
23460 if (node.isParent()) {
23461 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') {
23462 return shape;
23463 } else {
23464 return 'rectangle';
23465 }
23466 }
23467
23468 if (shape === 'polygon') {
23469 var points = node.pstyle('shape-polygon-points').value;
23470 return r.nodeShapes.makePolygon(points).name;
23471 }
23472
23473 return shape;
23474};
23475
23476var BRp$8 = {};
23477
23478BRp$8.registerCalculationListeners = function () {
23479 var cy = this.cy;
23480 var elesToUpdate = cy.collection();
23481 var r = this;
23482
23483 var enqueue = function enqueue(eles) {
23484 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23485 elesToUpdate.merge(eles);
23486
23487 if (dirtyStyleCaches) {
23488 for (var i = 0; i < eles.length; i++) {
23489 var ele = eles[i];
23490 var _p = ele._private;
23491 var rstyle = _p.rstyle;
23492 rstyle.clean = false;
23493 rstyle.cleanConnected = false;
23494 }
23495 }
23496 };
23497
23498 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23499 var ele = e.target;
23500 enqueue(ele);
23501 }).on('style.* background.*', function onDirtyStyle(e) {
23502 var ele = e.target;
23503 enqueue(ele, false);
23504 });
23505
23506 var updateEleCalcs = function updateEleCalcs(willDraw) {
23507 if (willDraw) {
23508 var fns = r.onUpdateEleCalcsFns;
23509
23510 for (var i = 0; i < elesToUpdate.length; i++) {
23511 var ele = elesToUpdate[i];
23512 var rstyle = ele._private.rstyle;
23513
23514 if (ele.isNode() && !rstyle.cleanConnected) {
23515 enqueue(ele.connectedEdges());
23516 rstyle.cleanConnected = true;
23517 }
23518 }
23519
23520 if (fns) {
23521 for (var i = 0; i < fns.length; i++) {
23522 var fn = fns[i];
23523 fn(willDraw, elesToUpdate);
23524 }
23525 }
23526
23527 r.recalculateRenderedStyle(elesToUpdate);
23528 elesToUpdate = cy.collection();
23529 }
23530 };
23531
23532 r.flushRenderedStyleQueue = function () {
23533 updateEleCalcs(true);
23534 };
23535
23536 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
23537};
23538
23539BRp$8.onUpdateEleCalcs = function (fn) {
23540 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
23541 fns.push(fn);
23542};
23543
23544BRp$8.recalculateRenderedStyle = function (eles, useCache) {
23545 var edges = [];
23546 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
23547
23548 if (this.destroyed) {
23549 return;
23550 } // use cache by default for perf
23551
23552
23553 if (useCache === undefined) {
23554 useCache = true;
23555 }
23556
23557 for (var i = 0; i < eles.length; i++) {
23558 var ele = eles[i];
23559 var _p = ele._private;
23560 var rstyle = _p.rstyle; // only update if dirty and in graph
23561
23562 if (useCache && rstyle.clean || ele.removed()) {
23563 continue;
23564 } // only update if not display: none
23565
23566
23567 if (ele.pstyle('display').value === 'none') {
23568 continue;
23569 }
23570
23571 if (_p.group === 'nodes') {
23572 nodes.push(ele);
23573 } else {
23574 // edges
23575 edges.push(ele);
23576 }
23577
23578 rstyle.clean = true;
23579 } // update node data from projections
23580
23581
23582 for (var i = 0; i < nodes.length; i++) {
23583 var ele = nodes[i];
23584 var _p = ele._private;
23585 var rstyle = _p.rstyle;
23586 var pos = ele.position();
23587 this.recalculateNodeLabelProjection(ele);
23588 rstyle.nodeX = pos.x;
23589 rstyle.nodeY = pos.y;
23590 rstyle.nodeW = ele.pstyle('width').pfValue;
23591 rstyle.nodeH = ele.pstyle('height').pfValue;
23592 }
23593
23594 this.recalculateEdgeProjections(edges); // update edge data from projections
23595
23596 for (var i = 0; i < edges.length; i++) {
23597 var ele = edges[i];
23598 var _p = ele._private;
23599 var rstyle = _p.rstyle;
23600 var rs = _p.rscratch; // update rstyle positions
23601
23602 rstyle.srcX = rs.arrowStartX;
23603 rstyle.srcY = rs.arrowStartY;
23604 rstyle.tgtX = rs.arrowEndX;
23605 rstyle.tgtY = rs.arrowEndY;
23606 rstyle.midX = rs.midX;
23607 rstyle.midY = rs.midY;
23608 rstyle.labelAngle = rs.labelAngle;
23609 rstyle.sourceLabelAngle = rs.sourceLabelAngle;
23610 rstyle.targetLabelAngle = rs.targetLabelAngle;
23611 }
23612};
23613
23614var BRp$9 = {};
23615
23616BRp$9.updateCachedGrabbedEles = function () {
23617 var eles = this.cachedZSortedEles;
23618
23619 if (!eles) {
23620 // just let this be recalculated on the next z sort tick
23621 return;
23622 }
23623
23624 eles.drag = [];
23625 eles.nondrag = [];
23626 var grabTargets = [];
23627
23628 for (var i = 0; i < eles.length; i++) {
23629 var ele = eles[i];
23630 var rs = ele._private.rscratch;
23631
23632 if (ele.grabbed() && !ele.isParent()) {
23633 grabTargets.push(ele);
23634 } else if (rs.inDragLayer) {
23635 eles.drag.push(ele);
23636 } else {
23637 eles.nondrag.push(ele);
23638 }
23639 } // put the grab target nodes last so it's on top of its neighbourhood
23640
23641
23642 for (var i = 0; i < grabTargets.length; i++) {
23643 var ele = grabTargets[i];
23644 eles.drag.push(ele);
23645 }
23646};
23647
23648BRp$9.invalidateCachedZSortedEles = function () {
23649 this.cachedZSortedEles = null;
23650};
23651
23652BRp$9.getCachedZSortedEles = function (forceRecalc) {
23653 if (forceRecalc || !this.cachedZSortedEles) {
23654 var eles = this.cy.mutableElements().toArray();
23655 eles.sort(zIndexSort);
23656 eles.interactive = eles.filter(function (ele) {
23657 return ele.interactive();
23658 });
23659 this.cachedZSortedEles = eles;
23660 this.updateCachedGrabbedEles();
23661 } else {
23662 eles = this.cachedZSortedEles;
23663 }
23664
23665 return eles;
23666};
23667
23668var BRp$a = {};
23669[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
23670 extend(BRp$a, props);
23671});
23672
23673var BRp$b = {};
23674
23675BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
23676 var r = this;
23677 var imageCache = r.imageCache = r.imageCache || {};
23678 var cache = imageCache[url];
23679
23680 if (cache) {
23681 if (!cache.image.complete) {
23682 cache.image.addEventListener('load', onLoad);
23683 }
23684
23685 return cache.image;
23686 } else {
23687 cache = imageCache[url] = imageCache[url] || {};
23688 var image = cache.image = new Image(); // eslint-disable-line no-undef
23689
23690 image.addEventListener('load', onLoad);
23691 image.addEventListener('error', function () {
23692 image.error = true;
23693 }); // #1582 safari doesn't load data uris with crossOrigin properly
23694 // https://bugs.webkit.org/show_bug.cgi?id=123978
23695
23696 var dataUriPrefix = 'data:';
23697 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
23698
23699 if (!isDataUri) {
23700 image.crossOrigin = crossOrigin; // prevent tainted canvas
23701 }
23702
23703 image.src = url;
23704 return image;
23705 }
23706};
23707
23708var BRp$c = {};
23709/* global document, window, ResizeObserver, MutationObserver */
23710
23711BRp$c.registerBinding = function (target, event, handler, useCapture) {
23712 // eslint-disable-line no-unused-vars
23713 var args = Array.prototype.slice.apply(arguments, [1]); // copy
23714
23715 var b = this.binder(target);
23716 return b.on.apply(b, args);
23717};
23718
23719BRp$c.binder = function (tgt) {
23720 var r = this;
23721 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
23722
23723 if (r.supportsPassiveEvents == null) {
23724 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
23725 var supportsPassive = false;
23726
23727 try {
23728 var opts = Object.defineProperty({}, 'passive', {
23729 get: function get() {
23730 supportsPassive = true;
23731 return true;
23732 }
23733 });
23734 window.addEventListener('test', null, opts);
23735 } catch (err) {// not supported
23736 }
23737
23738 r.supportsPassiveEvents = supportsPassive;
23739 }
23740
23741 var on = function on(event, handler, useCapture) {
23742 var args = Array.prototype.slice.call(arguments);
23743
23744 if (tgtIsDom && r.supportsPassiveEvents) {
23745 // replace useCapture w/ opts obj
23746 args[2] = {
23747 capture: useCapture != null ? useCapture : false,
23748 passive: false,
23749 once: false
23750 };
23751 }
23752
23753 r.bindings.push({
23754 target: tgt,
23755 args: args
23756 });
23757 (tgt.addEventListener || tgt.on).apply(tgt, args);
23758 return this;
23759 };
23760
23761 return {
23762 on: on,
23763 addEventListener: on,
23764 addListener: on,
23765 bind: on
23766 };
23767};
23768
23769BRp$c.nodeIsDraggable = function (node) {
23770 return node && node.isNode() && !node.locked() && node.grabbable();
23771};
23772
23773BRp$c.nodeIsGrabbable = function (node) {
23774 return this.nodeIsDraggable(node) && node.interactive();
23775};
23776
23777BRp$c.load = function () {
23778 var r = this;
23779
23780 var isSelected = function isSelected(ele) {
23781 return ele.selected();
23782 };
23783
23784 var triggerEvents = function triggerEvents(target, names, e, position) {
23785 if (target == null) {
23786 target = r.cy;
23787 }
23788
23789 for (var i = 0; i < names.length; i++) {
23790 var name = names[i];
23791 target.emit({
23792 originalEvent: e,
23793 type: name,
23794 position: position
23795 });
23796 }
23797 };
23798
23799 var isMultSelKeyDown = function isMultSelKeyDown(e) {
23800 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
23801 };
23802
23803 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
23804 var allowPassthrough = true;
23805
23806 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
23807 // a grabbable compound node below the ele => no passthrough panning
23808 for (var i = 0; downs && i < downs.length; i++) {
23809 var down = downs[i];
23810
23811 if (down.isNode() && down.isParent()) {
23812 allowPassthrough = false;
23813 break;
23814 }
23815 }
23816 } else {
23817 allowPassthrough = true;
23818 }
23819
23820 return allowPassthrough;
23821 };
23822
23823 var setGrabbed = function setGrabbed(ele) {
23824 ele[0]._private.grabbed = true;
23825 };
23826
23827 var setFreed = function setFreed(ele) {
23828 ele[0]._private.grabbed = false;
23829 };
23830
23831 var setInDragLayer = function setInDragLayer(ele) {
23832 ele[0]._private.rscratch.inDragLayer = true;
23833 };
23834
23835 var setOutDragLayer = function setOutDragLayer(ele) {
23836 ele[0]._private.rscratch.inDragLayer = false;
23837 };
23838
23839 var setGrabTarget = function setGrabTarget(ele) {
23840 ele[0]._private.rscratch.isGrabTarget = true;
23841 };
23842
23843 var removeGrabTarget = function removeGrabTarget(ele) {
23844 ele[0]._private.rscratch.isGrabTarget = false;
23845 };
23846
23847 var addToDragList = function addToDragList(ele, opts) {
23848 var list = opts.addToList;
23849 var listHasEle = list.has(ele);
23850
23851 if (!listHasEle) {
23852 list.merge(ele);
23853 setGrabbed(ele);
23854 }
23855 }; // helper function to determine which child nodes and inner edges
23856 // of a compound node to be dragged as well as the grabbed and selected nodes
23857
23858
23859 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
23860 if (!node.cy().hasCompoundNodes()) {
23861 return;
23862 }
23863
23864 if (opts.inDragLayer == null && opts.addToList == null) {
23865 return;
23866 } // nothing to do
23867
23868
23869 var innerNodes = node.descendants();
23870
23871 if (opts.inDragLayer) {
23872 innerNodes.forEach(setInDragLayer);
23873 innerNodes.connectedEdges().forEach(setInDragLayer);
23874 }
23875
23876 if (opts.addToList) {
23877 opts.addToList.unmerge(innerNodes);
23878 }
23879 }; // adds the given nodes and its neighbourhood to the drag layer
23880
23881
23882 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
23883 opts = opts || {};
23884 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
23885
23886 if (opts.inDragLayer) {
23887 nodes.forEach(setInDragLayer);
23888 nodes.neighborhood().stdFilter(function (ele) {
23889 return !hasCompoundNodes || ele.isEdge();
23890 }).forEach(setInDragLayer);
23891 }
23892
23893 if (opts.addToList) {
23894 nodes.forEach(function (ele) {
23895 addToDragList(ele, opts);
23896 });
23897 }
23898
23899 addDescendantsToDrag(nodes, opts); // always add to drag
23900 // also add nodes and edges related to the topmost ancestor
23901
23902 updateAncestorsInDragLayer(nodes, {
23903 inDragLayer: opts.inDragLayer
23904 });
23905 r.updateCachedGrabbedEles();
23906 };
23907
23908 var addNodeToDrag = addNodesToDrag;
23909
23910 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
23911 if (!grabbedEles) {
23912 return;
23913 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
23914
23915
23916 r.getCachedZSortedEles().forEach(function (ele) {
23917 setFreed(ele);
23918 setOutDragLayer(ele);
23919 removeGrabTarget(ele);
23920 });
23921 r.updateCachedGrabbedEles();
23922 }; // helper function to determine which ancestor nodes and edges should go
23923 // to the drag layer (or should be removed from drag layer).
23924
23925
23926 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
23927 if (opts.inDragLayer == null && opts.addToList == null) {
23928 return;
23929 } // nothing to do
23930
23931
23932 if (!node.cy().hasCompoundNodes()) {
23933 return;
23934 } // find top-level parent
23935
23936
23937 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
23938
23939 if (parent.same(node)) {
23940 return;
23941 }
23942
23943 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
23944 var edges = nodes.connectedEdges();
23945
23946 if (opts.inDragLayer) {
23947 edges.forEach(setInDragLayer);
23948 nodes.forEach(setInDragLayer);
23949 }
23950
23951 if (opts.addToList) {
23952 nodes.forEach(function (ele) {
23953 addToDragList(ele, opts);
23954 });
23955 }
23956 };
23957
23958 var blurActiveDomElement = function blurActiveDomElement() {
23959 if (document.activeElement != null && document.activeElement.blur != null) {
23960 document.activeElement.blur();
23961 }
23962 };
23963
23964 var haveMutationsApi = typeof MutationObserver !== 'undefined';
23965 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
23966
23967 if (haveMutationsApi) {
23968 r.removeObserver = new MutationObserver(function (mutns) {
23969 // eslint-disable-line no-undef
23970 for (var i = 0; i < mutns.length; i++) {
23971 var mutn = mutns[i];
23972 var rNodes = mutn.removedNodes;
23973
23974 if (rNodes) {
23975 for (var j = 0; j < rNodes.length; j++) {
23976 var rNode = rNodes[j];
23977
23978 if (rNode === r.container) {
23979 r.destroy();
23980 break;
23981 }
23982 }
23983 }
23984 }
23985 });
23986
23987 if (r.container.parentNode) {
23988 r.removeObserver.observe(r.container.parentNode, {
23989 childList: true
23990 });
23991 }
23992 } else {
23993 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
23994 // eslint-disable-line no-unused-vars
23995 r.destroy();
23996 });
23997 }
23998
23999 var onResize = util(function () {
24000 r.cy.resize();
24001 }, 100);
24002
24003 if (haveMutationsApi) {
24004 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24005
24006 r.styleObserver.observe(r.container, {
24007 attributes: true
24008 });
24009 } // auto resize
24010
24011
24012 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24013
24014 if (haveResizeObserverApi) {
24015 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24016
24017 r.resizeObserver.observe(r.container);
24018 }
24019
24020 var forEachUp = function forEachUp(domEle, fn) {
24021 while (domEle != null) {
24022 fn(domEle);
24023 domEle = domEle.parentNode;
24024 }
24025 };
24026
24027 var invalidateCoords = function invalidateCoords() {
24028 r.invalidateContainerClientCoordsCache();
24029 };
24030
24031 forEachUp(r.container, function (domEle) {
24032 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24033 r.registerBinding(domEle, 'animationend', invalidateCoords);
24034 r.registerBinding(domEle, 'scroll', invalidateCoords);
24035 }); // stop right click menu from appearing on cy
24036
24037 r.registerBinding(r.container, 'contextmenu', function (e) {
24038 e.preventDefault();
24039 });
24040
24041 var inBoxSelection = function inBoxSelection() {
24042 return r.selection[4] !== 0;
24043 };
24044
24045 var eventInContainer = function eventInContainer(e) {
24046 // save cycles if mouse events aren't to be captured
24047 var containerPageCoords = r.findContainerClientCoords();
24048 var x = containerPageCoords[0];
24049 var y = containerPageCoords[1];
24050 var width = containerPageCoords[2];
24051 var height = containerPageCoords[3];
24052 var positions = e.touches ? e.touches : [e];
24053 var atLeastOnePosInside = false;
24054
24055 for (var i = 0; i < positions.length; i++) {
24056 var p = positions[i];
24057
24058 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24059 atLeastOnePosInside = true;
24060 break;
24061 }
24062 }
24063
24064 if (!atLeastOnePosInside) {
24065 return false;
24066 }
24067
24068 var container = r.container;
24069 var target = e.target;
24070 var tParent = target.parentNode;
24071 var containerIsTarget = false;
24072
24073 while (tParent) {
24074 if (tParent === container) {
24075 containerIsTarget = true;
24076 break;
24077 }
24078
24079 tParent = tParent.parentNode;
24080 }
24081
24082 if (!containerIsTarget) {
24083 return false;
24084 } // if target is outisde cy container, then this event is not for us
24085
24086
24087 return true;
24088 }; // Primary key
24089
24090
24091 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24092 if (!eventInContainer(e)) {
24093 return;
24094 }
24095
24096 e.preventDefault();
24097 blurActiveDomElement();
24098 r.hoverData.capture = true;
24099 r.hoverData.which = e.which;
24100 var cy = r.cy;
24101 var gpos = [e.clientX, e.clientY];
24102 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24103 var select = r.selection;
24104 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24105 var near = nears[0];
24106 var draggedElements = r.dragData.possibleDragElements;
24107 r.hoverData.mdownPos = pos;
24108 r.hoverData.mdownGPos = gpos;
24109
24110 var checkForTaphold = function checkForTaphold() {
24111 r.hoverData.tapholdCancelled = false;
24112 clearTimeout(r.hoverData.tapholdTimeout);
24113 r.hoverData.tapholdTimeout = setTimeout(function () {
24114 if (r.hoverData.tapholdCancelled) {
24115 return;
24116 } else {
24117 var ele = r.hoverData.down;
24118
24119 if (ele) {
24120 ele.emit({
24121 originalEvent: e,
24122 type: 'taphold',
24123 position: {
24124 x: pos[0],
24125 y: pos[1]
24126 }
24127 });
24128 } else {
24129 cy.emit({
24130 originalEvent: e,
24131 type: 'taphold',
24132 position: {
24133 x: pos[0],
24134 y: pos[1]
24135 }
24136 });
24137 }
24138 }
24139 }, r.tapholdDuration);
24140 }; // Right click button
24141
24142
24143 if (e.which == 3) {
24144 r.hoverData.cxtStarted = true;
24145 var cxtEvt = {
24146 originalEvent: e,
24147 type: 'cxttapstart',
24148 position: {
24149 x: pos[0],
24150 y: pos[1]
24151 }
24152 };
24153
24154 if (near) {
24155 near.activate();
24156 near.emit(cxtEvt);
24157 r.hoverData.down = near;
24158 } else {
24159 cy.emit(cxtEvt);
24160 }
24161
24162 r.hoverData.downTime = new Date().getTime();
24163 r.hoverData.cxtDragged = false; // Primary button
24164 } else if (e.which == 1) {
24165 if (near) {
24166 near.activate();
24167 } // Element dragging
24168
24169
24170 {
24171 // If something is under the cursor and it is draggable, prepare to grab it
24172 if (near != null) {
24173 if (r.nodeIsGrabbable(near)) {
24174 var makeEvent = function makeEvent(type) {
24175 return {
24176 originalEvent: e,
24177 type: type,
24178 position: {
24179 x: pos[0],
24180 y: pos[1]
24181 }
24182 };
24183 };
24184
24185 var triggerGrab = function triggerGrab(ele) {
24186 ele.emit(makeEvent('grab'));
24187 };
24188
24189 setGrabTarget(near);
24190
24191 if (!near.selected()) {
24192 draggedElements = r.dragData.possibleDragElements = cy.collection();
24193 addNodeToDrag(near, {
24194 addToList: draggedElements
24195 });
24196 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24197 } else {
24198 draggedElements = r.dragData.possibleDragElements = cy.collection();
24199 var selectedNodes = cy.$(function (ele) {
24200 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24201 });
24202 addNodesToDrag(selectedNodes, {
24203 addToList: draggedElements
24204 });
24205 near.emit(makeEvent('grabon'));
24206 selectedNodes.forEach(triggerGrab);
24207 }
24208
24209 r.redrawHint('eles', true);
24210 r.redrawHint('drag', true);
24211 }
24212 }
24213
24214 r.hoverData.down = near;
24215 r.hoverData.downs = nears;
24216 r.hoverData.downTime = new Date().getTime();
24217 }
24218 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24219 x: pos[0],
24220 y: pos[1]
24221 });
24222
24223 if (near == null) {
24224 select[4] = 1;
24225 r.data.bgActivePosistion = {
24226 x: pos[0],
24227 y: pos[1]
24228 };
24229 r.redrawHint('select', true);
24230 r.redraw();
24231 } else if (near.pannable()) {
24232 select[4] = 1; // for future pan
24233 }
24234
24235 checkForTaphold();
24236 } // Initialize selection box coordinates
24237
24238
24239 select[0] = select[2] = pos[0];
24240 select[1] = select[3] = pos[1];
24241 }, false);
24242 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24243 // eslint-disable-line no-undef
24244 var capture = r.hoverData.capture;
24245
24246 if (!capture && !eventInContainer(e)) {
24247 return;
24248 }
24249
24250 var preventDefault = false;
24251 var cy = r.cy;
24252 var zoom = cy.zoom();
24253 var gpos = [e.clientX, e.clientY];
24254 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24255 var mdownPos = r.hoverData.mdownPos;
24256 var mdownGPos = r.hoverData.mdownGPos;
24257 var select = r.selection;
24258 var near = null;
24259
24260 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24261 near = r.findNearestElement(pos[0], pos[1], true, false);
24262 }
24263
24264 var last = r.hoverData.last;
24265 var down = r.hoverData.down;
24266 var disp = [pos[0] - select[2], pos[1] - select[3]];
24267 var draggedElements = r.dragData.possibleDragElements;
24268 var isOverThresholdDrag;
24269
24270 if (mdownGPos) {
24271 var dx = gpos[0] - mdownGPos[0];
24272 var dx2 = dx * dx;
24273 var dy = gpos[1] - mdownGPos[1];
24274 var dy2 = dy * dy;
24275 var dist2 = dx2 + dy2;
24276 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24277 }
24278
24279 var multSelKeyDown = isMultSelKeyDown(e);
24280
24281 if (isOverThresholdDrag) {
24282 r.hoverData.tapholdCancelled = true;
24283 }
24284
24285 var updateDragDelta = function updateDragDelta() {
24286 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24287
24288 if (dragDelta.length === 0) {
24289 dragDelta.push(disp[0]);
24290 dragDelta.push(disp[1]);
24291 } else {
24292 dragDelta[0] += disp[0];
24293 dragDelta[1] += disp[1];
24294 }
24295 };
24296
24297 preventDefault = true;
24298 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24299 x: pos[0],
24300 y: pos[1]
24301 });
24302
24303 var goIntoBoxMode = function goIntoBoxMode() {
24304 r.data.bgActivePosistion = undefined;
24305
24306 if (!r.hoverData.selecting) {
24307 cy.emit({
24308 originalEvent: e,
24309 type: 'boxstart',
24310 position: {
24311 x: pos[0],
24312 y: pos[1]
24313 }
24314 });
24315 }
24316
24317 select[4] = 1;
24318 r.hoverData.selecting = true;
24319 r.redrawHint('select', true);
24320 r.redraw();
24321 }; // trigger context drag if rmouse down
24322
24323
24324 if (r.hoverData.which === 3) {
24325 // but only if over threshold
24326 if (isOverThresholdDrag) {
24327 var cxtEvt = {
24328 originalEvent: e,
24329 type: 'cxtdrag',
24330 position: {
24331 x: pos[0],
24332 y: pos[1]
24333 }
24334 };
24335
24336 if (down) {
24337 down.emit(cxtEvt);
24338 } else {
24339 cy.emit(cxtEvt);
24340 }
24341
24342 r.hoverData.cxtDragged = true;
24343
24344 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24345 if (r.hoverData.cxtOver) {
24346 r.hoverData.cxtOver.emit({
24347 originalEvent: e,
24348 type: 'cxtdragout',
24349 position: {
24350 x: pos[0],
24351 y: pos[1]
24352 }
24353 });
24354 }
24355
24356 r.hoverData.cxtOver = near;
24357
24358 if (near) {
24359 near.emit({
24360 originalEvent: e,
24361 type: 'cxtdragover',
24362 position: {
24363 x: pos[0],
24364 y: pos[1]
24365 }
24366 });
24367 }
24368 }
24369 } // Check if we are drag panning the entire graph
24370
24371 } else if (r.hoverData.dragging) {
24372 preventDefault = true;
24373
24374 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24375 var deltaP;
24376
24377 if (r.hoverData.justStartedPan) {
24378 var mdPos = r.hoverData.mdownPos;
24379 deltaP = {
24380 x: (pos[0] - mdPos[0]) * zoom,
24381 y: (pos[1] - mdPos[1]) * zoom
24382 };
24383 r.hoverData.justStartedPan = false;
24384 } else {
24385 deltaP = {
24386 x: disp[0] * zoom,
24387 y: disp[1] * zoom
24388 };
24389 }
24390
24391 cy.panBy(deltaP);
24392 r.hoverData.dragged = true;
24393 } // Needs reproject due to pan changing viewport
24394
24395
24396 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24397 } else if (select[4] == 1 && (down == null || down.pannable())) {
24398 if (isOverThresholdDrag) {
24399 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24400 goIntoBoxMode();
24401 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24402 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24403
24404 if (allowPassthrough) {
24405 r.hoverData.dragging = true;
24406 r.hoverData.justStartedPan = true;
24407 select[4] = 0;
24408 r.data.bgActivePosistion = array2point(mdownPos);
24409 r.redrawHint('select', true);
24410 r.redraw();
24411 }
24412 }
24413
24414 if (down && down.pannable() && down.active()) {
24415 down.unactivate();
24416 }
24417 }
24418 } else {
24419 if (down && down.pannable() && down.active()) {
24420 down.unactivate();
24421 }
24422
24423 if ((!down || !down.grabbed()) && near != last) {
24424 if (last) {
24425 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24426 x: pos[0],
24427 y: pos[1]
24428 });
24429 }
24430
24431 if (near) {
24432 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24433 x: pos[0],
24434 y: pos[1]
24435 });
24436 }
24437
24438 r.hoverData.last = near;
24439 }
24440
24441 if (down) {
24442 if (isOverThresholdDrag) {
24443 // then we can take action
24444 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24445 // then selection overrides
24446 if (down && down.grabbed()) {
24447 freeDraggedElements(draggedElements);
24448 down.emit('freeon');
24449 draggedElements.emit('free');
24450
24451 if (r.dragData.didDrag) {
24452 down.emit('dragfreeon');
24453 draggedElements.emit('dragfree');
24454 }
24455 }
24456
24457 goIntoBoxMode();
24458 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24459 // drag node
24460 var justStartedDrag = !r.dragData.didDrag;
24461
24462 if (justStartedDrag) {
24463 r.redrawHint('eles', true);
24464 }
24465
24466 r.dragData.didDrag = true; // indicate that we actually did drag the node
24467
24468 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24469
24470 if (!r.hoverData.draggingEles) {
24471 addNodesToDrag(draggedElements, {
24472 inDragLayer: true
24473 });
24474 }
24475
24476 var totalShift = {
24477 x: 0,
24478 y: 0
24479 };
24480
24481 if (number(disp[0]) && number(disp[1])) {
24482 totalShift.x += disp[0];
24483 totalShift.y += disp[1];
24484
24485 if (justStartedDrag) {
24486 var dragDelta = r.hoverData.dragDelta;
24487
24488 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24489 totalShift.x += dragDelta[0];
24490 totalShift.y += dragDelta[1];
24491 }
24492 }
24493 }
24494
24495 for (var i = 0; i < draggedElements.length; i++) {
24496 var dEle = draggedElements[i];
24497
24498 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24499 toTrigger.merge(dEle);
24500 }
24501 }
24502
24503 r.hoverData.draggingEles = true;
24504 toTrigger.silentShift(totalShift).emit('position drag');
24505 r.redrawHint('drag', true);
24506 r.redraw();
24507 }
24508 } else {
24509 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24510 updateDragDelta();
24511 }
24512 } // prevent the dragging from triggering text selection on the page
24513
24514
24515 preventDefault = true;
24516 }
24517
24518 select[2] = pos[0];
24519 select[3] = pos[1];
24520
24521 if (preventDefault) {
24522 if (e.stopPropagation) e.stopPropagation();
24523 if (e.preventDefault) e.preventDefault();
24524 return false;
24525 }
24526 }, false);
24527 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24528 // eslint-disable-line no-undef
24529 var capture = r.hoverData.capture;
24530
24531 if (!capture) {
24532 return;
24533 }
24534
24535 r.hoverData.capture = false;
24536 var cy = r.cy;
24537 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24538 var select = r.selection;
24539 var near = r.findNearestElement(pos[0], pos[1], true, false);
24540 var draggedElements = r.dragData.possibleDragElements;
24541 var down = r.hoverData.down;
24542 var multSelKeyDown = isMultSelKeyDown(e);
24543
24544 if (r.data.bgActivePosistion) {
24545 r.redrawHint('select', true);
24546 r.redraw();
24547 }
24548
24549 r.hoverData.tapholdCancelled = true;
24550 r.data.bgActivePosistion = undefined; // not active bg now
24551
24552 if (down) {
24553 down.unactivate();
24554 }
24555
24556 if (r.hoverData.which === 3) {
24557 var cxtEvt = {
24558 originalEvent: e,
24559 type: 'cxttapend',
24560 position: {
24561 x: pos[0],
24562 y: pos[1]
24563 }
24564 };
24565
24566 if (down) {
24567 down.emit(cxtEvt);
24568 } else {
24569 cy.emit(cxtEvt);
24570 }
24571
24572 if (!r.hoverData.cxtDragged) {
24573 var cxtTap = {
24574 originalEvent: e,
24575 type: 'cxttap',
24576 position: {
24577 x: pos[0],
24578 y: pos[1]
24579 }
24580 };
24581
24582 if (down) {
24583 down.emit(cxtTap);
24584 } else {
24585 cy.emit(cxtTap);
24586 }
24587 }
24588
24589 r.hoverData.cxtDragged = false;
24590 r.hoverData.which = null;
24591 } else if (r.hoverData.which === 1) {
24592 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24593 x: pos[0],
24594 y: pos[1]
24595 });
24596
24597 if (!r.dragData.didDrag // didn't move a node around
24598 && !r.hoverData.dragged // didn't pan
24599 && !r.hoverData.selecting // not box selection
24600 && !r.hoverData.isOverThresholdDrag // didn't move too much
24601 ) {
24602 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24603 x: pos[0],
24604 y: pos[1]
24605 });
24606 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24607
24608
24609 if (down == null && // not mousedown on node
24610 !r.dragData.didDrag // didn't move the node around
24611 && !r.hoverData.selecting // not box selection
24612 && !r.hoverData.dragged // didn't pan
24613 && !isMultSelKeyDown(e)) {
24614 cy.$(isSelected).unselect(['tapunselect']);
24615
24616 if (draggedElements.length > 0) {
24617 r.redrawHint('eles', true);
24618 }
24619
24620 r.dragData.possibleDragElements = draggedElements = cy.collection();
24621 } // Single selection
24622
24623
24624 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24625 if (near != null && near._private.selectable) {
24626 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24627 if (near.selected()) {
24628 near.unselect(['tapunselect']);
24629 } else {
24630 near.select(['tapselect']);
24631 }
24632 } else {
24633 if (!multSelKeyDown) {
24634 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
24635 near.select(['tapselect']);
24636 }
24637 }
24638
24639 r.redrawHint('eles', true);
24640 }
24641 }
24642
24643 if (r.hoverData.selecting) {
24644 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
24645 r.redrawHint('select', true);
24646
24647 if (box.length > 0) {
24648 r.redrawHint('eles', true);
24649 }
24650
24651 cy.emit({
24652 type: 'boxend',
24653 originalEvent: e,
24654 position: {
24655 x: pos[0],
24656 y: pos[1]
24657 }
24658 });
24659
24660 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
24661 return ele.selectable() && !ele.selected();
24662 };
24663
24664 if (cy.selectionType() === 'additive') {
24665 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
24666 } else {
24667 if (!multSelKeyDown) {
24668 cy.$(isSelected).unmerge(box).unselect();
24669 }
24670
24671 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
24672 } // always need redraw in case eles unselectable
24673
24674
24675 r.redraw();
24676 } // Cancel drag pan
24677
24678
24679 if (r.hoverData.dragging) {
24680 r.hoverData.dragging = false;
24681 r.redrawHint('select', true);
24682 r.redrawHint('eles', true);
24683 r.redraw();
24684 }
24685
24686 if (!select[4]) {
24687 r.redrawHint('drag', true);
24688 r.redrawHint('eles', true);
24689 var downWasGrabbed = down && down.grabbed();
24690 freeDraggedElements(draggedElements);
24691
24692 if (downWasGrabbed) {
24693 down.emit('freeon');
24694 draggedElements.emit('free');
24695
24696 if (r.dragData.didDrag) {
24697 down.emit('dragfreeon');
24698 draggedElements.emit('dragfree');
24699 }
24700 }
24701 }
24702 } // else not right mouse
24703
24704
24705 select[4] = 0;
24706 r.hoverData.down = null;
24707 r.hoverData.cxtStarted = false;
24708 r.hoverData.draggingEles = false;
24709 r.hoverData.selecting = false;
24710 r.hoverData.isOverThresholdDrag = false;
24711 r.dragData.didDrag = false;
24712 r.hoverData.dragged = false;
24713 r.hoverData.dragDelta = [];
24714 r.hoverData.mdownPos = null;
24715 r.hoverData.mdownGPos = null;
24716 }, false);
24717
24718 var wheelHandler = function wheelHandler(e) {
24719 if (r.scrollingPage) {
24720 return;
24721 } // while scrolling, ignore wheel-to-zoom
24722
24723
24724 var cy = r.cy;
24725 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24726 var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y];
24727
24728 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
24729 // if pan dragging or cxt dragging, wheel movements make no zoom
24730 e.preventDefault();
24731 return;
24732 }
24733
24734 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
24735 e.preventDefault();
24736 r.data.wheelZooming = true;
24737 clearTimeout(r.data.wheelTimeout);
24738 r.data.wheelTimeout = setTimeout(function () {
24739 r.data.wheelZooming = false;
24740 r.redrawHint('eles', true);
24741 r.redraw();
24742 }, 150);
24743 var diff;
24744
24745 if (e.deltaY != null) {
24746 diff = e.deltaY / -250;
24747 } else if (e.wheelDeltaY != null) {
24748 diff = e.wheelDeltaY / 1000;
24749 } else {
24750 diff = e.wheelDelta / 1000;
24751 }
24752
24753 diff = diff * r.wheelSensitivity;
24754 var needsWheelFix = e.deltaMode === 1;
24755
24756 if (needsWheelFix) {
24757 // fixes slow wheel events on ff/linux and ff/windows
24758 diff *= 33;
24759 }
24760
24761 cy.zoom({
24762 level: cy.zoom() * Math.pow(10, diff),
24763 renderedPosition: {
24764 x: rpos[0],
24765 y: rpos[1]
24766 }
24767 });
24768 }
24769 }; // Functions to help with whether mouse wheel should trigger zooming
24770 // --
24771
24772
24773 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
24774 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
24775 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
24776 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
24777
24778 r.registerBinding(window, 'scroll', function scrollHandler(e) {
24779 // eslint-disable-line no-unused-vars
24780 r.scrollingPage = true;
24781 clearTimeout(r.scrollingPageTimeout);
24782 r.scrollingPageTimeout = setTimeout(function () {
24783 r.scrollingPage = false;
24784 }, 250);
24785 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
24786 // Handle mouseout on Cytoscape container
24787
24788 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
24789 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24790 r.cy.emit({
24791 originalEvent: e,
24792 type: 'mouseout',
24793 position: {
24794 x: pos[0],
24795 y: pos[1]
24796 }
24797 });
24798 }, false);
24799 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
24800 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24801 r.cy.emit({
24802 originalEvent: e,
24803 type: 'mouseover',
24804 position: {
24805 x: pos[0],
24806 y: pos[1]
24807 }
24808 });
24809 }, false);
24810 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
24811
24812 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
24813
24814 var center1, modelCenter1; // center point on start pinch to zoom
24815
24816 var offsetLeft, offsetTop;
24817 var containerWidth, containerHeight;
24818 var twoFingersStartInside;
24819
24820 var distance = function distance(x1, y1, x2, y2) {
24821 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
24822 };
24823
24824 var distanceSq = function distanceSq(x1, y1, x2, y2) {
24825 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
24826 };
24827
24828 var touchstartHandler;
24829 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
24830 if (!eventInContainer(e)) {
24831 return;
24832 }
24833
24834 blurActiveDomElement();
24835 r.touchData.capture = true;
24836 r.data.bgActivePosistion = undefined;
24837 var cy = r.cy;
24838 var now = r.touchData.now;
24839 var earlier = r.touchData.earlier;
24840
24841 if (e.touches[0]) {
24842 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
24843 now[0] = pos[0];
24844 now[1] = pos[1];
24845 }
24846
24847 if (e.touches[1]) {
24848 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
24849 now[2] = pos[0];
24850 now[3] = pos[1];
24851 }
24852
24853 if (e.touches[2]) {
24854 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
24855 now[4] = pos[0];
24856 now[5] = pos[1];
24857 } // record starting points for pinch-to-zoom
24858
24859
24860 if (e.touches[1]) {
24861 r.touchData.singleTouchMoved = true;
24862 freeDraggedElements(r.dragData.touchDragEles);
24863 var offsets = r.findContainerClientCoords();
24864 offsetLeft = offsets[0];
24865 offsetTop = offsets[1];
24866 containerWidth = offsets[2];
24867 containerHeight = offsets[3];
24868 f1x1 = e.touches[0].clientX - offsetLeft;
24869 f1y1 = e.touches[0].clientY - offsetTop;
24870 f2x1 = e.touches[1].clientX - offsetLeft;
24871 f2y1 = e.touches[1].clientY - offsetTop;
24872 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
24873 var pan = cy.pan();
24874 var zoom = cy.zoom();
24875 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
24876 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
24877 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
24878 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
24879
24880 var cxtDistThreshold = 200;
24881 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
24882
24883 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
24884 var near1 = r.findNearestElement(now[0], now[1], true, true);
24885 var near2 = r.findNearestElement(now[2], now[3], true, true);
24886
24887 if (near1 && near1.isNode()) {
24888 near1.activate().emit({
24889 originalEvent: e,
24890 type: 'cxttapstart',
24891 position: {
24892 x: now[0],
24893 y: now[1]
24894 }
24895 });
24896 r.touchData.start = near1;
24897 } else if (near2 && near2.isNode()) {
24898 near2.activate().emit({
24899 originalEvent: e,
24900 type: 'cxttapstart',
24901 position: {
24902 x: now[0],
24903 y: now[1]
24904 }
24905 });
24906 r.touchData.start = near2;
24907 } else {
24908 cy.emit({
24909 originalEvent: e,
24910 type: 'cxttapstart',
24911 position: {
24912 x: now[0],
24913 y: now[1]
24914 }
24915 });
24916 }
24917
24918 if (r.touchData.start) {
24919 r.touchData.start._private.grabbed = false;
24920 }
24921
24922 r.touchData.cxt = true;
24923 r.touchData.cxtDragged = false;
24924 r.data.bgActivePosistion = undefined;
24925 r.redraw();
24926 return;
24927 }
24928 }
24929
24930 if (e.touches[2]) {
24931 // ignore
24932 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
24933 if (cy.boxSelectionEnabled()) {
24934 e.preventDefault();
24935 }
24936 } else if (e.touches[1]) ; else if (e.touches[0]) {
24937 var nears = r.findNearestElements(now[0], now[1], true, true);
24938 var near = nears[0];
24939
24940 if (near != null) {
24941 near.activate();
24942 r.touchData.start = near;
24943 r.touchData.starts = nears;
24944
24945 if (r.nodeIsGrabbable(near)) {
24946 var draggedEles = r.dragData.touchDragEles = cy.collection();
24947 var selectedNodes = null;
24948 r.redrawHint('eles', true);
24949 r.redrawHint('drag', true);
24950
24951 if (near.selected()) {
24952 // reset drag elements, since near will be added again
24953 selectedNodes = cy.$(function (ele) {
24954 return ele.selected() && r.nodeIsGrabbable(ele);
24955 });
24956 addNodesToDrag(selectedNodes, {
24957 addToList: draggedEles
24958 });
24959 } else {
24960 addNodeToDrag(near, {
24961 addToList: draggedEles
24962 });
24963 }
24964
24965 setGrabTarget(near);
24966
24967 var makeEvent = function makeEvent(type) {
24968 return {
24969 originalEvent: e,
24970 type: type,
24971 position: {
24972 x: now[0],
24973 y: now[1]
24974 }
24975 };
24976 };
24977
24978 near.emit(makeEvent('grabon'));
24979
24980 if (selectedNodes) {
24981 selectedNodes.forEach(function (n) {
24982 n.emit(makeEvent('grab'));
24983 });
24984 } else {
24985 near.emit(makeEvent('grab'));
24986 }
24987 }
24988 }
24989
24990 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
24991 x: now[0],
24992 y: now[1]
24993 });
24994
24995 if (near == null) {
24996 r.data.bgActivePosistion = {
24997 x: pos[0],
24998 y: pos[1]
24999 };
25000 r.redrawHint('select', true);
25001 r.redraw();
25002 } // Tap, taphold
25003 // -----
25004
25005
25006 r.touchData.singleTouchMoved = false;
25007 r.touchData.singleTouchStartTime = +new Date();
25008 clearTimeout(r.touchData.tapholdTimeout);
25009 r.touchData.tapholdTimeout = setTimeout(function () {
25010 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25011 && !r.touchData.selecting // box selection shouldn't allow taphold through
25012 ) {
25013 triggerEvents(r.touchData.start, ['taphold'], e, {
25014 x: now[0],
25015 y: now[1]
25016 });
25017 }
25018 }, r.tapholdDuration);
25019 }
25020
25021 if (e.touches.length >= 1) {
25022 var sPos = r.touchData.startPosition = [];
25023
25024 for (var i = 0; i < now.length; i++) {
25025 sPos[i] = earlier[i] = now[i];
25026 }
25027
25028 var touch0 = e.touches[0];
25029 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25030 }
25031 }, false);
25032 var touchmoveHandler;
25033 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25034 // eslint-disable-line no-undef
25035 var capture = r.touchData.capture;
25036
25037 if (!capture && !eventInContainer(e)) {
25038 return;
25039 }
25040
25041 var select = r.selection;
25042 var cy = r.cy;
25043 var now = r.touchData.now;
25044 var earlier = r.touchData.earlier;
25045 var zoom = cy.zoom();
25046
25047 if (e.touches[0]) {
25048 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25049 now[0] = pos[0];
25050 now[1] = pos[1];
25051 }
25052
25053 if (e.touches[1]) {
25054 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25055 now[2] = pos[0];
25056 now[3] = pos[1];
25057 }
25058
25059 if (e.touches[2]) {
25060 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25061 now[4] = pos[0];
25062 now[5] = pos[1];
25063 }
25064
25065 var startGPos = r.touchData.startGPosition;
25066 var isOverThresholdDrag;
25067
25068 if (capture && e.touches[0] && startGPos) {
25069 var disp = [];
25070
25071 for (var j = 0; j < now.length; j++) {
25072 disp[j] = now[j] - earlier[j];
25073 }
25074
25075 var dx = e.touches[0].clientX - startGPos[0];
25076 var dx2 = dx * dx;
25077 var dy = e.touches[0].clientY - startGPos[1];
25078 var dy2 = dy * dy;
25079 var dist2 = dx2 + dy2;
25080 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25081 } // context swipe cancelling
25082
25083
25084 if (capture && r.touchData.cxt) {
25085 e.preventDefault();
25086 var f1x2 = e.touches[0].clientX - offsetLeft,
25087 f1y2 = e.touches[0].clientY - offsetTop;
25088 var f2x2 = e.touches[1].clientX - offsetLeft,
25089 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25090
25091 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25092 var factorSq = distance2Sq / distance1Sq;
25093 var distThreshold = 150;
25094 var distThresholdSq = distThreshold * distThreshold;
25095 var factorThreshold = 1.5;
25096 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25097
25098 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25099 r.touchData.cxt = false;
25100 r.data.bgActivePosistion = undefined;
25101 r.redrawHint('select', true);
25102 var cxtEvt = {
25103 originalEvent: e,
25104 type: 'cxttapend',
25105 position: {
25106 x: now[0],
25107 y: now[1]
25108 }
25109 };
25110
25111 if (r.touchData.start) {
25112 r.touchData.start.unactivate().emit(cxtEvt);
25113 r.touchData.start = null;
25114 } else {
25115 cy.emit(cxtEvt);
25116 }
25117 }
25118 } // context swipe
25119
25120
25121 if (capture && r.touchData.cxt) {
25122 var cxtEvt = {
25123 originalEvent: e,
25124 type: 'cxtdrag',
25125 position: {
25126 x: now[0],
25127 y: now[1]
25128 }
25129 };
25130 r.data.bgActivePosistion = undefined;
25131 r.redrawHint('select', true);
25132
25133 if (r.touchData.start) {
25134 r.touchData.start.emit(cxtEvt);
25135 } else {
25136 cy.emit(cxtEvt);
25137 }
25138
25139 if (r.touchData.start) {
25140 r.touchData.start._private.grabbed = false;
25141 }
25142
25143 r.touchData.cxtDragged = true;
25144 var near = r.findNearestElement(now[0], now[1], true, true);
25145
25146 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25147 if (r.touchData.cxtOver) {
25148 r.touchData.cxtOver.emit({
25149 originalEvent: e,
25150 type: 'cxtdragout',
25151 position: {
25152 x: now[0],
25153 y: now[1]
25154 }
25155 });
25156 }
25157
25158 r.touchData.cxtOver = near;
25159
25160 if (near) {
25161 near.emit({
25162 originalEvent: e,
25163 type: 'cxtdragover',
25164 position: {
25165 x: now[0],
25166 y: now[1]
25167 }
25168 });
25169 }
25170 } // box selection
25171
25172 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25173 e.preventDefault();
25174 r.data.bgActivePosistion = undefined;
25175 this.lastThreeTouch = +new Date();
25176
25177 if (!r.touchData.selecting) {
25178 cy.emit({
25179 originalEvent: e,
25180 type: 'boxstart',
25181 position: {
25182 x: now[0],
25183 y: now[1]
25184 }
25185 });
25186 }
25187
25188 r.touchData.selecting = true;
25189 r.touchData.didSelect = true;
25190 select[4] = 1;
25191
25192 if (!select || select.length === 0 || select[0] === undefined) {
25193 select[0] = (now[0] + now[2] + now[4]) / 3;
25194 select[1] = (now[1] + now[3] + now[5]) / 3;
25195 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25196 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25197 } else {
25198 select[2] = (now[0] + now[2] + now[4]) / 3;
25199 select[3] = (now[1] + now[3] + now[5]) / 3;
25200 }
25201
25202 r.redrawHint('select', true);
25203 r.redraw(); // pinch to zoom
25204 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25205 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25206 // two fingers => pinch to zoom
25207 e.preventDefault();
25208 r.data.bgActivePosistion = undefined;
25209 r.redrawHint('select', true);
25210 var draggedEles = r.dragData.touchDragEles;
25211
25212 if (draggedEles) {
25213 r.redrawHint('drag', true);
25214
25215 for (var i = 0; i < draggedEles.length; i++) {
25216 var de_p = draggedEles[i]._private;
25217 de_p.grabbed = false;
25218 de_p.rscratch.inDragLayer = false;
25219 }
25220 }
25221
25222 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25223
25224 var f1x2 = e.touches[0].clientX - offsetLeft,
25225 f1y2 = e.touches[0].clientY - offsetTop;
25226 var f2x2 = e.touches[1].clientX - offsetLeft,
25227 f2y2 = e.touches[1].clientY - offsetTop;
25228 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25229 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25230
25231 var factor = distance2 / distance1;
25232
25233 if (twoFingersStartInside) {
25234 // delta finger1
25235 var df1x = f1x2 - f1x1;
25236 var df1y = f1y2 - f1y1; // delta finger 2
25237
25238 var df2x = f2x2 - f2x1;
25239 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25240 // i.e. so pinching cancels out and moving together pans
25241
25242 var tx = (df1x + df2x) / 2;
25243 var ty = (df1y + df2y) / 2; // now calculate the zoom
25244
25245 var zoom1 = cy.zoom();
25246 var zoom2 = zoom1 * factor;
25247 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25248
25249 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25250 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25251 var pan2 = {
25252 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25253 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25254 }; // remove dragged eles
25255
25256 if (_start && _start.active()) {
25257 var draggedEles = r.dragData.touchDragEles;
25258 freeDraggedElements(draggedEles);
25259 r.redrawHint('drag', true);
25260 r.redrawHint('eles', true);
25261
25262 _start.unactivate().emit('freeon');
25263
25264 draggedEles.emit('free');
25265
25266 if (r.dragData.didDrag) {
25267 _start.emit('dragfreeon');
25268
25269 draggedEles.emit('dragfree');
25270 }
25271 }
25272
25273 cy.viewport({
25274 zoom: zoom2,
25275 pan: pan2,
25276 cancelOnFailedZoom: true
25277 });
25278 distance1 = distance2;
25279 f1x1 = f1x2;
25280 f1y1 = f1y2;
25281 f2x1 = f2x2;
25282 f2y1 = f2y2;
25283 r.pinching = true;
25284 } // Re-project
25285
25286
25287 if (e.touches[0]) {
25288 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25289 now[0] = pos[0];
25290 now[1] = pos[1];
25291 }
25292
25293 if (e.touches[1]) {
25294 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25295 now[2] = pos[0];
25296 now[3] = pos[1];
25297 }
25298
25299 if (e.touches[2]) {
25300 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25301 now[4] = pos[0];
25302 now[5] = pos[1];
25303 }
25304 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25305 ) {
25306 var start = r.touchData.start;
25307 var last = r.touchData.last;
25308 var near;
25309
25310 if (!r.hoverData.draggingEles && !r.swipePanning) {
25311 near = r.findNearestElement(now[0], now[1], true, true);
25312 }
25313
25314 if (capture && start != null) {
25315 e.preventDefault();
25316 } // dragging nodes
25317
25318
25319 if (capture && start != null && r.nodeIsDraggable(start)) {
25320 if (isOverThresholdDrag) {
25321 // then dragging can happen
25322 var draggedEles = r.dragData.touchDragEles;
25323 var justStartedDrag = !r.dragData.didDrag;
25324
25325 if (justStartedDrag) {
25326 addNodesToDrag(draggedEles, {
25327 inDragLayer: true
25328 });
25329 }
25330
25331 r.dragData.didDrag = true;
25332 var totalShift = {
25333 x: 0,
25334 y: 0
25335 };
25336
25337 if (number(disp[0]) && number(disp[1])) {
25338 totalShift.x += disp[0];
25339 totalShift.y += disp[1];
25340
25341 if (justStartedDrag) {
25342 r.redrawHint('eles', true);
25343 var dragDelta = r.touchData.dragDelta;
25344
25345 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25346 totalShift.x += dragDelta[0];
25347 totalShift.y += dragDelta[1];
25348 }
25349 }
25350 }
25351
25352 r.hoverData.draggingEles = true;
25353 draggedEles.silentShift(totalShift).emit('position drag');
25354 r.redrawHint('drag', true);
25355
25356 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25357 r.redrawHint('eles', true);
25358 }
25359
25360 r.redraw();
25361 } else {
25362 // otherise keep track of drag delta for later
25363 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25364
25365 if (dragDelta.length === 0) {
25366 dragDelta.push(disp[0]);
25367 dragDelta.push(disp[1]);
25368 } else {
25369 dragDelta[0] += disp[0];
25370 dragDelta[1] += disp[1];
25371 }
25372 }
25373 } // touchmove
25374
25375
25376 {
25377 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25378 x: now[0],
25379 y: now[1]
25380 });
25381
25382 if ((!start || !start.grabbed()) && near != last) {
25383 if (last) {
25384 last.emit({
25385 originalEvent: e,
25386 type: 'tapdragout',
25387 position: {
25388 x: now[0],
25389 y: now[1]
25390 }
25391 });
25392 }
25393
25394 if (near) {
25395 near.emit({
25396 originalEvent: e,
25397 type: 'tapdragover',
25398 position: {
25399 x: now[0],
25400 y: now[1]
25401 }
25402 });
25403 }
25404 }
25405
25406 r.touchData.last = near;
25407 } // check to cancel taphold
25408
25409 if (capture) {
25410 for (var i = 0; i < now.length; i++) {
25411 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25412 r.touchData.singleTouchMoved = true;
25413 }
25414 }
25415 } // panning
25416
25417
25418 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25419 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25420
25421 if (allowPassthrough) {
25422 e.preventDefault();
25423
25424 if (!r.data.bgActivePosistion) {
25425 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25426 }
25427
25428 if (r.swipePanning) {
25429 cy.panBy({
25430 x: disp[0] * zoom,
25431 y: disp[1] * zoom
25432 });
25433 } else if (isOverThresholdDrag) {
25434 r.swipePanning = true;
25435 cy.panBy({
25436 x: dx * zoom,
25437 y: dy * zoom
25438 });
25439
25440 if (start) {
25441 start.unactivate();
25442 r.redrawHint('select', true);
25443 r.touchData.start = null;
25444 }
25445 }
25446 } // Re-project
25447
25448
25449 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25450 now[0] = pos[0];
25451 now[1] = pos[1];
25452 }
25453 }
25454
25455 for (var j = 0; j < now.length; j++) {
25456 earlier[j] = now[j];
25457 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25458
25459
25460 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25461 r.data.bgActivePosistion = undefined;
25462 r.redrawHint('select', true);
25463 r.redraw();
25464 }
25465 }, false);
25466 var touchcancelHandler;
25467 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25468 // eslint-disable-line no-unused-vars
25469 var start = r.touchData.start;
25470 r.touchData.capture = false;
25471
25472 if (start) {
25473 start.unactivate();
25474 }
25475 });
25476 var touchendHandler;
25477 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25478 // eslint-disable-line no-unused-vars
25479 var start = r.touchData.start;
25480 var capture = r.touchData.capture;
25481
25482 if (capture) {
25483 if (e.touches.length === 0) {
25484 r.touchData.capture = false;
25485 }
25486
25487 e.preventDefault();
25488 } else {
25489 return;
25490 }
25491
25492 var select = r.selection;
25493 r.swipePanning = false;
25494 r.hoverData.draggingEles = false;
25495 var cy = r.cy;
25496 var zoom = cy.zoom();
25497 var now = r.touchData.now;
25498 var earlier = r.touchData.earlier;
25499
25500 if (e.touches[0]) {
25501 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25502 now[0] = pos[0];
25503 now[1] = pos[1];
25504 }
25505
25506 if (e.touches[1]) {
25507 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25508 now[2] = pos[0];
25509 now[3] = pos[1];
25510 }
25511
25512 if (e.touches[2]) {
25513 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25514 now[4] = pos[0];
25515 now[5] = pos[1];
25516 }
25517
25518 if (start) {
25519 start.unactivate();
25520 }
25521
25522 var ctxTapend;
25523
25524 if (r.touchData.cxt) {
25525 ctxTapend = {
25526 originalEvent: e,
25527 type: 'cxttapend',
25528 position: {
25529 x: now[0],
25530 y: now[1]
25531 }
25532 };
25533
25534 if (start) {
25535 start.emit(ctxTapend);
25536 } else {
25537 cy.emit(ctxTapend);
25538 }
25539
25540 if (!r.touchData.cxtDragged) {
25541 var ctxTap = {
25542 originalEvent: e,
25543 type: 'cxttap',
25544 position: {
25545 x: now[0],
25546 y: now[1]
25547 }
25548 };
25549
25550 if (start) {
25551 start.emit(ctxTap);
25552 } else {
25553 cy.emit(ctxTap);
25554 }
25555 }
25556
25557 if (r.touchData.start) {
25558 r.touchData.start._private.grabbed = false;
25559 }
25560
25561 r.touchData.cxt = false;
25562 r.touchData.start = null;
25563 r.redraw();
25564 return;
25565 } // no more box selection if we don't have three fingers
25566
25567
25568 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25569 r.touchData.selecting = false;
25570 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25571 select[0] = undefined;
25572 select[1] = undefined;
25573 select[2] = undefined;
25574 select[3] = undefined;
25575 select[4] = 0;
25576 r.redrawHint('select', true);
25577 cy.emit({
25578 type: 'boxend',
25579 originalEvent: e,
25580 position: {
25581 x: now[0],
25582 y: now[1]
25583 }
25584 });
25585
25586 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25587 return ele.selectable() && !ele.selected();
25588 };
25589
25590 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25591
25592 if (box.nonempty()) {
25593 r.redrawHint('eles', true);
25594 }
25595
25596 r.redraw();
25597 }
25598
25599 if (start != null) {
25600 start.unactivate();
25601 }
25602
25603 if (e.touches[2]) {
25604 r.data.bgActivePosistion = undefined;
25605 r.redrawHint('select', true);
25606 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
25607 r.data.bgActivePosistion = undefined;
25608 r.redrawHint('select', true);
25609 var draggedEles = r.dragData.touchDragEles;
25610
25611 if (start != null) {
25612 var startWasGrabbed = start._private.grabbed;
25613 freeDraggedElements(draggedEles);
25614 r.redrawHint('drag', true);
25615 r.redrawHint('eles', true);
25616
25617 if (startWasGrabbed) {
25618 start.emit('freeon');
25619 draggedEles.emit('free');
25620
25621 if (r.dragData.didDrag) {
25622 start.emit('dragfreeon');
25623 draggedEles.emit('dragfree');
25624 }
25625 }
25626
25627 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25628 x: now[0],
25629 y: now[1]
25630 });
25631 start.unactivate();
25632 r.touchData.start = null;
25633 } else {
25634 var near = r.findNearestElement(now[0], now[1], true, true);
25635 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25636 x: now[0],
25637 y: now[1]
25638 });
25639 }
25640
25641 var dx = r.touchData.startPosition[0] - now[0];
25642 var dx2 = dx * dx;
25643 var dy = r.touchData.startPosition[1] - now[1];
25644 var dy2 = dy * dy;
25645 var dist2 = dx2 + dy2;
25646 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
25647
25648 if (!r.touchData.singleTouchMoved) {
25649 if (!start) {
25650 cy.$(':selected').unselect(['tapunselect']);
25651 }
25652
25653 triggerEvents(start, ['tap', 'vclick'], e, {
25654 x: now[0],
25655 y: now[1]
25656 });
25657 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
25658
25659
25660 if (start != null && !r.dragData.didDrag // didn't drag nodes around
25661 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
25662 ) {
25663 if (cy.selectionType() === 'single') {
25664 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
25665 start.select(['tapselect']);
25666 } else {
25667 if (start.selected()) {
25668 start.unselect(['tapunselect']);
25669 } else {
25670 start.select(['tapselect']);
25671 }
25672 }
25673
25674 r.redrawHint('eles', true);
25675 }
25676
25677 r.touchData.singleTouchMoved = true;
25678 }
25679
25680 for (var j = 0; j < now.length; j++) {
25681 earlier[j] = now[j];
25682 }
25683
25684 r.dragData.didDrag = false; // reset for next touchstart
25685
25686 if (e.touches.length === 0) {
25687 r.touchData.dragDelta = [];
25688 r.touchData.startPosition = null;
25689 r.touchData.startGPosition = null;
25690 r.touchData.didSelect = false;
25691 }
25692
25693 if (e.touches.length < 2) {
25694 if (e.touches.length === 1) {
25695 // the old start global pos'n may not be the same finger that remains
25696 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
25697 }
25698
25699 r.pinching = false;
25700 r.redrawHint('eles', true);
25701 r.redraw();
25702 } //r.redraw();
25703
25704 }, false); // fallback compatibility layer for ms pointer events
25705
25706 if (typeof TouchEvent === 'undefined') {
25707 var pointers = [];
25708
25709 var makeTouch = function makeTouch(e) {
25710 return {
25711 clientX: e.clientX,
25712 clientY: e.clientY,
25713 force: 1,
25714 identifier: e.pointerId,
25715 pageX: e.pageX,
25716 pageY: e.pageY,
25717 radiusX: e.width / 2,
25718 radiusY: e.height / 2,
25719 screenX: e.screenX,
25720 screenY: e.screenY,
25721 target: e.target
25722 };
25723 };
25724
25725 var makePointer = function makePointer(e) {
25726 return {
25727 event: e,
25728 touch: makeTouch(e)
25729 };
25730 };
25731
25732 var addPointer = function addPointer(e) {
25733 pointers.push(makePointer(e));
25734 };
25735
25736 var removePointer = function removePointer(e) {
25737 for (var i = 0; i < pointers.length; i++) {
25738 var p = pointers[i];
25739
25740 if (p.event.pointerId === e.pointerId) {
25741 pointers.splice(i, 1);
25742 return;
25743 }
25744 }
25745 };
25746
25747 var updatePointer = function updatePointer(e) {
25748 var p = pointers.filter(function (p) {
25749 return p.event.pointerId === e.pointerId;
25750 })[0];
25751 p.event = e;
25752 p.touch = makeTouch(e);
25753 };
25754
25755 var addTouchesToEvent = function addTouchesToEvent(e) {
25756 e.touches = pointers.map(function (p) {
25757 return p.touch;
25758 });
25759 };
25760
25761 var pointerIsMouse = function pointerIsMouse(e) {
25762 return e.pointerType === 'mouse' || e.pointerType === 4;
25763 };
25764
25765 r.registerBinding(r.container, 'pointerdown', function (e) {
25766 if (pointerIsMouse(e)) {
25767 return;
25768 } // mouse already handled
25769
25770
25771 e.preventDefault();
25772 addPointer(e);
25773 addTouchesToEvent(e);
25774 touchstartHandler(e);
25775 });
25776 r.registerBinding(r.container, 'pointerup', function (e) {
25777 if (pointerIsMouse(e)) {
25778 return;
25779 } // mouse already handled
25780
25781
25782 removePointer(e);
25783 addTouchesToEvent(e);
25784 touchendHandler(e);
25785 });
25786 r.registerBinding(r.container, 'pointercancel', function (e) {
25787 if (pointerIsMouse(e)) {
25788 return;
25789 } // mouse already handled
25790
25791
25792 removePointer(e);
25793 addTouchesToEvent(e);
25794 touchcancelHandler(e);
25795 });
25796 r.registerBinding(r.container, 'pointermove', function (e) {
25797 if (pointerIsMouse(e)) {
25798 return;
25799 } // mouse already handled
25800
25801
25802 e.preventDefault();
25803 updatePointer(e);
25804 addTouchesToEvent(e);
25805 touchmoveHandler(e);
25806 });
25807 }
25808};
25809
25810var BRp$d = {};
25811
25812BRp$d.generatePolygon = function (name, points) {
25813 return this.nodeShapes[name] = {
25814 renderer: this,
25815 name: name,
25816 points: points,
25817 draw: function draw(context, centerX, centerY, width, height) {
25818 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
25819 },
25820 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
25821 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
25822 },
25823 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
25824 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
25825 }
25826 };
25827};
25828
25829BRp$d.generateEllipse = function () {
25830 return this.nodeShapes['ellipse'] = {
25831 renderer: this,
25832 name: 'ellipse',
25833 draw: function draw(context, centerX, centerY, width, height) {
25834 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
25835 },
25836 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
25837 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
25838 },
25839 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
25840 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
25841 }
25842 };
25843};
25844
25845BRp$d.generateRoundPolygon = function (name, points) {
25846 // Pre-compute control points
25847 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
25848 // the unit vectors.
25849 // For simplicity the layout will be:
25850 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
25851 var allPoints = new Array(points.length * 2);
25852
25853 for (var i = 0; i < points.length / 2; i++) {
25854 var sourceIndex = i * 2;
25855 var destIndex = void 0;
25856
25857 if (i < points.length / 2 - 1) {
25858 destIndex = (i + 1) * 2;
25859 } else {
25860 destIndex = 0;
25861 }
25862
25863 allPoints[i * 4] = points[sourceIndex];
25864 allPoints[i * 4 + 1] = points[sourceIndex + 1];
25865 var xDest = points[destIndex] - points[sourceIndex];
25866 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
25867 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
25868 allPoints[i * 4 + 2] = xDest / norm;
25869 allPoints[i * 4 + 3] = yDest / norm;
25870 }
25871
25872 return this.nodeShapes[name] = {
25873 renderer: this,
25874 name: name,
25875 points: allPoints,
25876 draw: function draw(context, centerX, centerY, width, height) {
25877 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
25878 },
25879 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
25880 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
25881 },
25882 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
25883 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
25884 }
25885 };
25886};
25887
25888BRp$d.generateRoundRectangle = function () {
25889 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
25890 renderer: this,
25891 name: 'round-rectangle',
25892 points: generateUnitNgonPointsFitToSquare(4, 0),
25893 draw: function draw(context, centerX, centerY, width, height) {
25894 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
25895 },
25896 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
25897 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
25898 },
25899 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
25900 var cornerRadius = getRoundRectangleRadius(width, height);
25901 var diam = cornerRadius * 2; // Check hBox
25902
25903 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
25904 return true;
25905 } // Check vBox
25906
25907
25908 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
25909 return true;
25910 } // Check top left quarter circle
25911
25912
25913 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
25914 return true;
25915 } // Check top right quarter circle
25916
25917
25918 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
25919 return true;
25920 } // Check bottom right quarter circle
25921
25922
25923 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
25924 return true;
25925 } // Check bottom left quarter circle
25926
25927
25928 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
25929 return true;
25930 }
25931
25932 return false;
25933 }
25934 };
25935};
25936
25937BRp$d.generateCutRectangle = function () {
25938 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
25939 renderer: this,
25940 name: 'cut-rectangle',
25941 cornerLength: getCutRectangleCornerLength(),
25942 points: generateUnitNgonPointsFitToSquare(4, 0),
25943 draw: function draw(context, centerX, centerY, width, height) {
25944 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
25945 },
25946 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
25947 var cl = this.cornerLength;
25948 var hh = height / 2;
25949 var hw = width / 2;
25950 var xBegin = centerX - hw;
25951 var xEnd = centerX + hw;
25952 var yBegin = centerY - hh;
25953 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
25954
25955 return {
25956 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
25957 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
25958 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
25959 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
25960 };
25961 },
25962 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
25963 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
25964 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
25965 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
25966 },
25967 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
25968 // Check hBox
25969 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
25970 return true;
25971 } // Check vBox
25972
25973
25974 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
25975 return true;
25976 }
25977
25978 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
25979 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
25980 }
25981 };
25982};
25983
25984BRp$d.generateBarrel = function () {
25985 return this.nodeShapes['barrel'] = {
25986 renderer: this,
25987 name: 'barrel',
25988 points: generateUnitNgonPointsFitToSquare(4, 0),
25989 draw: function draw(context, centerX, centerY, width, height) {
25990 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
25991 },
25992 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
25993 // use two fixed t values for the bezier curve approximation
25994 var t0 = 0.15;
25995 var t1 = 0.5;
25996 var t2 = 0.85;
25997 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
25998
25999 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26000 // approximate curve pts based on the two t values
26001 var m0 = qbezierPtAt({
26002 x: pts[0],
26003 y: pts[1]
26004 }, {
26005 x: pts[2],
26006 y: pts[3]
26007 }, {
26008 x: pts[4],
26009 y: pts[5]
26010 }, t0);
26011 var m1 = qbezierPtAt({
26012 x: pts[0],
26013 y: pts[1]
26014 }, {
26015 x: pts[2],
26016 y: pts[3]
26017 }, {
26018 x: pts[4],
26019 y: pts[5]
26020 }, t1);
26021 var m2 = qbezierPtAt({
26022 x: pts[0],
26023 y: pts[1]
26024 }, {
26025 x: pts[2],
26026 y: pts[3]
26027 }, {
26028 x: pts[4],
26029 y: pts[5]
26030 }, t2);
26031 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26032 };
26033
26034 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26035 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26036 },
26037 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26038 var hh = height / 2;
26039 var hw = width / 2;
26040 var xBegin = centerX - hw;
26041 var xEnd = centerX + hw;
26042 var yBegin = centerY - hh;
26043 var yEnd = centerY + hh;
26044 var curveConstants = getBarrelCurveConstants(width, height);
26045 var hOffset = curveConstants.heightOffset;
26046 var wOffset = curveConstants.widthOffset;
26047 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26048
26049 var pts = {
26050 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26051 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26052 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26053 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26054 };
26055 pts.topLeft.isTop = true;
26056 pts.topRight.isTop = true;
26057 pts.bottomLeft.isBottom = true;
26058 pts.bottomRight.isBottom = true;
26059 return pts;
26060 },
26061 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26062 var curveConstants = getBarrelCurveConstants(width, height);
26063 var hOffset = curveConstants.heightOffset;
26064 var wOffset = curveConstants.widthOffset; // Check hBox
26065
26066 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26067 return true;
26068 } // Check vBox
26069
26070
26071 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26072 return true;
26073 }
26074
26075 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26076
26077 var getCurveT = function getCurveT(x, y, curvePts) {
26078 var x0 = curvePts[4];
26079 var x1 = curvePts[2];
26080 var x2 = curvePts[0];
26081 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26082
26083 var y2 = curvePts[1];
26084 var xMin = Math.min(x0, x2);
26085 var xMax = Math.max(x0, x2);
26086 var yMin = Math.min(y0, y2);
26087 var yMax = Math.max(y0, y2);
26088
26089 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26090 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26091 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26092 var validRoots = roots.filter(function (r) {
26093 return 0 <= r && r <= 1;
26094 });
26095
26096 if (validRoots.length > 0) {
26097 return validRoots[0];
26098 }
26099 }
26100
26101 return null;
26102 };
26103
26104 var curveRegions = Object.keys(barrelCurvePts);
26105
26106 for (var i = 0; i < curveRegions.length; i++) {
26107 var corner = curveRegions[i];
26108 var cornerPts = barrelCurvePts[corner];
26109 var t = getCurveT(x, y, cornerPts);
26110
26111 if (t == null) {
26112 continue;
26113 }
26114
26115 var y0 = cornerPts[5];
26116 var y1 = cornerPts[3];
26117 var y2 = cornerPts[1];
26118 var bezY = qbezierAt(y0, y1, y2, t);
26119
26120 if (cornerPts.isTop && bezY <= y) {
26121 return true;
26122 }
26123
26124 if (cornerPts.isBottom && y <= bezY) {
26125 return true;
26126 }
26127 }
26128
26129 return false;
26130 }
26131 };
26132};
26133
26134BRp$d.generateBottomRoundrectangle = function () {
26135 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26136 renderer: this,
26137 name: 'bottom-round-rectangle',
26138 points: generateUnitNgonPointsFitToSquare(4, 0),
26139 draw: function draw(context, centerX, centerY, width, height) {
26140 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26141 },
26142 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26143 var topStartX = nodeX - (width / 2 + padding);
26144 var topStartY = nodeY - (height / 2 + padding);
26145 var topEndY = topStartY;
26146 var topEndX = nodeX + (width / 2 + padding);
26147 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26148
26149 if (topIntersections.length > 0) {
26150 return topIntersections;
26151 }
26152
26153 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26154 },
26155 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26156 var cornerRadius = getRoundRectangleRadius(width, height);
26157 var diam = 2 * cornerRadius; // Check hBox
26158
26159 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26160 return true;
26161 } // Check vBox
26162
26163
26164 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26165 return true;
26166 } // check non-rounded top side
26167
26168
26169 var outerWidth = width / 2 + 2 * padding;
26170 var outerHeight = height / 2 + 2 * padding;
26171 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26172
26173 if (pointInsidePolygonPoints(x, y, points)) {
26174 return true;
26175 } // Check bottom right quarter circle
26176
26177
26178 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26179 return true;
26180 } // Check bottom left quarter circle
26181
26182
26183 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26184 return true;
26185 }
26186
26187 return false;
26188 }
26189 };
26190};
26191
26192BRp$d.registerNodeShapes = function () {
26193 var nodeShapes = this.nodeShapes = {};
26194 var renderer = this;
26195 this.generateEllipse();
26196 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26197 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26198 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26199 nodeShapes['square'] = nodeShapes['rectangle'];
26200 this.generateRoundRectangle();
26201 this.generateCutRectangle();
26202 this.generateBarrel();
26203 this.generateBottomRoundrectangle();
26204 {
26205 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26206 this.generatePolygon('diamond', diamondPoints);
26207 this.generateRoundPolygon('round-diamond', diamondPoints);
26208 }
26209 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26210 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26211 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26212 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26213 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26214 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26215 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26216 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26217 var star5Points = new Array(20);
26218 {
26219 var outerPoints = generateUnitNgonPoints(5, 0);
26220 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26221
26222 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26223 innerRadius *= 1.57;
26224
26225 for (var i = 0; i < innerPoints.length / 2; i++) {
26226 innerPoints[i * 2] *= innerRadius;
26227 innerPoints[i * 2 + 1] *= innerRadius;
26228 }
26229
26230 for (var i = 0; i < 20 / 4; i++) {
26231 star5Points[i * 4] = outerPoints[i * 2];
26232 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26233 star5Points[i * 4 + 2] = innerPoints[i * 2];
26234 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26235 }
26236 }
26237 star5Points = fitPolygonToSquare(star5Points);
26238 this.generatePolygon('star', star5Points);
26239 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26240 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26241 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]);
26242 {
26243 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26244 this.generatePolygon('tag', tagPoints);
26245 this.generateRoundPolygon('round-tag', tagPoints);
26246 }
26247
26248 nodeShapes.makePolygon = function (points) {
26249 // use caching on user-specified polygons so they are as fast as native shapes
26250 var key = points.join('$');
26251 var name = 'polygon-' + key;
26252 var shape;
26253
26254 if (shape = this[name]) {
26255 // got cached shape
26256 return shape;
26257 } // create and cache new shape
26258
26259
26260 return renderer.generatePolygon(name, points);
26261 };
26262};
26263
26264var BRp$e = {};
26265
26266BRp$e.timeToRender = function () {
26267 return this.redrawTotalTime / this.redrawCount;
26268};
26269
26270BRp$e.redraw = function (options) {
26271 options = options || staticEmptyObject();
26272 var r = this;
26273
26274 if (r.averageRedrawTime === undefined) {
26275 r.averageRedrawTime = 0;
26276 }
26277
26278 if (r.lastRedrawTime === undefined) {
26279 r.lastRedrawTime = 0;
26280 }
26281
26282 if (r.lastDrawTime === undefined) {
26283 r.lastDrawTime = 0;
26284 }
26285
26286 r.requestedFrame = true;
26287 r.renderOptions = options;
26288};
26289
26290BRp$e.beforeRender = function (fn, priority) {
26291 // the renderer can't add tick callbacks when destroyed
26292 if (this.destroyed) {
26293 return;
26294 }
26295
26296 if (priority == null) {
26297 error('Priority is not optional for beforeRender');
26298 }
26299
26300 var cbs = this.beforeRenderCallbacks;
26301 cbs.push({
26302 fn: fn,
26303 priority: priority
26304 }); // higher priority callbacks executed first
26305
26306 cbs.sort(function (a, b) {
26307 return b.priority - a.priority;
26308 });
26309};
26310
26311var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26312 var cbs = r.beforeRenderCallbacks;
26313
26314 for (var i = 0; i < cbs.length; i++) {
26315 cbs[i].fn(willDraw, startTime);
26316 }
26317};
26318
26319BRp$e.startRenderLoop = function () {
26320 var r = this;
26321 var cy = r.cy;
26322
26323 if (r.renderLoopStarted) {
26324 return;
26325 } else {
26326 r.renderLoopStarted = true;
26327 }
26328
26329 var renderFn = function renderFn(requestTime) {
26330 if (r.destroyed) {
26331 return;
26332 }
26333
26334 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26335 beforeRenderCallbacks(r, true, requestTime);
26336 var startTime = performanceNow();
26337 r.render(r.renderOptions);
26338 var endTime = r.lastDrawTime = performanceNow();
26339
26340 if (r.averageRedrawTime === undefined) {
26341 r.averageRedrawTime = endTime - startTime;
26342 }
26343
26344 if (r.redrawCount === undefined) {
26345 r.redrawCount = 0;
26346 }
26347
26348 r.redrawCount++;
26349
26350 if (r.redrawTotalTime === undefined) {
26351 r.redrawTotalTime = 0;
26352 }
26353
26354 var duration = endTime - startTime;
26355 r.redrawTotalTime += duration;
26356 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26357
26358 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26359 r.requestedFrame = false;
26360 } else {
26361 beforeRenderCallbacks(r, false, requestTime);
26362 }
26363
26364 r.skipFrame = false;
26365 requestAnimationFrame(renderFn);
26366 };
26367
26368 requestAnimationFrame(renderFn);
26369};
26370
26371var BaseRenderer = function BaseRenderer(options) {
26372 this.init(options);
26373};
26374
26375var BR = BaseRenderer;
26376var BRp$f = BR.prototype;
26377BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26378
26379BRp$f.init = function (options) {
26380 var r = this;
26381 r.options = options;
26382 r.cy = options.cy;
26383 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26384
26385 if (window$1) {
26386 var document = window$1.document;
26387 var head = document.head;
26388 var stylesheetId = '__________cytoscape_stylesheet';
26389 var className = '__________cytoscape_container';
26390 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26391
26392 if (ctr.className.indexOf(className) < 0) {
26393 ctr.className = (ctr.className || '') + ' ' + className;
26394 }
26395
26396 if (!stylesheetAlreadyExists) {
26397 var stylesheet = document.createElement('style');
26398 stylesheet.id = stylesheetId;
26399 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26400 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26401 }
26402
26403 var computedStyle = window$1.getComputedStyle(ctr);
26404 var position = computedStyle.getPropertyValue('position');
26405
26406 if (position === 'static') {
26407 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26408 }
26409 }
26410
26411 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26412
26413 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26414
26415 r.hoverData = {
26416 down: null,
26417 last: null,
26418 downTime: null,
26419 triggerMode: null,
26420 dragging: false,
26421 initialPan: [null, null],
26422 capture: false
26423 };
26424 r.dragData = {
26425 possibleDragElements: []
26426 };
26427 r.touchData = {
26428 start: null,
26429 capture: false,
26430 // These 3 fields related to tap, taphold events
26431 startPosition: [null, null, null, null, null, null],
26432 singleTouchStartTime: null,
26433 singleTouchMoved: true,
26434 now: [null, null, null, null, null, null],
26435 earlier: [null, null, null, null, null, null]
26436 };
26437 r.redraws = 0;
26438 r.showFps = options.showFps;
26439 r.debug = options.debug;
26440 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26441 r.textureOnViewport = options.textureOnViewport;
26442 r.wheelSensitivity = options.wheelSensitivity;
26443 r.motionBlurEnabled = options.motionBlur; // on by default
26444
26445 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26446 r.motionBlur = options.motionBlur; // for initial kick off
26447
26448 r.motionBlurOpacity = options.motionBlurOpacity;
26449 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26450 r.motionBlurPxRatio = 1;
26451 r.mbPxRBlurry = 1; //0.8;
26452
26453 r.minMbLowQualFrames = 4;
26454 r.fullQualityMb = false;
26455 r.clearedForMotionBlur = [];
26456 r.desktopTapThreshold = options.desktopTapThreshold;
26457 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26458 r.touchTapThreshold = options.touchTapThreshold;
26459 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26460 r.tapholdDuration = 500;
26461 r.bindings = [];
26462 r.beforeRenderCallbacks = [];
26463 r.beforeRenderPriorities = {
26464 // higher priority execs before lower one
26465 animations: 400,
26466 eleCalcs: 300,
26467 eleTxrDeq: 200,
26468 lyrTxrDeq: 150,
26469 lyrTxrSkip: 100
26470 };
26471 r.registerNodeShapes();
26472 r.registerArrowShapes();
26473 r.registerCalculationListeners();
26474};
26475
26476BRp$f.notify = function (eventName, eles) {
26477 var r = this;
26478 var cy = r.cy; // the renderer can't be notified after it's destroyed
26479
26480 if (this.destroyed) {
26481 return;
26482 }
26483
26484 if (eventName === 'init') {
26485 r.load();
26486 return;
26487 }
26488
26489 if (eventName === 'destroy') {
26490 r.destroy();
26491 return;
26492 }
26493
26494 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26495 r.invalidateCachedZSortedEles();
26496 }
26497
26498 if (eventName === 'viewport') {
26499 r.redrawHint('select', true);
26500 }
26501
26502 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26503 r.invalidateContainerClientCoordsCache();
26504 r.matchCanvasSize(r.container);
26505 }
26506
26507 r.redrawHint('eles', true);
26508 r.redrawHint('drag', true);
26509 this.startRenderLoop();
26510 this.redraw();
26511};
26512
26513BRp$f.destroy = function () {
26514 var r = this;
26515 r.destroyed = true;
26516 r.cy.stopAnimationLoop();
26517
26518 for (var i = 0; i < r.bindings.length; i++) {
26519 var binding = r.bindings[i];
26520 var b = binding;
26521 var tgt = b.target;
26522 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26523 }
26524
26525 r.bindings = [];
26526 r.beforeRenderCallbacks = [];
26527 r.onUpdateEleCalcsFns = [];
26528
26529 if (r.removeObserver) {
26530 r.removeObserver.disconnect();
26531 }
26532
26533 if (r.styleObserver) {
26534 r.styleObserver.disconnect();
26535 }
26536
26537 if (r.resizeObserver) {
26538 r.resizeObserver.disconnect();
26539 }
26540
26541 if (r.labelCalcDiv) {
26542 try {
26543 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26544 } catch (e) {// ie10 issue #1014
26545 }
26546 }
26547};
26548
26549BRp$f.isHeadless = function () {
26550 return false;
26551};
26552
26553[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26554 extend(BRp$f, props);
26555});
26556
26557var fullFpsTime = 1000 / 60; // assume 60 frames per second
26558
26559var defs = {
26560 setupDequeueing: function setupDequeueing(opts) {
26561 return function setupDequeueingImpl() {
26562 var self = this;
26563 var r = this.renderer;
26564
26565 if (self.dequeueingSetup) {
26566 return;
26567 } else {
26568 self.dequeueingSetup = true;
26569 }
26570
26571 var queueRedraw = util(function () {
26572 r.redrawHint('eles', true);
26573 r.redrawHint('drag', true);
26574 r.redraw();
26575 }, opts.deqRedrawThreshold);
26576
26577 var dequeue = function dequeue(willDraw, frameStartTime) {
26578 var startTime = performanceNow();
26579 var avgRenderTime = r.averageRedrawTime;
26580 var renderTime = r.lastRedrawTime;
26581 var deqd = [];
26582 var extent = r.cy.extent();
26583 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26584 // queue won't automatically be flushed before dequeueing starts
26585
26586 if (!willDraw) {
26587 r.flushRenderedStyleQueue();
26588 }
26589
26590 while (true) {
26591 // eslint-disable-line no-constant-condition
26592 var now = performanceNow();
26593 var duration = now - startTime;
26594 var frameDuration = now - frameStartTime;
26595
26596 if (renderTime < fullFpsTime) {
26597 // if we're rendering faster than the ideal fps, then do dequeueing
26598 // during all of the remaining frame time
26599 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26600
26601 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26602 break;
26603 }
26604 } else {
26605 if (willDraw) {
26606 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26607 break;
26608 }
26609 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
26610 break;
26611 }
26612 }
26613
26614 var thisDeqd = opts.deq(self, pixelRatio, extent);
26615
26616 if (thisDeqd.length > 0) {
26617 for (var i = 0; i < thisDeqd.length; i++) {
26618 deqd.push(thisDeqd[i]);
26619 }
26620 } else {
26621 break;
26622 }
26623 } // callbacks on dequeue
26624
26625
26626 if (deqd.length > 0) {
26627 opts.onDeqd(self, deqd);
26628
26629 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
26630 queueRedraw();
26631 }
26632 }
26633 };
26634
26635 var priority = opts.priority || noop;
26636 r.beforeRender(dequeue, priority(self));
26637 };
26638 }
26639};
26640
26641// Uses keys so elements may share the same cache.
26642
26643var ElementTextureCacheLookup =
26644/*#__PURE__*/
26645function () {
26646 function ElementTextureCacheLookup(getKey) {
26647 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
26648
26649 _classCallCheck(this, ElementTextureCacheLookup);
26650
26651 this.idsByKey = new Map$1();
26652 this.keyForId = new Map$1();
26653 this.cachesByLvl = new Map$1();
26654 this.lvls = [];
26655 this.getKey = getKey;
26656 this.doesEleInvalidateKey = doesEleInvalidateKey;
26657 }
26658
26659 _createClass(ElementTextureCacheLookup, [{
26660 key: "getIdsFor",
26661 value: function getIdsFor(key) {
26662 if (key == null) {
26663 error("Can not get id list for null key");
26664 }
26665
26666 var idsByKey = this.idsByKey;
26667 var ids = this.idsByKey.get(key);
26668
26669 if (!ids) {
26670 ids = new Set$1();
26671 idsByKey.set(key, ids);
26672 }
26673
26674 return ids;
26675 }
26676 }, {
26677 key: "addIdForKey",
26678 value: function addIdForKey(key, id) {
26679 if (key != null) {
26680 this.getIdsFor(key).add(id);
26681 }
26682 }
26683 }, {
26684 key: "deleteIdForKey",
26685 value: function deleteIdForKey(key, id) {
26686 if (key != null) {
26687 this.getIdsFor(key)["delete"](id);
26688 }
26689 }
26690 }, {
26691 key: "getNumberOfIdsForKey",
26692 value: function getNumberOfIdsForKey(key) {
26693 if (key == null) {
26694 return 0;
26695 } else {
26696 return this.getIdsFor(key).size;
26697 }
26698 }
26699 }, {
26700 key: "updateKeyMappingFor",
26701 value: function updateKeyMappingFor(ele) {
26702 var id = ele.id();
26703 var prevKey = this.keyForId.get(id);
26704 var currKey = this.getKey(ele);
26705 this.deleteIdForKey(prevKey, id);
26706 this.addIdForKey(currKey, id);
26707 this.keyForId.set(id, currKey);
26708 }
26709 }, {
26710 key: "deleteKeyMappingFor",
26711 value: function deleteKeyMappingFor(ele) {
26712 var id = ele.id();
26713 var prevKey = this.keyForId.get(id);
26714 this.deleteIdForKey(prevKey, id);
26715 this.keyForId["delete"](id);
26716 }
26717 }, {
26718 key: "keyHasChangedFor",
26719 value: function keyHasChangedFor(ele) {
26720 var id = ele.id();
26721 var prevKey = this.keyForId.get(id);
26722 var newKey = this.getKey(ele);
26723 return prevKey !== newKey;
26724 }
26725 }, {
26726 key: "isInvalid",
26727 value: function isInvalid(ele) {
26728 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
26729 }
26730 }, {
26731 key: "getCachesAt",
26732 value: function getCachesAt(lvl) {
26733 var cachesByLvl = this.cachesByLvl,
26734 lvls = this.lvls;
26735 var caches = cachesByLvl.get(lvl);
26736
26737 if (!caches) {
26738 caches = new Map$1();
26739 cachesByLvl.set(lvl, caches);
26740 lvls.push(lvl);
26741 }
26742
26743 return caches;
26744 }
26745 }, {
26746 key: "getCache",
26747 value: function getCache(key, lvl) {
26748 return this.getCachesAt(lvl).get(key);
26749 }
26750 }, {
26751 key: "get",
26752 value: function get(ele, lvl) {
26753 var key = this.getKey(ele);
26754 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
26755
26756 if (cache != null) {
26757 this.updateKeyMappingFor(ele);
26758 }
26759
26760 return cache;
26761 }
26762 }, {
26763 key: "getForCachedKey",
26764 value: function getForCachedKey(ele, lvl) {
26765 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
26766
26767 var cache = this.getCache(key, lvl);
26768 return cache;
26769 }
26770 }, {
26771 key: "hasCache",
26772 value: function hasCache(key, lvl) {
26773 return this.getCachesAt(lvl).has(key);
26774 }
26775 }, {
26776 key: "has",
26777 value: function has(ele, lvl) {
26778 var key = this.getKey(ele);
26779 return this.hasCache(key, lvl);
26780 }
26781 }, {
26782 key: "setCache",
26783 value: function setCache(key, lvl, cache) {
26784 cache.key = key;
26785 this.getCachesAt(lvl).set(key, cache);
26786 }
26787 }, {
26788 key: "set",
26789 value: function set(ele, lvl, cache) {
26790 var key = this.getKey(ele);
26791 this.setCache(key, lvl, cache);
26792 this.updateKeyMappingFor(ele);
26793 }
26794 }, {
26795 key: "deleteCache",
26796 value: function deleteCache(key, lvl) {
26797 this.getCachesAt(lvl)["delete"](key);
26798 }
26799 }, {
26800 key: "delete",
26801 value: function _delete(ele, lvl) {
26802 var key = this.getKey(ele);
26803 this.deleteCache(key, lvl);
26804 }
26805 }, {
26806 key: "invalidateKey",
26807 value: function invalidateKey(key) {
26808 var _this = this;
26809
26810 this.lvls.forEach(function (lvl) {
26811 return _this.deleteCache(key, lvl);
26812 });
26813 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
26814
26815 }, {
26816 key: "invalidate",
26817 value: function invalidate(ele) {
26818 var id = ele.id();
26819 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
26820
26821 this.deleteKeyMappingFor(ele);
26822 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
26823
26824 if (entireKeyInvalidated) {
26825 // clear mapping for current key
26826 this.invalidateKey(key);
26827 }
26828
26829 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
26830 }
26831 }]);
26832
26833 return ElementTextureCacheLookup;
26834}();
26835
26836var minTxrH = 25; // the size of the texture cache for small height eles (special case)
26837
26838var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
26839
26840var minLvl = -4; // when scaling smaller than that we don't need to re-render
26841
26842var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
26843
26844var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
26845
26846var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
26847
26848var defTxrWidth = 1024; // default/minimum texture width
26849
26850var maxTxrW = 1024; // the maximum width of a texture
26851
26852var maxTxrH = 1024; // the maximum height of a texture
26853
26854var minUtility = 0.2; // if usage of texture is less than this, it is retired
26855
26856var maxFullness = 0.8; // fullness of texture after which queue removal is checked
26857
26858var maxFullnessChecks = 10; // dequeued after this many checks
26859
26860var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
26861
26862var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
26863
26864var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
26865
26866var deqFastCost = 0.9; // % of frame time to be used when >60fps
26867
26868var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
26869
26870var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
26871
26872var getTxrReasons = {
26873 dequeue: 'dequeue',
26874 downscale: 'downscale',
26875 highQuality: 'highQuality'
26876};
26877var initDefaults = defaults({
26878 getKey: null,
26879 doesEleInvalidateKey: falsify,
26880 drawElement: null,
26881 getBoundingBox: null,
26882 getRotationPoint: null,
26883 getRotationOffset: null,
26884 isVisible: trueify,
26885 allowEdgeTxrCaching: true,
26886 allowParentTxrCaching: true
26887});
26888
26889var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
26890 var self = this;
26891 self.renderer = renderer;
26892 self.onDequeues = [];
26893 var opts = initDefaults(initOptions);
26894 extend(self, opts);
26895 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
26896 self.setupDequeueing();
26897};
26898
26899var ETCp = ElementTextureCache.prototype;
26900ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
26901
26902ETCp.getTextureQueue = function (txrH) {
26903 var self = this;
26904 self.eleImgCaches = self.eleImgCaches || {};
26905 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
26906}; // the list of usused textures which can be recycled (in use in texture queue)
26907
26908
26909ETCp.getRetiredTextureQueue = function (txrH) {
26910 var self = this;
26911 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
26912 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
26913 return rtxtrQ;
26914}; // queue of element draw requests at different scale levels
26915
26916
26917ETCp.getElementQueue = function () {
26918 var self = this;
26919 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
26920 return b.reqs - a.reqs;
26921 });
26922 return q;
26923}; // queue of element draw requests at different scale levels (element id lookup)
26924
26925
26926ETCp.getElementKeyToQueue = function () {
26927 var self = this;
26928 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
26929 return k2q;
26930};
26931
26932ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
26933 var self = this;
26934 var r = this.renderer;
26935 var zoom = r.cy.zoom();
26936 var lookup = this.lookup;
26937
26938 if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
26939 return null;
26940 }
26941
26942 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
26943 return null;
26944 }
26945
26946 if (lvl == null) {
26947 lvl = Math.ceil(log2(zoom * pxRatio));
26948 }
26949
26950 if (lvl < minLvl) {
26951 lvl = minLvl;
26952 } else if (zoom >= maxZoom || lvl > maxLvl) {
26953 return null;
26954 }
26955
26956 var scale = Math.pow(2, lvl);
26957 var eleScaledH = bb.h * scale;
26958 var eleScaledW = bb.w * scale;
26959 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
26960
26961 if (!this.isVisible(ele, scaledLabelShown)) {
26962 return null;
26963 }
26964
26965 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
26966
26967 if (eleCache && eleCache.invalidated) {
26968 eleCache.invalidated = false;
26969 eleCache.texture.invalidatedWidth -= eleCache.width;
26970 }
26971
26972 if (eleCache) {
26973 return eleCache;
26974 }
26975
26976 var txrH; // which texture height this ele belongs to
26977
26978 if (eleScaledH <= minTxrH) {
26979 txrH = minTxrH;
26980 } else if (eleScaledH <= txrStepH) {
26981 txrH = txrStepH;
26982 } else {
26983 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
26984 }
26985
26986 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
26987 return null; // caching large elements is not efficient
26988 }
26989
26990 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
26991
26992 var txr = txrQ[txrQ.length - 2];
26993
26994 var addNewTxr = function addNewTxr() {
26995 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
26996 }; // try the last one if there is no second last one
26997
26998
26999 if (!txr) {
27000 txr = txrQ[txrQ.length - 1];
27001 } // if the last one doesn't exist, we need a first one
27002
27003
27004 if (!txr) {
27005 txr = addNewTxr();
27006 } // if there's no room in the current texture, we need a new one
27007
27008
27009 if (txr.width - txr.usedWidth < eleScaledW) {
27010 txr = addNewTxr();
27011 }
27012
27013 var scalableFrom = function scalableFrom(otherCache) {
27014 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27015 };
27016
27017 var deqing = reason && reason === getTxrReasons.dequeue;
27018 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27019 var downscaleReq = reason && reason === getTxrReasons.downscale;
27020 var higherCache; // the nearest cache with a higher level
27021
27022 for (var l = lvl + 1; l <= maxLvl; l++) {
27023 var c = lookup.get(ele, l);
27024
27025 if (c) {
27026 higherCache = c;
27027 break;
27028 }
27029 }
27030
27031 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27032
27033 var downscale = function downscale() {
27034 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27035 }; // reset ele area in texture
27036
27037
27038 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27039 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27040
27041 if (scalableFrom(oneUpCache)) {
27042 // then we can relatively cheaply rescale the existing image w/o rerendering
27043 downscale();
27044 } else if (scalableFrom(higherCache)) {
27045 // then use the higher cache for now and queue the next level down
27046 // to cheaply scale towards the smaller level
27047 if (highQualityReq) {
27048 for (var _l = higherCache.level; _l > lvl; _l--) {
27049 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27050 }
27051
27052 downscale();
27053 } else {
27054 self.queueElement(ele, higherCache.level - 1);
27055 return higherCache;
27056 }
27057 } else {
27058 var lowerCache; // the nearest cache with a lower level
27059
27060 if (!deqing && !highQualityReq && !downscaleReq) {
27061 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27062 var _c = lookup.get(ele, _l2);
27063
27064 if (_c) {
27065 lowerCache = _c;
27066 break;
27067 }
27068 }
27069 }
27070
27071 if (scalableFrom(lowerCache)) {
27072 // then use the lower quality cache for now and queue the better one for later
27073 self.queueElement(ele, lvl);
27074 return lowerCache;
27075 }
27076
27077 txr.context.translate(txr.usedWidth, 0);
27078 txr.context.scale(scale, scale);
27079 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27080 txr.context.scale(1 / scale, 1 / scale);
27081 txr.context.translate(-txr.usedWidth, 0);
27082 }
27083
27084 eleCache = {
27085 x: txr.usedWidth,
27086 texture: txr,
27087 level: lvl,
27088 scale: scale,
27089 width: eleScaledW,
27090 height: eleScaledH,
27091 scaledLabelShown: scaledLabelShown
27092 };
27093 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27094 txr.eleCaches.push(eleCache);
27095 lookup.set(ele, lvl, eleCache);
27096 self.checkTextureFullness(txr);
27097 return eleCache;
27098};
27099
27100ETCp.invalidateElements = function (eles) {
27101 for (var i = 0; i < eles.length; i++) {
27102 this.invalidateElement(eles[i]);
27103 }
27104};
27105
27106ETCp.invalidateElement = function (ele) {
27107 var self = this;
27108 var lookup = self.lookup;
27109 var caches = [];
27110 var invalid = lookup.isInvalid(ele);
27111
27112 if (!invalid) {
27113 return; // override the invalidation request if the element key has not changed
27114 }
27115
27116 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27117 var cache = lookup.getForCachedKey(ele, lvl);
27118
27119 if (cache) {
27120 caches.push(cache);
27121 }
27122 }
27123
27124 var noOtherElesUseCache = lookup.invalidate(ele);
27125
27126 if (noOtherElesUseCache) {
27127 for (var i = 0; i < caches.length; i++) {
27128 var _cache = caches[i];
27129 var txr = _cache.texture; // remove space from the texture it belongs to
27130
27131 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27132
27133 _cache.invalidated = true; // retire the texture if its utility is low
27134
27135 self.checkTextureUtility(txr);
27136 }
27137 } // remove from queue since the old req was for the old state
27138
27139
27140 self.removeFromQueue(ele);
27141};
27142
27143ETCp.checkTextureUtility = function (txr) {
27144 // invalidate all entries in the cache if the cache size is small
27145 if (txr.invalidatedWidth >= minUtility * txr.width) {
27146 this.retireTexture(txr);
27147 }
27148};
27149
27150ETCp.checkTextureFullness = function (txr) {
27151 // if texture has been mostly filled and passed over several times, remove
27152 // it from the queue so we don't need to waste time looking at it to put new things
27153 var self = this;
27154 var txrQ = self.getTextureQueue(txr.height);
27155
27156 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27157 removeFromArray(txrQ, txr);
27158 } else {
27159 txr.fullnessChecks++;
27160 }
27161};
27162
27163ETCp.retireTexture = function (txr) {
27164 var self = this;
27165 var txrH = txr.height;
27166 var txrQ = self.getTextureQueue(txrH);
27167 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27168
27169 removeFromArray(txrQ, txr);
27170 txr.retired = true; // remove the refs from the eles to the caches:
27171
27172 var eleCaches = txr.eleCaches;
27173
27174 for (var i = 0; i < eleCaches.length; i++) {
27175 var eleCache = eleCaches[i];
27176 lookup.deleteCache(eleCache.key, eleCache.level);
27177 }
27178
27179 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27180
27181 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27182 rtxtrQ.push(txr);
27183};
27184
27185ETCp.addTexture = function (txrH, minW) {
27186 var self = this;
27187 var txrQ = self.getTextureQueue(txrH);
27188 var txr = {};
27189 txrQ.push(txr);
27190 txr.eleCaches = [];
27191 txr.height = txrH;
27192 txr.width = Math.max(defTxrWidth, minW);
27193 txr.usedWidth = 0;
27194 txr.invalidatedWidth = 0;
27195 txr.fullnessChecks = 0;
27196 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27197 txr.context = txr.canvas.getContext('2d');
27198 return txr;
27199};
27200
27201ETCp.recycleTexture = function (txrH, minW) {
27202 var self = this;
27203 var txrQ = self.getTextureQueue(txrH);
27204 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27205
27206 for (var i = 0; i < rtxtrQ.length; i++) {
27207 var txr = rtxtrQ[i];
27208
27209 if (txr.width >= minW) {
27210 txr.retired = false;
27211 txr.usedWidth = 0;
27212 txr.invalidatedWidth = 0;
27213 txr.fullnessChecks = 0;
27214 clearArray(txr.eleCaches);
27215 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27216 txr.context.clearRect(0, 0, txr.width, txr.height);
27217 removeFromArray(rtxtrQ, txr);
27218 txrQ.push(txr);
27219 return txr;
27220 }
27221 }
27222};
27223
27224ETCp.queueElement = function (ele, lvl) {
27225 var self = this;
27226 var q = self.getElementQueue();
27227 var k2q = self.getElementKeyToQueue();
27228 var key = this.getKey(ele);
27229 var existingReq = k2q[key];
27230
27231 if (existingReq) {
27232 // use the max lvl b/c in between lvls are cheap to make
27233 existingReq.level = Math.max(existingReq.level, lvl);
27234 existingReq.eles.merge(ele);
27235 existingReq.reqs++;
27236 q.updateItem(existingReq);
27237 } else {
27238 var req = {
27239 eles: ele.spawn().merge(ele),
27240 level: lvl,
27241 reqs: 1,
27242 key: key
27243 };
27244 q.push(req);
27245 k2q[key] = req;
27246 }
27247};
27248
27249ETCp.dequeue = function (pxRatio
27250/*, extent*/
27251) {
27252 var self = this;
27253 var q = self.getElementQueue();
27254 var k2q = self.getElementKeyToQueue();
27255 var dequeued = [];
27256 var lookup = self.lookup;
27257
27258 for (var i = 0; i < maxDeqSize; i++) {
27259 if (q.size() > 0) {
27260 var req = q.pop();
27261 var key = req.key;
27262 var ele = req.eles[0]; // all eles have the same key
27263
27264 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27265
27266 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27267
27268 if (cacheExists) {
27269 continue;
27270 }
27271
27272 dequeued.push(req);
27273 var bb = self.getBoundingBox(ele);
27274 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27275 } else {
27276 break;
27277 }
27278 }
27279
27280 return dequeued;
27281};
27282
27283ETCp.removeFromQueue = function (ele) {
27284 var self = this;
27285 var q = self.getElementQueue();
27286 var k2q = self.getElementKeyToQueue();
27287 var key = this.getKey(ele);
27288 var req = k2q[key];
27289
27290 if (req != null) {
27291 if (req.eles.length === 1) {
27292 // remove if last ele in the req
27293 // bring to front of queue
27294 req.reqs = MAX_INT;
27295 q.updateItem(req);
27296 q.pop(); // remove from queue
27297
27298 k2q[key] = null; // remove from lookup map
27299 } else {
27300 // otherwise just remove ele from req
27301 req.eles.unmerge(ele);
27302 }
27303 }
27304};
27305
27306ETCp.onDequeue = function (fn) {
27307 this.onDequeues.push(fn);
27308};
27309
27310ETCp.offDequeue = function (fn) {
27311 removeFromArray(this.onDequeues, fn);
27312};
27313
27314ETCp.setupDequeueing = defs.setupDequeueing({
27315 deqRedrawThreshold: deqRedrawThreshold,
27316 deqCost: deqCost,
27317 deqAvgCost: deqAvgCost,
27318 deqNoDrawCost: deqNoDrawCost,
27319 deqFastCost: deqFastCost,
27320 deq: function deq(self, pxRatio, extent) {
27321 return self.dequeue(pxRatio, extent);
27322 },
27323 onDeqd: function onDeqd(self, deqd) {
27324 for (var i = 0; i < self.onDequeues.length; i++) {
27325 var fn = self.onDequeues[i];
27326 fn(deqd);
27327 }
27328 },
27329 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27330 for (var i = 0; i < deqd.length; i++) {
27331 var eles = deqd[i].eles;
27332
27333 for (var j = 0; j < eles.length; j++) {
27334 var bb = eles[j].boundingBox();
27335
27336 if (boundingBoxesIntersect(bb, extent)) {
27337 return true;
27338 }
27339 }
27340 }
27341
27342 return false;
27343 },
27344 priority: function priority(self) {
27345 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27346 }
27347});
27348
27349var defNumLayers = 1; // default number of layers to use
27350
27351var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27352
27353var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27354
27355var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27356
27357var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27358
27359var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27360
27361var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27362
27363var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27364
27365var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27366
27367var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27368
27369var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27370
27371var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27372
27373var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27374
27375var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27376// var log = function(){ console.log.apply( console, arguments ); };
27377
27378var LayeredTextureCache = function LayeredTextureCache(renderer) {
27379 var self = this;
27380 var r = self.renderer = renderer;
27381 var cy = r.cy;
27382 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27383
27384 self.firstGet = true;
27385 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27386 self.skipping = false;
27387 self.eleTxrDeqs = cy.collection();
27388 self.scheduleElementRefinement = util(function () {
27389 self.refineElementTextures(self.eleTxrDeqs);
27390 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27391 }, refineEleDebounceTime);
27392 r.beforeRender(function (willDraw, now) {
27393 if (now - self.lastInvalidationTime <= invalidThreshold) {
27394 self.skipping = true;
27395 } else {
27396 self.skipping = false;
27397 }
27398 }, r.beforeRenderPriorities.lyrTxrSkip);
27399
27400 var qSort = function qSort(a, b) {
27401 return b.reqs - a.reqs;
27402 };
27403
27404 self.layersQueue = new Heap(qSort);
27405 self.setupDequeueing();
27406};
27407
27408var LTCp = LayeredTextureCache.prototype;
27409var layerIdPool = 0;
27410var MAX_INT$1 = Math.pow(2, 53) - 1;
27411
27412LTCp.makeLayer = function (bb, lvl) {
27413 var scale = Math.pow(2, lvl);
27414 var w = Math.ceil(bb.w * scale);
27415 var h = Math.ceil(bb.h * scale);
27416 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27417 var layer = {
27418 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27419 bb: bb,
27420 level: lvl,
27421 width: w,
27422 height: h,
27423 canvas: canvas,
27424 context: canvas.getContext('2d'),
27425 eles: [],
27426 elesQueue: [],
27427 reqs: 0
27428 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27429
27430 var cxt = layer.context;
27431 var dx = -layer.bb.x1;
27432 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27433
27434 cxt.scale(scale, scale);
27435 cxt.translate(dx, dy);
27436 return layer;
27437};
27438
27439LTCp.getLayers = function (eles, pxRatio, lvl) {
27440 var self = this;
27441 var r = self.renderer;
27442 var cy = r.cy;
27443 var zoom = cy.zoom();
27444 var firstGet = self.firstGet;
27445 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27446 //log eles.map(function(ele){ return ele.id() }) );
27447
27448 if (lvl == null) {
27449 lvl = Math.ceil(log2(zoom * pxRatio));
27450
27451 if (lvl < minLvl$1) {
27452 lvl = minLvl$1;
27453 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27454 return null;
27455 }
27456 }
27457
27458 self.validateLayersElesOrdering(lvl, eles);
27459 var layersByLvl = self.layersByLevel;
27460 var scale = Math.pow(2, lvl);
27461 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27462 var bb;
27463 var lvlComplete = self.levelIsComplete(lvl, eles);
27464 var tmpLayers;
27465
27466 var checkTempLevels = function checkTempLevels() {
27467 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27468 self.validateLayersElesOrdering(l, eles);
27469
27470 if (self.levelIsComplete(l, eles)) {
27471 tmpLayers = layersByLvl[l];
27472 return true;
27473 }
27474 };
27475
27476 var checkLvls = function checkLvls(dir) {
27477 if (tmpLayers) {
27478 return;
27479 }
27480
27481 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27482 if (canUseAsTmpLvl(l)) {
27483 break;
27484 }
27485 }
27486 };
27487
27488 checkLvls(+1);
27489 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27490
27491 for (var i = layers.length - 1; i >= 0; i--) {
27492 var layer = layers[i];
27493
27494 if (layer.invalid) {
27495 removeFromArray(layers, layer);
27496 }
27497 }
27498 };
27499
27500 if (!lvlComplete) {
27501 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27502 // and later queue the current layerset so we can get the proper quality level soon
27503 checkTempLevels();
27504 } else {
27505 // log('level complete, using existing layers\n--');
27506 return layers;
27507 }
27508
27509 var getBb = function getBb() {
27510 if (!bb) {
27511 bb = makeBoundingBox();
27512
27513 for (var i = 0; i < eles.length; i++) {
27514 updateBoundingBox(bb, eles[i].boundingBox());
27515 }
27516 }
27517
27518 return bb;
27519 };
27520
27521 var makeLayer = function makeLayer(opts) {
27522 opts = opts || {};
27523 var after = opts.after;
27524 getBb();
27525 var area = bb.w * scale * (bb.h * scale);
27526
27527 if (area > maxLayerArea) {
27528 return null;
27529 }
27530
27531 var layer = self.makeLayer(bb, lvl);
27532
27533 if (after != null) {
27534 var index = layers.indexOf(after) + 1;
27535 layers.splice(index, 0, layer);
27536 } else if (opts.insert === undefined || opts.insert) {
27537 // no after specified => first layer made so put at start
27538 layers.unshift(layer);
27539 } // if( tmpLayers ){
27540 //self.queueLayer( layer );
27541 // }
27542
27543
27544 return layer;
27545 };
27546
27547 if (self.skipping && !firstGet) {
27548 // log('skip layers');
27549 return null;
27550 } // log('do layers');
27551
27552
27553 var layer = null;
27554 var maxElesPerLayer = eles.length / defNumLayers;
27555 var allowLazyQueueing = !firstGet;
27556
27557 for (var i = 0; i < eles.length; i++) {
27558 var ele = eles[i];
27559 var rs = ele._private.rscratch;
27560 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27561
27562 var existingLayer = caches[lvl];
27563
27564 if (existingLayer) {
27565 // reuse layer for later eles
27566 // log('reuse layer for', ele.id());
27567 layer = existingLayer;
27568 continue;
27569 }
27570
27571 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27572 // log('make new layer for ele %s', ele.id());
27573 layer = makeLayer({
27574 insert: true,
27575 after: layer
27576 }); // if now layer can be built then we can't use layers at this level
27577
27578 if (!layer) {
27579 return null;
27580 } // log('new layer with id %s', layer.id);
27581
27582 }
27583
27584 if (tmpLayers || allowLazyQueueing) {
27585 // log('queue ele %s in layer %s', ele.id(), layer.id);
27586 self.queueLayer(layer, ele);
27587 } else {
27588 // log('draw ele %s in layer %s', ele.id(), layer.id);
27589 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27590 }
27591
27592 layer.eles.push(ele);
27593 caches[lvl] = layer;
27594 } // log('--');
27595
27596
27597 if (tmpLayers) {
27598 // then we only queued the current layerset and can't draw it yet
27599 return tmpLayers;
27600 }
27601
27602 if (allowLazyQueueing) {
27603 // log('lazy queue level', lvl);
27604 return null;
27605 }
27606
27607 return layers;
27608}; // a layer may want to use an ele cache of a higher level to avoid blurriness
27609// so the layer level might not equal the ele level
27610
27611
27612LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
27613 return lvl;
27614};
27615
27616LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
27617 var self = this;
27618 var r = this.renderer;
27619 var context = layer.context;
27620 var bb = ele.boundingBox();
27621
27622 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
27623 return;
27624 }
27625
27626 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
27627
27628 {
27629 r.setImgSmoothing(context, false);
27630 }
27631
27632 {
27633 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
27634 }
27635
27636 {
27637 r.setImgSmoothing(context, true);
27638 }
27639};
27640
27641LTCp.levelIsComplete = function (lvl, eles) {
27642 var self = this;
27643 var layers = self.layersByLevel[lvl];
27644
27645 if (!layers || layers.length === 0) {
27646 return false;
27647 }
27648
27649 var numElesInLayers = 0;
27650
27651 for (var i = 0; i < layers.length; i++) {
27652 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
27653
27654 if (layer.reqs > 0) {
27655 return false;
27656 } // if the layer is invalid, the level is not complete
27657
27658
27659 if (layer.invalid) {
27660 return false;
27661 }
27662
27663 numElesInLayers += layer.eles.length;
27664 } // we should have exactly the number of eles passed in to be complete
27665
27666
27667 if (numElesInLayers !== eles.length) {
27668 return false;
27669 }
27670
27671 return true;
27672};
27673
27674LTCp.validateLayersElesOrdering = function (lvl, eles) {
27675 var layers = this.layersByLevel[lvl];
27676
27677 if (!layers) {
27678 return;
27679 } // if in a layer the eles are not in the same order, then the layer is invalid
27680 // (i.e. there is an ele in between the eles in the layer)
27681
27682
27683 for (var i = 0; i < layers.length; i++) {
27684 var layer = layers[i];
27685 var offset = -1; // find the offset
27686
27687 for (var j = 0; j < eles.length; j++) {
27688 if (layer.eles[0] === eles[j]) {
27689 offset = j;
27690 break;
27691 }
27692 }
27693
27694 if (offset < 0) {
27695 // then the layer has nonexistant elements and is invalid
27696 this.invalidateLayer(layer);
27697 continue;
27698 } // the eles in the layer must be in the same continuous order, else the layer is invalid
27699
27700
27701 var o = offset;
27702
27703 for (var j = 0; j < layer.eles.length; j++) {
27704 if (layer.eles[j] !== eles[o + j]) {
27705 // log('invalidate based on ordering', layer.id);
27706 this.invalidateLayer(layer);
27707 break;
27708 }
27709 }
27710 }
27711};
27712
27713LTCp.updateElementsInLayers = function (eles, update) {
27714 var self = this;
27715 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
27716 // layer itself along the way
27717
27718 for (var i = 0; i < eles.length; i++) {
27719 var req = isEles ? null : eles[i];
27720 var ele = isEles ? eles[i] : eles[i].ele;
27721 var rs = ele._private.rscratch;
27722 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
27723
27724 for (var l = minLvl$1; l <= maxLvl$1; l++) {
27725 var layer = caches[l];
27726
27727 if (!layer) {
27728 continue;
27729 } // if update is a request from the ele cache, then it affects only
27730 // the matching level
27731
27732
27733 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
27734 continue;
27735 }
27736
27737 update(layer, ele, req);
27738 }
27739 }
27740};
27741
27742LTCp.haveLayers = function () {
27743 var self = this;
27744 var haveLayers = false;
27745
27746 for (var l = minLvl$1; l <= maxLvl$1; l++) {
27747 var layers = self.layersByLevel[l];
27748
27749 if (layers && layers.length > 0) {
27750 haveLayers = true;
27751 break;
27752 }
27753 }
27754
27755 return haveLayers;
27756};
27757
27758LTCp.invalidateElements = function (eles) {
27759 var self = this;
27760
27761 if (eles.length === 0) {
27762 return;
27763 }
27764
27765 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
27766
27767 if (eles.length === 0 || !self.haveLayers()) {
27768 return;
27769 }
27770
27771 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
27772 self.invalidateLayer(layer);
27773 });
27774};
27775
27776LTCp.invalidateLayer = function (layer) {
27777 // log('update invalidate layer time');
27778 this.lastInvalidationTime = performanceNow();
27779
27780 if (layer.invalid) {
27781 return;
27782 } // save cycles
27783
27784
27785 var lvl = layer.level;
27786 var eles = layer.eles;
27787 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
27788
27789 removeFromArray(layers, layer); // layer.eles = [];
27790
27791 layer.elesQueue = [];
27792 layer.invalid = true;
27793
27794 if (layer.replacement) {
27795 layer.replacement.invalid = true;
27796 }
27797
27798 for (var i = 0; i < eles.length; i++) {
27799 var caches = eles[i]._private.rscratch.imgLayerCaches;
27800
27801 if (caches) {
27802 caches[lvl] = null;
27803 }
27804 }
27805};
27806
27807LTCp.refineElementTextures = function (eles) {
27808 var self = this; // log('refine', eles.length);
27809
27810 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
27811 var rLyr = layer.replacement;
27812
27813 if (!rLyr) {
27814 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
27815 rLyr.replaces = layer;
27816 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
27817 }
27818
27819 if (!rLyr.reqs) {
27820 for (var i = 0; i < rLyr.eles.length; i++) {
27821 self.queueLayer(rLyr, rLyr.eles[i]);
27822 } // log('queue replacement layer refinement', rLyr.id);
27823
27824 }
27825 });
27826};
27827
27828LTCp.enqueueElementRefinement = function (ele) {
27829
27830 this.eleTxrDeqs.merge(ele);
27831 this.scheduleElementRefinement();
27832};
27833
27834LTCp.queueLayer = function (layer, ele) {
27835 var self = this;
27836 var q = self.layersQueue;
27837 var elesQ = layer.elesQueue;
27838 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
27839
27840 if (layer.replacement) {
27841 return;
27842 }
27843
27844 if (ele) {
27845 if (hasId[ele.id()]) {
27846 return;
27847 }
27848
27849 elesQ.push(ele);
27850 hasId[ele.id()] = true;
27851 }
27852
27853 if (layer.reqs) {
27854 layer.reqs++;
27855 q.updateItem(layer);
27856 } else {
27857 layer.reqs = 1;
27858 q.push(layer);
27859 }
27860};
27861
27862LTCp.dequeue = function (pxRatio) {
27863 var self = this;
27864 var q = self.layersQueue;
27865 var deqd = [];
27866 var eleDeqs = 0;
27867
27868 while (eleDeqs < maxDeqSize$1) {
27869 if (q.size() === 0) {
27870 break;
27871 }
27872
27873 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
27874
27875 if (layer.replacement) {
27876 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
27877 q.pop();
27878 continue;
27879 } // if this is a replacement layer that has been superceded, then forget it
27880
27881
27882 if (layer.replaces && layer !== layer.replaces.replacement) {
27883 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
27884 q.pop();
27885 continue;
27886 }
27887
27888 if (layer.invalid) {
27889 // log('replacement layer %s is invalid; dequeued', layer.id);
27890 q.pop();
27891 continue;
27892 }
27893
27894 var ele = layer.elesQueue.shift();
27895
27896 if (ele) {
27897 // log('dequeue layer %s', layer.id);
27898 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
27899 eleDeqs++;
27900 }
27901
27902 if (deqd.length === 0) {
27903 // we need only one entry in deqd to queue redrawing etc
27904 deqd.push(true);
27905 } // if the layer has all its eles done, then remove from the queue
27906
27907
27908 if (layer.elesQueue.length === 0) {
27909 q.pop();
27910 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
27911 // when a replacement layer is dequeued, it replaces the old layer in the level
27912
27913 if (layer.replaces) {
27914 self.applyLayerReplacement(layer);
27915 }
27916
27917 self.requestRedraw();
27918 }
27919 }
27920
27921 return deqd;
27922};
27923
27924LTCp.applyLayerReplacement = function (layer) {
27925 var self = this;
27926 var layersInLevel = self.layersByLevel[layer.level];
27927 var replaced = layer.replaces;
27928 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
27929 // refs would be a mistake (i.e. overwriting the true active layer)
27930
27931 if (index < 0 || replaced.invalid) {
27932 // log('replacement layer would have no effect', layer.id);
27933 return;
27934 }
27935
27936 layersInLevel[index] = layer; // replace level ref
27937 // replace refs in eles
27938
27939 for (var i = 0; i < layer.eles.length; i++) {
27940 var _p = layer.eles[i]._private;
27941 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
27942
27943 if (cache) {
27944 cache[layer.level] = layer;
27945 }
27946 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
27947
27948
27949 self.requestRedraw();
27950};
27951
27952LTCp.requestRedraw = util(function () {
27953 var r = this.renderer;
27954 r.redrawHint('eles', true);
27955 r.redrawHint('drag', true);
27956 r.redraw();
27957}, 100);
27958LTCp.setupDequeueing = defs.setupDequeueing({
27959 deqRedrawThreshold: deqRedrawThreshold$1,
27960 deqCost: deqCost$1,
27961 deqAvgCost: deqAvgCost$1,
27962 deqNoDrawCost: deqNoDrawCost$1,
27963 deqFastCost: deqFastCost$1,
27964 deq: function deq(self, pxRatio) {
27965 return self.dequeue(pxRatio);
27966 },
27967 onDeqd: noop,
27968 shouldRedraw: trueify,
27969 priority: function priority(self) {
27970 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
27971 }
27972});
27973
27974var CRp = {};
27975var impl;
27976
27977function polygon(context, points) {
27978 for (var i = 0; i < points.length; i++) {
27979 var pt = points[i];
27980 context.lineTo(pt.x, pt.y);
27981 }
27982}
27983
27984function triangleBackcurve(context, points, controlPoint) {
27985 var firstPt;
27986
27987 for (var i = 0; i < points.length; i++) {
27988 var pt = points[i];
27989
27990 if (i === 0) {
27991 firstPt = pt;
27992 }
27993
27994 context.lineTo(pt.x, pt.y);
27995 }
27996
27997 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
27998}
27999
28000function triangleTee(context, trianglePoints, teePoints) {
28001 if (context.beginPath) {
28002 context.beginPath();
28003 }
28004
28005 var triPts = trianglePoints;
28006
28007 for (var i = 0; i < triPts.length; i++) {
28008 var pt = triPts[i];
28009 context.lineTo(pt.x, pt.y);
28010 }
28011
28012 var teePts = teePoints;
28013 var firstTeePt = teePoints[0];
28014 context.moveTo(firstTeePt.x, firstTeePt.y);
28015
28016 for (var i = 1; i < teePts.length; i++) {
28017 var pt = teePts[i];
28018 context.lineTo(pt.x, pt.y);
28019 }
28020
28021 if (context.closePath) {
28022 context.closePath();
28023 }
28024}
28025
28026function circle(context, rx, ry, r) {
28027 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28028}
28029
28030CRp.arrowShapeImpl = function (name) {
28031 return (impl || (impl = {
28032 'polygon': polygon,
28033 'triangle-backcurve': triangleBackcurve,
28034 'triangle-tee': triangleTee,
28035 'triangle-cross': triangleTee,
28036 'circle': circle
28037 }))[name];
28038};
28039
28040var CRp$1 = {};
28041
28042CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28043 var r = this;
28044
28045 if (ele.isNode()) {
28046 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28047 } else {
28048 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28049 }
28050};
28051
28052CRp$1.drawElementOverlay = function (context, ele) {
28053 var r = this;
28054
28055 if (ele.isNode()) {
28056 r.drawNodeOverlay(context, ele);
28057 } else {
28058 r.drawEdgeOverlay(context, ele);
28059 }
28060};
28061
28062CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28063 var r = this;
28064 var bb = eleTxrCache.getBoundingBox(ele);
28065
28066 if (bb.w === 0 || bb.h === 0) {
28067 return;
28068 } // ignore zero size case
28069
28070
28071 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28072
28073 if (eleCache != null) {
28074 var opacity = getOpacity(r, ele);
28075
28076 if (opacity === 0) {
28077 return;
28078 }
28079
28080 var theta = getRotation(r, ele);
28081 var x1 = bb.x1,
28082 y1 = bb.y1,
28083 w = bb.w,
28084 h = bb.h;
28085 var x, y, sx, sy, smooth;
28086
28087 if (theta !== 0) {
28088 var rotPt = eleTxrCache.getRotationPoint(ele);
28089 sx = rotPt.x;
28090 sy = rotPt.y;
28091 context.translate(sx, sy);
28092 context.rotate(theta);
28093 smooth = r.getImgSmoothing(context);
28094
28095 if (!smooth) {
28096 r.setImgSmoothing(context, true);
28097 }
28098
28099 var off = eleTxrCache.getRotationOffset(ele);
28100 x = off.x;
28101 y = off.y;
28102 } else {
28103 x = x1;
28104 y = y1;
28105 }
28106
28107 var oldGlobalAlpha;
28108
28109 if (opacity !== 1) {
28110 oldGlobalAlpha = context.globalAlpha;
28111 context.globalAlpha = oldGlobalAlpha * opacity;
28112 }
28113
28114 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28115
28116 if (opacity !== 1) {
28117 context.globalAlpha = oldGlobalAlpha;
28118 }
28119
28120 if (theta !== 0) {
28121 context.rotate(-theta);
28122 context.translate(-sx, -sy);
28123
28124 if (!smooth) {
28125 r.setImgSmoothing(context, false);
28126 }
28127 }
28128 } else {
28129 eleTxrCache.drawElement(context, ele); // direct draw fallback
28130 }
28131};
28132
28133var getZeroRotation = function getZeroRotation() {
28134 return 0;
28135};
28136
28137var getLabelRotation = function getLabelRotation(r, ele) {
28138 return r.getTextAngle(ele, null);
28139};
28140
28141var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28142 return r.getTextAngle(ele, 'source');
28143};
28144
28145var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28146 return r.getTextAngle(ele, 'target');
28147};
28148
28149var getOpacity = function getOpacity(r, ele) {
28150 return ele.effectiveOpacity();
28151};
28152
28153var getTextOpacity = function getTextOpacity(e, ele) {
28154 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28155};
28156
28157CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28158 var r = this;
28159 var _r$data = r.data,
28160 eleTxrCache = _r$data.eleTxrCache,
28161 lblTxrCache = _r$data.lblTxrCache,
28162 slbTxrCache = _r$data.slbTxrCache,
28163 tlbTxrCache = _r$data.tlbTxrCache;
28164 var bb = ele.boundingBox();
28165 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28166
28167 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28168 return;
28169 }
28170
28171 if (!extent || boundingBoxesIntersect(bb, extent)) {
28172 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28173 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28174
28175 if (ele.isEdge()) {
28176 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28177 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28178 }
28179
28180 r.drawElementOverlay(context, ele);
28181 }
28182};
28183
28184CRp$1.drawElements = function (context, eles) {
28185 var r = this;
28186
28187 for (var i = 0; i < eles.length; i++) {
28188 var ele = eles[i];
28189 r.drawElement(context, ele);
28190 }
28191};
28192
28193CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28194 var r = this;
28195
28196 for (var i = 0; i < eles.length; i++) {
28197 var ele = eles[i];
28198 r.drawCachedElement(context, ele, pxRatio, extent);
28199 }
28200};
28201
28202CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28203 var r = this;
28204
28205 for (var i = 0; i < eles.length; i++) {
28206 var ele = eles[i];
28207
28208 if (!ele.isNode()) {
28209 continue;
28210 }
28211
28212 r.drawCachedElement(context, ele, pxRatio, extent);
28213 }
28214};
28215
28216CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28217 var r = this;
28218 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28219
28220 if (layers) {
28221 for (var i = 0; i < layers.length; i++) {
28222 var layer = layers[i];
28223 var bb = layer.bb;
28224
28225 if (bb.w === 0 || bb.h === 0) {
28226 continue;
28227 }
28228
28229 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28230 }
28231 } else {
28232 // fall back on plain caching if no layers
28233 r.drawCachedElements(context, eles, pxRatio, extent);
28234 }
28235};
28236
28237/* global Path2D */
28238var CRp$2 = {};
28239
28240CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28241 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28242 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28243 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28244 var r = this;
28245 var rs = edge._private.rscratch;
28246
28247 if (shouldDrawOpacity && !edge.visible()) {
28248 return;
28249 } // if bezier ctrl pts can not be calculated, then die
28250
28251
28252 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28253 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28254 return;
28255 }
28256
28257 var bb;
28258
28259 if (shiftToOriginWithBb) {
28260 bb = shiftToOriginWithBb;
28261 context.translate(-bb.x1, -bb.y1);
28262 }
28263
28264 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28265 var lineStyle = edge.pstyle('line-style').value;
28266 var edgeWidth = edge.pstyle('width').pfValue;
28267 var lineCap = edge.pstyle('line-cap').value;
28268
28269 var drawLine = function drawLine() {
28270 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28271 context.lineWidth = edgeWidth;
28272 context.lineCap = lineCap;
28273 r.eleStrokeStyle(context, edge, strokeOpacity);
28274 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28275 context.lineCap = 'butt'; // reset for other drawing functions
28276 };
28277
28278 var drawOverlay = function drawOverlay() {
28279 if (!shouldDrawOverlay) {
28280 return;
28281 }
28282
28283 r.drawEdgeOverlay(context, edge);
28284 };
28285
28286 var drawArrows = function drawArrows() {
28287 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28288 r.drawArrowheads(context, edge, arrowOpacity);
28289 };
28290
28291 var drawText = function drawText() {
28292 r.drawElementText(context, edge, null, drawLabel);
28293 };
28294
28295 context.lineJoin = 'round';
28296 var ghost = edge.pstyle('ghost').value === 'yes';
28297
28298 if (ghost) {
28299 var gx = edge.pstyle('ghost-offset-x').pfValue;
28300 var gy = edge.pstyle('ghost-offset-y').pfValue;
28301 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28302 var effectiveGhostOpacity = opacity * ghostOpacity;
28303 context.translate(gx, gy);
28304 drawLine(effectiveGhostOpacity);
28305 drawArrows(effectiveGhostOpacity);
28306 context.translate(-gx, -gy);
28307 }
28308
28309 drawLine();
28310 drawArrows();
28311 drawOverlay();
28312 drawText();
28313
28314 if (shiftToOriginWithBb) {
28315 context.translate(bb.x1, bb.y1);
28316 }
28317};
28318
28319CRp$2.drawEdgeOverlay = function (context, edge) {
28320 if (!edge.visible()) {
28321 return;
28322 }
28323
28324 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28325
28326 if (overlayOpacity === 0) {
28327 return;
28328 }
28329
28330 var r = this;
28331 var usePaths = r.usePaths();
28332 var rs = edge._private.rscratch;
28333 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28334 var overlayWidth = 2 * overlayPadding;
28335 var overlayColor = edge.pstyle('overlay-color').value;
28336 context.lineWidth = overlayWidth;
28337
28338 if (rs.edgeType === 'self' && !usePaths) {
28339 context.lineCap = 'butt';
28340 } else {
28341 context.lineCap = 'round';
28342 }
28343
28344 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28345 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28346};
28347
28348CRp$2.drawEdgePath = function (edge, context, pts, type) {
28349 var rs = edge._private.rscratch;
28350 var canvasCxt = context;
28351 var path;
28352 var pathCacheHit = false;
28353 var usePaths = this.usePaths();
28354 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28355 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28356
28357 if (usePaths) {
28358 var pathCacheKey = pts.join('$');
28359 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28360
28361 if (keyMatches) {
28362 path = context = rs.pathCache;
28363 pathCacheHit = true;
28364 } else {
28365 path = context = new Path2D();
28366 rs.pathCacheKey = pathCacheKey;
28367 rs.pathCache = path;
28368 }
28369 }
28370
28371 if (canvasCxt.setLineDash) {
28372 // for very outofdate browsers
28373 switch (type) {
28374 case 'dotted':
28375 canvasCxt.setLineDash([1, 1]);
28376 break;
28377
28378 case 'dashed':
28379 canvasCxt.setLineDash(lineDashPattern);
28380 canvasCxt.lineDashOffset = lineDashOffset;
28381 break;
28382
28383 case 'solid':
28384 canvasCxt.setLineDash([]);
28385 break;
28386 }
28387 }
28388
28389 if (!pathCacheHit && !rs.badLine) {
28390 if (context.beginPath) {
28391 context.beginPath();
28392 }
28393
28394 context.moveTo(pts[0], pts[1]);
28395
28396 switch (rs.edgeType) {
28397 case 'bezier':
28398 case 'self':
28399 case 'compound':
28400 case 'multibezier':
28401 for (var i = 2; i + 3 < pts.length; i += 4) {
28402 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28403 }
28404
28405 break;
28406
28407 case 'straight':
28408 case 'segments':
28409 case 'haystack':
28410 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28411 context.lineTo(pts[_i], pts[_i + 1]);
28412 }
28413
28414 break;
28415 }
28416 }
28417
28418 context = canvasCxt;
28419
28420 if (usePaths) {
28421 context.stroke(path);
28422 } else {
28423 context.stroke();
28424 } // reset any line dashes
28425
28426
28427 if (context.setLineDash) {
28428 // for very outofdate browsers
28429 context.setLineDash([]);
28430 }
28431};
28432
28433CRp$2.drawArrowheads = function (context, edge, opacity) {
28434 var rs = edge._private.rscratch;
28435 var isHaystack = rs.edgeType === 'haystack';
28436
28437 if (!isHaystack) {
28438 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28439 }
28440
28441 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28442 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28443
28444 if (!isHaystack) {
28445 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28446 }
28447};
28448
28449CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28450 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28451 return;
28452 }
28453
28454 var self = this;
28455 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28456
28457 if (arrowShape === 'none') {
28458 return;
28459 }
28460
28461 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28462 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28463 var edgeWidth = edge.pstyle('width').pfValue;
28464 var edgeOpacity = edge.pstyle('opacity').value;
28465
28466 if (opacity === undefined) {
28467 opacity = edgeOpacity;
28468 }
28469
28470 var gco = context.globalCompositeOperation;
28471
28472 if (opacity !== 1 || arrowFill === 'hollow') {
28473 // then extra clear is needed
28474 context.globalCompositeOperation = 'destination-out';
28475 self.colorFillStyle(context, 255, 255, 255, 1);
28476 self.colorStrokeStyle(context, 255, 255, 255, 1);
28477 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28478 context.globalCompositeOperation = gco;
28479 } // otherwise, the opaque arrow clears it for free :)
28480
28481
28482 var color = edge.pstyle(prefix + '-arrow-color').value;
28483 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28484 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28485 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28486};
28487
28488CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28489 var r = this;
28490 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28491 var pathCacheHit = false;
28492 var path;
28493 var canvasContext = context;
28494 var translation = {
28495 x: x,
28496 y: y
28497 };
28498 var scale = edge.pstyle('arrow-scale').value;
28499 var size = this.getArrowWidth(edgeWidth, scale);
28500 var shapeImpl = r.arrowShapes[shape];
28501
28502 if (usePaths) {
28503 var cache = r.arrowPathCache = r.arrowPathCache || [];
28504 var key = hashString(shape);
28505 var cachedPath = cache[key];
28506
28507 if (cachedPath != null) {
28508 path = context = cachedPath;
28509 pathCacheHit = true;
28510 } else {
28511 path = context = new Path2D();
28512 cache[key] = path;
28513 }
28514 }
28515
28516 if (context.beginPath) {
28517 context.beginPath();
28518 }
28519
28520 if (!pathCacheHit) {
28521 if (usePaths) {
28522 // store in the path cache with values easily manipulated later
28523 shapeImpl.draw(context, 1, 0, {
28524 x: 0,
28525 y: 0
28526 }, 1);
28527 } else {
28528 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28529 }
28530 }
28531
28532 if (context.closePath) {
28533 context.closePath();
28534 }
28535
28536 context = canvasContext;
28537
28538 if (usePaths) {
28539 // set transform to arrow position/orientation
28540 context.translate(x, y);
28541 context.rotate(angle);
28542 context.scale(size, size);
28543 }
28544
28545 if (fill === 'filled' || fill === 'both') {
28546 if (usePaths) {
28547 context.fill(path);
28548 } else {
28549 context.fill();
28550 }
28551 }
28552
28553 if (fill === 'hollow' || fill === 'both') {
28554 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28555 context.lineJoin = 'miter';
28556
28557 if (usePaths) {
28558 context.stroke(path);
28559 } else {
28560 context.stroke();
28561 }
28562 }
28563
28564 if (usePaths) {
28565 // reset transform by applying inverse
28566 context.scale(1 / size, 1 / size);
28567 context.rotate(-angle);
28568 context.translate(-x, -y);
28569 }
28570};
28571
28572var CRp$3 = {};
28573
28574CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28575 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28576 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
28577 return;
28578 }
28579
28580 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
28581};
28582
28583CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
28584 var r = this;
28585 var pos = node.position();
28586 var nodeX = pos.x;
28587 var nodeY = pos.y;
28588 var styleObj = node.cy().style();
28589 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
28590 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
28591 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
28592 var nodeW = node.width();
28593 var nodeH = node.height();
28594 var paddingX2 = node.padding() * 2;
28595 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28596 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28597 var rs = node._private.rscratch;
28598 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
28599 var shouldClip = clip === 'node';
28600 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
28601 var imgW = img.width || img.cachedW;
28602 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
28603
28604 if (null == imgW || null == imgH) {
28605 document.body.appendChild(img); // eslint-disable-line no-undef
28606
28607 imgW = img.cachedW = img.width || img.offsetWidth;
28608 imgH = img.cachedH = img.height || img.offsetHeight;
28609 document.body.removeChild(img); // eslint-disable-line no-undef
28610 }
28611
28612 var w = imgW;
28613 var h = imgH;
28614
28615 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
28616 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
28617 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
28618 } else {
28619 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
28620 }
28621 }
28622
28623 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
28624 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
28625 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
28626 } else {
28627 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
28628 }
28629 }
28630
28631 if (w === 0 || h === 0) {
28632 return; // no point in drawing empty image (and chrome is broken in this case)
28633 }
28634
28635 if (fit === 'contain') {
28636 var scale = Math.min(nodeTW / w, nodeTH / h);
28637 w *= scale;
28638 h *= scale;
28639 } else if (fit === 'cover') {
28640 var scale = Math.max(nodeTW / w, nodeTH / h);
28641 w *= scale;
28642 h *= scale;
28643 }
28644
28645 var x = nodeX - nodeTW / 2; // left
28646
28647 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
28648 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
28649
28650 if (posXUnits === '%') {
28651 x += (nodeTW - w) * posXPfVal;
28652 } else {
28653 x += posXPfVal;
28654 }
28655
28656 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
28657 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
28658
28659 if (offXUnits === '%') {
28660 x += (nodeTW - w) * offXPfVal;
28661 } else {
28662 x += offXPfVal;
28663 }
28664
28665 var y = nodeY - nodeTH / 2; // top
28666
28667 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
28668 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
28669
28670 if (posYUnits === '%') {
28671 y += (nodeTH - h) * posYPfVal;
28672 } else {
28673 y += posYPfVal;
28674 }
28675
28676 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
28677 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
28678
28679 if (offYUnits === '%') {
28680 y += (nodeTH - h) * offYPfVal;
28681 } else {
28682 y += offYPfVal;
28683 }
28684
28685 if (rs.pathCache) {
28686 x -= nodeX;
28687 y -= nodeY;
28688 nodeX = 0;
28689 nodeY = 0;
28690 }
28691
28692 var gAlpha = context.globalAlpha;
28693 context.globalAlpha = imgOpacity;
28694
28695 if (repeat === 'no-repeat') {
28696 if (shouldClip) {
28697 context.save();
28698
28699 if (rs.pathCache) {
28700 context.clip(rs.pathCache);
28701 } else {
28702 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
28703 context.clip();
28704 }
28705 }
28706
28707 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
28708
28709 if (shouldClip) {
28710 context.restore();
28711 }
28712 } else {
28713 var pattern = context.createPattern(img, repeat);
28714 context.fillStyle = pattern;
28715 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
28716 context.translate(x, y);
28717 context.fill();
28718 context.translate(-x, -y);
28719 }
28720
28721 context.globalAlpha = gAlpha;
28722};
28723
28724var CRp$4 = {};
28725
28726CRp$4.eleTextBiggerThanMin = function (ele, scale) {
28727 if (!scale) {
28728 var zoom = ele.cy().zoom();
28729 var pxRatio = this.getPixelRatio();
28730 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
28731
28732 scale = Math.pow(2, lvl);
28733 }
28734
28735 var computedSize = ele.pstyle('font-size').pfValue * scale;
28736 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
28737
28738 if (computedSize < minSize) {
28739 return false;
28740 }
28741
28742 return true;
28743};
28744
28745CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
28746 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28747 var r = this;
28748
28749 if (force == null) {
28750 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
28751 return;
28752 }
28753 } else if (force === false) {
28754 return;
28755 }
28756
28757 if (ele.isNode()) {
28758 var label = ele.pstyle('label');
28759
28760 if (!label || !label.value) {
28761 return;
28762 }
28763
28764 var justification = r.getLabelJustification(ele);
28765 context.textAlign = justification;
28766 context.textBaseline = 'bottom';
28767 } else {
28768 var _label = ele.pstyle('label');
28769
28770 var srcLabel = ele.pstyle('source-label');
28771 var tgtLabel = ele.pstyle('target-label');
28772
28773 if ((!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
28774 return;
28775 }
28776
28777 context.textAlign = 'center';
28778 context.textBaseline = 'bottom';
28779 }
28780
28781 var applyRotation = !shiftToOriginWithBb;
28782 var bb;
28783
28784 if (shiftToOriginWithBb) {
28785 bb = shiftToOriginWithBb;
28786 context.translate(-bb.x1, -bb.y1);
28787 }
28788
28789 if (prefix == null) {
28790 r.drawText(context, ele, null, applyRotation, useEleOpacity);
28791
28792 if (ele.isEdge()) {
28793 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
28794 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
28795 }
28796 } else {
28797 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
28798 }
28799
28800 if (shiftToOriginWithBb) {
28801 context.translate(bb.x1, bb.y1);
28802 }
28803};
28804
28805CRp$4.getFontCache = function (context) {
28806 var cache;
28807 this.fontCaches = this.fontCaches || [];
28808
28809 for (var i = 0; i < this.fontCaches.length; i++) {
28810 cache = this.fontCaches[i];
28811
28812 if (cache.context === context) {
28813 return cache;
28814 }
28815 }
28816
28817 cache = {
28818 context: context
28819 };
28820 this.fontCaches.push(cache);
28821 return cache;
28822}; // set up canvas context with font
28823// returns transformed text string
28824
28825
28826CRp$4.setupTextStyle = function (context, ele) {
28827 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
28828 // Font style
28829 var labelStyle = ele.pstyle('font-style').strValue;
28830 var labelSize = ele.pstyle('font-size').pfValue + 'px';
28831 var labelFamily = ele.pstyle('font-family').strValue;
28832 var labelWeight = ele.pstyle('font-weight').strValue;
28833 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
28834 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
28835 var color = ele.pstyle('color').value;
28836 var outlineColor = ele.pstyle('text-outline-color').value;
28837 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
28838 context.lineJoin = 'round'; // so text outlines aren't jagged
28839
28840 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
28841 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
28842}; // TODO ensure re-used
28843
28844
28845function roundRect(ctx, x, y, width, height) {
28846 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
28847 ctx.beginPath();
28848 ctx.moveTo(x + radius, y);
28849 ctx.lineTo(x + width - radius, y);
28850 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
28851 ctx.lineTo(x + width, y + height - radius);
28852 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
28853 ctx.lineTo(x + radius, y + height);
28854 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
28855 ctx.lineTo(x, y + radius);
28856 ctx.quadraticCurveTo(x, y, x + radius, y);
28857 ctx.closePath();
28858 ctx.fill();
28859}
28860
28861CRp$4.getTextAngle = function (ele, prefix) {
28862 var theta;
28863 var _p = ele._private;
28864 var rscratch = _p.rscratch;
28865 var pdash = prefix ? prefix + '-' : '';
28866 var rotation = ele.pstyle(pdash + 'text-rotation');
28867 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
28868
28869 if (rotation.strValue === 'autorotate') {
28870 theta = ele.isEdge() ? textAngle : 0;
28871 } else if (rotation.strValue === 'none') {
28872 theta = 0;
28873 } else {
28874 theta = rotation.pfValue;
28875 }
28876
28877 return theta;
28878};
28879
28880CRp$4.drawText = function (context, ele, prefix) {
28881 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28882 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28883 var _p = ele._private;
28884 var rscratch = _p.rscratch;
28885 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
28886
28887 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
28888 return;
28889 } // use 'main' as an alias for the main label (i.e. null prefix)
28890
28891
28892 if (prefix === 'main') {
28893 prefix = null;
28894 }
28895
28896 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
28897 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
28898 var orgTextX, orgTextY; // used for rotation
28899
28900 var text = this.getLabelText(ele, prefix);
28901
28902 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
28903 this.setupTextStyle(context, ele, useEleOpacity);
28904 var pdash = prefix ? prefix + '-' : '';
28905 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
28906 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
28907 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
28908 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
28909 var isEdge = ele.isEdge();
28910 var halign = ele.pstyle('text-halign').value;
28911 var valign = ele.pstyle('text-valign').value;
28912
28913 if (isEdge) {
28914 halign = 'center';
28915 valign = 'center';
28916 }
28917
28918 textX += marginX;
28919 textY += marginY;
28920 var theta;
28921
28922 if (!applyRotation) {
28923 theta = 0;
28924 } else {
28925 theta = this.getTextAngle(ele, prefix);
28926 }
28927
28928 if (theta !== 0) {
28929 orgTextX = textX;
28930 orgTextY = textY;
28931 context.translate(orgTextX, orgTextY);
28932 context.rotate(theta);
28933 textX = 0;
28934 textY = 0;
28935 }
28936
28937 switch (valign) {
28938 case 'top':
28939 break;
28940
28941 case 'center':
28942 textY += textH / 2;
28943 break;
28944
28945 case 'bottom':
28946 textY += textH;
28947 break;
28948 }
28949
28950 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
28951 var borderOpacity = ele.pstyle('text-border-opacity').value;
28952 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
28953 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
28954
28955 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
28956 var bgX = textX - backgroundPadding;
28957
28958 switch (halign) {
28959 case 'left':
28960 bgX -= textW;
28961 break;
28962
28963 case 'center':
28964 bgX -= textW / 2;
28965 break;
28966
28967 case 'right':
28968 break;
28969 }
28970
28971 var bgY = textY - textH - backgroundPadding;
28972 var bgW = textW + 2 * backgroundPadding;
28973 var bgH = textH + 2 * backgroundPadding;
28974
28975 if (backgroundOpacity > 0) {
28976 var textFill = context.fillStyle;
28977 var textBackgroundColor = ele.pstyle('text-background-color').value;
28978 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
28979 var styleShape = ele.pstyle('text-background-shape').strValue;
28980
28981 if (styleShape.indexOf('round') === 0) {
28982 roundRect(context, bgX, bgY, bgW, bgH, 2);
28983 } else {
28984 context.fillRect(bgX, bgY, bgW, bgH);
28985 }
28986
28987 context.fillStyle = textFill;
28988 }
28989
28990 if (textBorderWidth > 0 && borderOpacity > 0) {
28991 var textStroke = context.strokeStyle;
28992 var textLineWidth = context.lineWidth;
28993 var textBorderColor = ele.pstyle('text-border-color').value;
28994 var textBorderStyle = ele.pstyle('text-border-style').value;
28995 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
28996 context.lineWidth = textBorderWidth;
28997
28998 if (context.setLineDash) {
28999 // for very outofdate browsers
29000 switch (textBorderStyle) {
29001 case 'dotted':
29002 context.setLineDash([1, 1]);
29003 break;
29004
29005 case 'dashed':
29006 context.setLineDash([4, 2]);
29007 break;
29008
29009 case 'double':
29010 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29011
29012 context.setLineDash([]);
29013 break;
29014
29015 case 'solid':
29016 context.setLineDash([]);
29017 break;
29018 }
29019 }
29020
29021 context.strokeRect(bgX, bgY, bgW, bgH);
29022
29023 if (textBorderStyle === 'double') {
29024 var whiteWidth = textBorderWidth / 2;
29025 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29026 }
29027
29028 if (context.setLineDash) {
29029 // for very outofdate browsers
29030 context.setLineDash([]);
29031 }
29032
29033 context.lineWidth = textLineWidth;
29034 context.strokeStyle = textStroke;
29035 }
29036 }
29037
29038 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29039
29040 if (lineWidth > 0) {
29041 context.lineWidth = lineWidth;
29042 }
29043
29044 if (ele.pstyle('text-wrap').value === 'wrap') {
29045 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29046 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29047 var halfTextW = textW / 2;
29048 var justification = this.getLabelJustification(ele);
29049
29050 if (justification === 'auto') ; else if (halign === 'left') {
29051 // auto justification : right
29052 if (justification === 'left') {
29053 textX += -textW;
29054 } else if (justification === 'center') {
29055 textX += -halfTextW;
29056 } // else same as auto
29057
29058 } else if (halign === 'center') {
29059 // auto justfication : center
29060 if (justification === 'left') {
29061 textX += -halfTextW;
29062 } else if (justification === 'right') {
29063 textX += halfTextW;
29064 } // else same as auto
29065
29066 } else if (halign === 'right') {
29067 // auto justification : left
29068 if (justification === 'center') {
29069 textX += halfTextW;
29070 } else if (justification === 'right') {
29071 textX += textW;
29072 } // else same as auto
29073
29074 }
29075
29076 switch (valign) {
29077 case 'top':
29078 textY -= (lines.length - 1) * lineHeight;
29079 break;
29080
29081 case 'center':
29082 case 'bottom':
29083 textY -= (lines.length - 1) * lineHeight;
29084 break;
29085 }
29086
29087 for (var l = 0; l < lines.length; l++) {
29088 if (lineWidth > 0) {
29089 context.strokeText(lines[l], textX, textY);
29090 }
29091
29092 context.fillText(lines[l], textX, textY);
29093 textY += lineHeight;
29094 }
29095 } else {
29096 if (lineWidth > 0) {
29097 context.strokeText(text, textX, textY);
29098 }
29099
29100 context.fillText(text, textX, textY);
29101 }
29102
29103 if (theta !== 0) {
29104 context.rotate(-theta);
29105 context.translate(-orgTextX, -orgTextY);
29106 }
29107 }
29108};
29109
29110/* global Path2D */
29111var CRp$5 = {};
29112
29113CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29114 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29115 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29116 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29117 var r = this;
29118 var nodeWidth, nodeHeight;
29119 var _p = node._private;
29120 var rs = _p.rscratch;
29121 var pos = node.position();
29122
29123 if (!number(pos.x) || !number(pos.y)) {
29124 return; // can't draw node with undefined position
29125 }
29126
29127 if (shouldDrawOpacity && !node.visible()) {
29128 return;
29129 }
29130
29131 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29132 var usePaths = r.usePaths();
29133 var path;
29134 var pathCacheHit = false;
29135 var padding = node.padding();
29136 nodeWidth = node.width() + 2 * padding;
29137 nodeHeight = node.height() + 2 * padding; //
29138 // setup shift
29139
29140 var bb;
29141
29142 if (shiftToOriginWithBb) {
29143 bb = shiftToOriginWithBb;
29144 context.translate(-bb.x1, -bb.y1);
29145 } //
29146 // load bg image
29147
29148
29149 var bgImgProp = node.pstyle('background-image');
29150 var urls = bgImgProp.value;
29151 var urlDefined = new Array(urls.length);
29152 var image = new Array(urls.length);
29153 var numImages = 0;
29154
29155 for (var i = 0; i < urls.length; i++) {
29156 var url = urls[i];
29157 var defd = urlDefined[i] = url != null && url !== 'none';
29158
29159 if (defd) {
29160 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29161 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29162
29163 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29164 _p.backgroundTimestamp = Date.now();
29165 node.emitAndNotify('background');
29166 });
29167 }
29168 } //
29169 // setup styles
29170
29171
29172 var darkness = node.pstyle('background-blacken').value;
29173 var borderWidth = node.pstyle('border-width').pfValue;
29174 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29175 var borderColor = node.pstyle('border-color').value;
29176 var borderStyle = node.pstyle('border-style').value;
29177 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29178 context.lineJoin = 'miter'; // so borders are square with the node shape
29179
29180 var setupShapeColor = function setupShapeColor() {
29181 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29182 r.eleFillStyle(context, node, bgOpy);
29183 };
29184
29185 var setupBorderColor = function setupBorderColor() {
29186 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29187 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29188 }; //
29189 // setup shape
29190
29191
29192 var styleShape = node.pstyle('shape').strValue;
29193 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29194
29195 if (usePaths) {
29196 context.translate(pos.x, pos.y);
29197 var pathCache = r.nodePathCache = r.nodePathCache || [];
29198 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29199 var cachedPath = pathCache[key];
29200
29201 if (cachedPath != null) {
29202 path = cachedPath;
29203 pathCacheHit = true;
29204 rs.pathCache = path;
29205 } else {
29206 path = new Path2D();
29207 pathCache[key] = rs.pathCache = path;
29208 }
29209 }
29210
29211 var drawShape = function drawShape() {
29212 if (!pathCacheHit) {
29213 var npos = pos;
29214
29215 if (usePaths) {
29216 npos = {
29217 x: 0,
29218 y: 0
29219 };
29220 }
29221
29222 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29223 }
29224
29225 if (usePaths) {
29226 context.fill(path);
29227 } else {
29228 context.fill();
29229 }
29230 };
29231
29232 var drawImages = function drawImages() {
29233 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29234 var prevBging = _p.backgrounding;
29235 var totalCompleted = 0;
29236
29237 for (var _i = 0; _i < image.length; _i++) {
29238 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29239 totalCompleted++;
29240 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29241 }
29242 }
29243
29244 _p.backgrounding = !(totalCompleted === numImages);
29245
29246 if (prevBging !== _p.backgrounding) {
29247 // update style b/c :backgrounding state changed
29248 node.updateStyle(false);
29249 }
29250 };
29251
29252 var drawPie = function drawPie() {
29253 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29254 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29255
29256 if (r.hasPie(node)) {
29257 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29258
29259 if (redrawShape) {
29260 if (!usePaths) {
29261 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29262 }
29263 }
29264 }
29265 };
29266
29267 var darken = function darken() {
29268 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29269 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29270 var c = darkness > 0 ? 0 : 255;
29271
29272 if (darkness !== 0) {
29273 r.colorFillStyle(context, c, c, c, opacity);
29274
29275 if (usePaths) {
29276 context.fill(path);
29277 } else {
29278 context.fill();
29279 }
29280 }
29281 };
29282
29283 var drawBorder = function drawBorder() {
29284 if (borderWidth > 0) {
29285 context.lineWidth = borderWidth;
29286 context.lineCap = 'butt';
29287
29288 if (context.setLineDash) {
29289 // for very outofdate browsers
29290 switch (borderStyle) {
29291 case 'dotted':
29292 context.setLineDash([1, 1]);
29293 break;
29294
29295 case 'dashed':
29296 context.setLineDash([4, 2]);
29297 break;
29298
29299 case 'solid':
29300 case 'double':
29301 context.setLineDash([]);
29302 break;
29303 }
29304 }
29305
29306 if (usePaths) {
29307 context.stroke(path);
29308 } else {
29309 context.stroke();
29310 }
29311
29312 if (borderStyle === 'double') {
29313 context.lineWidth = borderWidth / 3;
29314 var gco = context.globalCompositeOperation;
29315 context.globalCompositeOperation = 'destination-out';
29316
29317 if (usePaths) {
29318 context.stroke(path);
29319 } else {
29320 context.stroke();
29321 }
29322
29323 context.globalCompositeOperation = gco;
29324 } // reset in case we changed the border style
29325
29326
29327 if (context.setLineDash) {
29328 // for very outofdate browsers
29329 context.setLineDash([]);
29330 }
29331 }
29332 };
29333
29334 var drawOverlay = function drawOverlay() {
29335 if (shouldDrawOverlay) {
29336 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29337 }
29338 };
29339
29340 var drawText = function drawText() {
29341 r.drawElementText(context, node, null, drawLabel);
29342 };
29343
29344 var ghost = node.pstyle('ghost').value === 'yes';
29345
29346 if (ghost) {
29347 var gx = node.pstyle('ghost-offset-x').pfValue;
29348 var gy = node.pstyle('ghost-offset-y').pfValue;
29349 var ghostOpacity = node.pstyle('ghost-opacity').value;
29350 var effGhostOpacity = ghostOpacity * eleOpacity;
29351 context.translate(gx, gy);
29352 setupShapeColor(ghostOpacity * bgOpacity);
29353 drawShape();
29354 drawImages(effGhostOpacity);
29355 drawPie(darkness !== 0 || borderWidth !== 0);
29356 darken(effGhostOpacity);
29357 setupBorderColor(ghostOpacity * borderOpacity);
29358 drawBorder();
29359 context.translate(-gx, -gy);
29360 }
29361
29362 setupShapeColor();
29363 drawShape();
29364 drawImages();
29365 drawPie(darkness !== 0 || borderWidth !== 0);
29366 darken();
29367 setupBorderColor();
29368 drawBorder();
29369
29370 if (usePaths) {
29371 context.translate(-pos.x, -pos.y);
29372 }
29373
29374 drawText();
29375 drawOverlay(); //
29376 // clean up shift
29377
29378 if (shiftToOriginWithBb) {
29379 context.translate(bb.x1, bb.y1);
29380 }
29381};
29382
29383CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29384 var r = this;
29385
29386 if (!node.visible()) {
29387 return;
29388 }
29389
29390 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29391 var overlayOpacity = node.pstyle('overlay-opacity').value;
29392 var overlayColor = node.pstyle('overlay-color').value;
29393
29394 if (overlayOpacity > 0) {
29395 pos = pos || node.position();
29396
29397 if (nodeWidth == null || nodeHeight == null) {
29398 var padding = node.padding();
29399 nodeWidth = node.width() + 2 * padding;
29400 nodeHeight = node.height() + 2 * padding;
29401 }
29402
29403 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29404 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29405 context.fill();
29406 }
29407}; // does the node have at least one pie piece?
29408
29409
29410CRp$5.hasPie = function (node) {
29411 node = node[0]; // ensure ele ref
29412
29413 return node._private.hasPie;
29414};
29415
29416CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29417 node = node[0]; // ensure ele ref
29418
29419 pos = pos || node.position();
29420 var cyStyle = node.cy().style();
29421 var pieSize = node.pstyle('pie-size');
29422 var x = pos.x;
29423 var y = pos.y;
29424 var nodeW = node.width();
29425 var nodeH = node.height();
29426 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29427
29428 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29429
29430 var usePaths = this.usePaths();
29431
29432 if (usePaths) {
29433 x = 0;
29434 y = 0;
29435 }
29436
29437 if (pieSize.units === '%') {
29438 radius = radius * pieSize.pfValue;
29439 } else if (pieSize.pfValue !== undefined) {
29440 radius = pieSize.pfValue / 2;
29441 }
29442
29443 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29444 // 1..N
29445 var size = node.pstyle('pie-' + i + '-background-size').value;
29446 var color = node.pstyle('pie-' + i + '-background-color').value;
29447 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29448 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29449 // percent can't push beyond 1
29450
29451 if (percent + lastPercent > 1) {
29452 percent = 1 - lastPercent;
29453 }
29454
29455 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29456
29457 var angleDelta = 2 * Math.PI * percent;
29458 var angleEnd = angleStart + angleDelta; // ignore if
29459 // - zero size
29460 // - we're already beyond the full circle
29461 // - adding the current slice would go beyond the full circle
29462
29463 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29464 continue;
29465 }
29466
29467 context.beginPath();
29468 context.moveTo(x, y);
29469 context.arc(x, y, radius, angleStart, angleEnd);
29470 context.closePath();
29471 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29472 context.fill();
29473 lastPercent += percent;
29474 }
29475};
29476
29477var CRp$6 = {};
29478var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29479
29480CRp$6.getPixelRatio = function () {
29481 var context = this.data.contexts[0];
29482
29483 if (this.forcedPixelRatio != null) {
29484 return this.forcedPixelRatio;
29485 }
29486
29487 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29488 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29489};
29490
29491CRp$6.paintCache = function (context) {
29492 var caches = this.paintCaches = this.paintCaches || [];
29493 var needToCreateCache = true;
29494 var cache;
29495
29496 for (var i = 0; i < caches.length; i++) {
29497 cache = caches[i];
29498
29499 if (cache.context === context) {
29500 needToCreateCache = false;
29501 break;
29502 }
29503 }
29504
29505 if (needToCreateCache) {
29506 cache = {
29507 context: context
29508 };
29509 caches.push(cache);
29510 }
29511
29512 return cache;
29513};
29514
29515CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29516 var gradientStyle;
29517 var usePaths = this.usePaths();
29518 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29519 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29520
29521 if (fill === 'radial-gradient') {
29522 if (ele.isEdge()) {
29523 var start = ele.sourceEndpoint(),
29524 end = ele.targetEndpoint(),
29525 mid = ele.midpoint();
29526 var d1 = dist(start, mid);
29527 var d2 = dist(end, mid);
29528 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29529 } else {
29530 var pos = usePaths ? {
29531 x: 0,
29532 y: 0
29533 } : ele.position(),
29534 width = ele.paddedWidth(),
29535 height = ele.paddedHeight();
29536 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29537 }
29538 } else {
29539 if (ele.isEdge()) {
29540 var _start = ele.sourceEndpoint(),
29541 _end = ele.targetEndpoint();
29542
29543 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29544 } else {
29545 var _pos = usePaths ? {
29546 x: 0,
29547 y: 0
29548 } : ele.position(),
29549 _width = ele.paddedWidth(),
29550 _height = ele.paddedHeight(),
29551 halfWidth = _width / 2,
29552 halfHeight = _height / 2;
29553
29554 var direction = ele.pstyle('background-gradient-direction').value;
29555
29556 switch (direction) {
29557 case 'to-bottom':
29558 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
29559 break;
29560
29561 case 'to-top':
29562 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
29563 break;
29564
29565 case 'to-left':
29566 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
29567 break;
29568
29569 case 'to-right':
29570 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
29571 break;
29572
29573 case 'to-bottom-right':
29574 case 'to-right-bottom':
29575 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
29576 break;
29577
29578 case 'to-top-right':
29579 case 'to-right-top':
29580 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
29581 break;
29582
29583 case 'to-bottom-left':
29584 case 'to-left-bottom':
29585 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
29586 break;
29587
29588 case 'to-top-left':
29589 case 'to-left-top':
29590 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
29591 break;
29592 }
29593 }
29594 }
29595
29596 if (!gradientStyle) return null; // invalid gradient style
29597
29598 var hasPositions = positions.length === colors.length;
29599 var length = colors.length;
29600
29601 for (var i = 0; i < length; i++) {
29602 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
29603 }
29604
29605 return gradientStyle;
29606};
29607
29608CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
29609 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
29610 if (!gradientStyle) return null; // error
29611
29612 context.fillStyle = gradientStyle;
29613};
29614
29615CRp$6.colorFillStyle = function (context, r, g, b, a) {
29616 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
29617 // var cache = this.paintCache(context);
29618 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29619 // if( cache.fillStyle !== fillStyle ){
29620 // context.fillStyle = cache.fillStyle = fillStyle;
29621 // }
29622};
29623
29624CRp$6.eleFillStyle = function (context, ele, opacity) {
29625 var backgroundFill = ele.pstyle('background-fill').value;
29626
29627 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
29628 this.gradientFillStyle(context, ele, backgroundFill, opacity);
29629 } else {
29630 var backgroundColor = ele.pstyle('background-color').value;
29631 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
29632 }
29633};
29634
29635CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
29636 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
29637 if (!gradientStyle) return null; // error
29638
29639 context.strokeStyle = gradientStyle;
29640};
29641
29642CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
29643 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
29644 // var cache = this.paintCache(context);
29645 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29646 // if( cache.strokeStyle !== strokeStyle ){
29647 // context.strokeStyle = cache.strokeStyle = strokeStyle;
29648 // }
29649};
29650
29651CRp$6.eleStrokeStyle = function (context, ele, opacity) {
29652 var lineFill = ele.pstyle('line-fill').value;
29653
29654 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
29655 this.gradientStrokeStyle(context, ele, lineFill, opacity);
29656 } else {
29657 var lineColor = ele.pstyle('line-color').value;
29658 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
29659 }
29660}; // Resize canvas
29661
29662
29663CRp$6.matchCanvasSize = function (container) {
29664 var r = this;
29665 var data = r.data;
29666 var bb = r.findContainerClientCoords();
29667 var width = bb[2];
29668 var height = bb[3];
29669 var pixelRatio = r.getPixelRatio();
29670 var mbPxRatio = r.motionBlurPxRatio;
29671
29672 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
29673 pixelRatio = mbPxRatio;
29674 }
29675
29676 var canvasWidth = width * pixelRatio;
29677 var canvasHeight = height * pixelRatio;
29678 var canvas;
29679
29680 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
29681 return; // save cycles if same
29682 }
29683
29684 r.fontCaches = null; // resizing resets the style
29685
29686 var canvasContainer = data.canvasContainer;
29687 canvasContainer.style.width = width + 'px';
29688 canvasContainer.style.height = height + 'px';
29689
29690 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
29691 canvas = data.canvases[i];
29692 canvas.width = canvasWidth;
29693 canvas.height = canvasHeight;
29694 canvas.style.width = width + 'px';
29695 canvas.style.height = height + 'px';
29696 }
29697
29698 for (var i = 0; i < r.BUFFER_COUNT; i++) {
29699 canvas = data.bufferCanvases[i];
29700 canvas.width = canvasWidth;
29701 canvas.height = canvasHeight;
29702 canvas.style.width = width + 'px';
29703 canvas.style.height = height + 'px';
29704 }
29705
29706 r.textureMult = 1;
29707
29708 if (pixelRatio <= 1) {
29709 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
29710 r.textureMult = 2;
29711 canvas.width = canvasWidth * r.textureMult;
29712 canvas.height = canvasHeight * r.textureMult;
29713 }
29714
29715 r.canvasWidth = canvasWidth;
29716 r.canvasHeight = canvasHeight;
29717};
29718
29719CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
29720 this.render({
29721 forcedContext: cxt,
29722 forcedZoom: zoom,
29723 forcedPan: pan,
29724 drawAllLayers: true,
29725 forcedPxRatio: pxRatio
29726 });
29727};
29728
29729CRp$6.render = function (options) {
29730 options = options || staticEmptyObject();
29731 var forcedContext = options.forcedContext;
29732 var drawAllLayers = options.drawAllLayers;
29733 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
29734 var forcedZoom = options.forcedZoom;
29735 var forcedPan = options.forcedPan;
29736 var r = this;
29737 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
29738 var cy = r.cy;
29739 var data = r.data;
29740 var needDraw = data.canvasNeedsRedraw;
29741 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
29742 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
29743 var mbPxRatio = r.motionBlurPxRatio;
29744 var hasCompoundNodes = cy.hasCompoundNodes();
29745 var inNodeDragGesture = r.hoverData.draggingEles;
29746 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
29747 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
29748 var motionBlurFadeEffect = motionBlur;
29749
29750 if (!forcedContext) {
29751 if (r.prevPxRatio !== pixelRatio) {
29752 r.invalidateContainerClientCoordsCache();
29753 r.matchCanvasSize(r.container);
29754 r.redrawHint('eles', true);
29755 r.redrawHint('drag', true);
29756 }
29757
29758 r.prevPxRatio = pixelRatio;
29759 }
29760
29761 if (!forcedContext && r.motionBlurTimeout) {
29762 clearTimeout(r.motionBlurTimeout);
29763 }
29764
29765 if (motionBlur) {
29766 if (r.mbFrames == null) {
29767 r.mbFrames = 0;
29768 }
29769
29770 r.mbFrames++;
29771
29772 if (r.mbFrames < 3) {
29773 // need several frames before even high quality motionblur
29774 motionBlurFadeEffect = false;
29775 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
29776
29777
29778 if (r.mbFrames > r.minMbLowQualFrames) {
29779 //r.fullQualityMb = false;
29780 r.motionBlurPxRatio = r.mbPxRBlurry;
29781 }
29782 }
29783
29784 if (r.clearingMotionBlur) {
29785 r.motionBlurPxRatio = 1;
29786 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
29787 // because a rogue async texture frame would clear needDraw
29788
29789
29790 if (r.textureDrawLastFrame && !textureDraw) {
29791 needDraw[r.NODE] = true;
29792 needDraw[r.SELECT_BOX] = true;
29793 }
29794
29795 var style = cy.style();
29796 var zoom = cy.zoom();
29797 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
29798 var pan = cy.pan();
29799 var effectivePan = {
29800 x: pan.x,
29801 y: pan.y
29802 };
29803 var vp = {
29804 zoom: zoom,
29805 pan: {
29806 x: pan.x,
29807 y: pan.y
29808 }
29809 };
29810 var prevVp = r.prevViewport;
29811 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)
29812
29813 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
29814 r.motionBlurPxRatio = 1;
29815 }
29816
29817 if (forcedPan) {
29818 effectivePan = forcedPan;
29819 } // apply pixel ratio
29820
29821
29822 effectiveZoom *= pixelRatio;
29823 effectivePan.x *= pixelRatio;
29824 effectivePan.y *= pixelRatio;
29825 var eles = r.getCachedZSortedEles();
29826
29827 function mbclear(context, x, y, w, h) {
29828 var gco = context.globalCompositeOperation;
29829 context.globalCompositeOperation = 'destination-out';
29830 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
29831 context.fillRect(x, y, w, h);
29832 context.globalCompositeOperation = gco;
29833 }
29834
29835 function setContextTransform(context, clear) {
29836 var ePan, eZoom, w, h;
29837
29838 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
29839 ePan = {
29840 x: pan.x * mbPxRatio,
29841 y: pan.y * mbPxRatio
29842 };
29843 eZoom = zoom * mbPxRatio;
29844 w = r.canvasWidth * mbPxRatio;
29845 h = r.canvasHeight * mbPxRatio;
29846 } else {
29847 ePan = effectivePan;
29848 eZoom = effectiveZoom;
29849 w = r.canvasWidth;
29850 h = r.canvasHeight;
29851 }
29852
29853 context.setTransform(1, 0, 0, 1, 0, 0);
29854
29855 if (clear === 'motionBlur') {
29856 mbclear(context, 0, 0, w, h);
29857 } else if (!forcedContext && (clear === undefined || clear)) {
29858 context.clearRect(0, 0, w, h);
29859 }
29860
29861 if (!drawAllLayers) {
29862 context.translate(ePan.x, ePan.y);
29863 context.scale(eZoom, eZoom);
29864 }
29865
29866 if (forcedPan) {
29867 context.translate(forcedPan.x, forcedPan.y);
29868 }
29869
29870 if (forcedZoom) {
29871 context.scale(forcedZoom, forcedZoom);
29872 }
29873 }
29874
29875 if (!textureDraw) {
29876 r.textureDrawLastFrame = false;
29877 }
29878
29879 if (textureDraw) {
29880 r.textureDrawLastFrame = true;
29881
29882 if (!r.textureCache) {
29883 r.textureCache = {};
29884 r.textureCache.bb = cy.mutableElements().boundingBox();
29885 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
29886 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
29887 cxt.setTransform(1, 0, 0, 1, 0, 0);
29888 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
29889 r.render({
29890 forcedContext: cxt,
29891 drawOnlyNodeLayer: true,
29892 forcedPxRatio: pixelRatio * r.textureMult
29893 });
29894 var vp = r.textureCache.viewport = {
29895 zoom: cy.zoom(),
29896 pan: cy.pan(),
29897 width: r.canvasWidth,
29898 height: r.canvasHeight
29899 };
29900 vp.mpan = {
29901 x: (0 - vp.pan.x) / vp.zoom,
29902 y: (0 - vp.pan.y) / vp.zoom
29903 };
29904 }
29905
29906 needDraw[r.DRAG] = false;
29907 needDraw[r.NODE] = false;
29908 var context = data.contexts[r.NODE];
29909 var texture = r.textureCache.texture;
29910 var vp = r.textureCache.viewport;
29911 context.setTransform(1, 0, 0, 1, 0, 0);
29912
29913 if (motionBlur) {
29914 mbclear(context, 0, 0, vp.width, vp.height);
29915 } else {
29916 context.clearRect(0, 0, vp.width, vp.height);
29917 }
29918
29919 var outsideBgColor = style.core('outside-texture-bg-color').value;
29920 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
29921 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
29922 context.fillRect(0, 0, vp.width, vp.height);
29923 var zoom = cy.zoom();
29924 setContextTransform(context, false);
29925 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
29926 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
29927 } else if (r.textureOnViewport && !forcedContext) {
29928 // clear the cache since we don't need it
29929 r.textureCache = null;
29930 }
29931
29932 var extent = cy.extent();
29933 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
29934 var hideEdges = r.hideEdgesOnViewport && vpManip;
29935 var needMbClear = [];
29936 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
29937
29938 if (needMbClear[r.NODE]) {
29939 r.clearedForMotionBlur[r.NODE] = true;
29940 }
29941
29942 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
29943
29944 if (needMbClear[r.DRAG]) {
29945 r.clearedForMotionBlur[r.DRAG] = true;
29946 }
29947
29948 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
29949 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
29950 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
29951 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
29952 setContextTransform(context, clear);
29953
29954 if (hideEdges) {
29955 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
29956 } else {
29957 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
29958 }
29959
29960 if (r.debug) {
29961 r.drawDebugPoints(context, eles.nondrag);
29962 }
29963
29964 if (!drawAllLayers && !motionBlur) {
29965 needDraw[r.NODE] = false;
29966 }
29967 }
29968
29969 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
29970 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
29971 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
29972 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
29973
29974 if (hideEdges) {
29975 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
29976 } else {
29977 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
29978 }
29979
29980 if (r.debug) {
29981 r.drawDebugPoints(context, eles.drag);
29982 }
29983
29984 if (!drawAllLayers && !motionBlur) {
29985 needDraw[r.DRAG] = false;
29986 }
29987 }
29988
29989 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
29990 var context = forcedContext || data.contexts[r.SELECT_BOX];
29991 setContextTransform(context);
29992
29993 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
29994 var zoom = r.cy.zoom();
29995 var borderWidth = style.core('selection-box-border-width').value / zoom;
29996 context.lineWidth = borderWidth;
29997 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 + ')';
29998 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
29999
30000 if (borderWidth > 0) {
30001 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 + ')';
30002 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30003 }
30004 }
30005
30006 if (data.bgActivePosistion && !r.hoverData.selecting) {
30007 var zoom = r.cy.zoom();
30008 var pos = data.bgActivePosistion;
30009 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 + ')';
30010 context.beginPath();
30011 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30012 context.fill();
30013 }
30014
30015 var timeToRender = r.lastRedrawTime;
30016
30017 if (r.showFps && timeToRender) {
30018 timeToRender = Math.round(timeToRender);
30019 var fps = Math.round(1000 / timeToRender);
30020 context.setTransform(1, 0, 0, 1, 0, 0);
30021 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30022 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30023 context.lineWidth = 1;
30024 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30025 var maxFps = 60;
30026 context.strokeRect(0, 30, 250, 20);
30027 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30028 }
30029
30030 if (!drawAllLayers) {
30031 needDraw[r.SELECT_BOX] = false;
30032 }
30033 } // motionblur: blit rendered blurry frames
30034
30035
30036 if (motionBlur && mbPxRatio !== 1) {
30037 var cxtNode = data.contexts[r.NODE];
30038 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30039 var cxtDrag = data.contexts[r.DRAG];
30040 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30041
30042 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30043 cxt.setTransform(1, 0, 0, 1, 0, 0);
30044
30045 if (needClear || !motionBlurFadeEffect) {
30046 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30047 } else {
30048 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30049 }
30050
30051 var pxr = mbPxRatio;
30052 cxt.drawImage(txt, // img
30053 0, 0, // sx, sy
30054 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30055 0, 0, // x, y
30056 r.canvasWidth, r.canvasHeight // w, h
30057 );
30058 };
30059
30060 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30061 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30062 needDraw[r.NODE] = false;
30063 }
30064
30065 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30066 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30067 needDraw[r.DRAG] = false;
30068 }
30069 }
30070
30071 r.prevViewport = vp;
30072
30073 if (r.clearingMotionBlur) {
30074 r.clearingMotionBlur = false;
30075 r.motionBlurCleared = true;
30076 r.motionBlur = true;
30077 }
30078
30079 if (motionBlur) {
30080 r.motionBlurTimeout = setTimeout(function () {
30081 r.motionBlurTimeout = null;
30082 r.clearedForMotionBlur[r.NODE] = false;
30083 r.clearedForMotionBlur[r.DRAG] = false;
30084 r.motionBlur = false;
30085 r.clearingMotionBlur = !textureDraw;
30086 r.mbFrames = 0;
30087 needDraw[r.NODE] = true;
30088 needDraw[r.DRAG] = true;
30089 r.redraw();
30090 }, motionBlurDelay);
30091 }
30092
30093 if (!forcedContext) {
30094 cy.emit('render');
30095 }
30096};
30097
30098var CRp$7 = {}; // @O Polygon drawing
30099
30100CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30101 var halfW = width / 2;
30102 var halfH = height / 2;
30103
30104 if (context.beginPath) {
30105 context.beginPath();
30106 }
30107
30108 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30109
30110 for (var i = 1; i < points.length / 2; i++) {
30111 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30112 }
30113
30114 context.closePath();
30115};
30116
30117CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30118 var halfW = width / 2;
30119 var halfH = height / 2;
30120 var cornerRadius = getRoundPolygonRadius(width, height);
30121
30122 if (context.beginPath) {
30123 context.beginPath();
30124 }
30125
30126 for (var _i = 0; _i < points.length / 4; _i++) {
30127 var sourceUv = void 0,
30128 destUv = void 0;
30129
30130 if (_i === 0) {
30131 sourceUv = points.length - 2;
30132 } else {
30133 sourceUv = _i * 4 - 2;
30134 }
30135
30136 destUv = _i * 4 + 2;
30137 var px = x + halfW * points[_i * 4];
30138 var py = y + halfH * points[_i * 4 + 1];
30139 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30140 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30141 var cp0x = px - offset * points[sourceUv];
30142 var cp0y = py - offset * points[sourceUv + 1];
30143 var cp1x = px + offset * points[destUv];
30144 var cp1y = py + offset * points[destUv + 1];
30145
30146 if (_i === 0) {
30147 context.moveTo(cp0x, cp0y);
30148 } else {
30149 context.lineTo(cp0x, cp0y);
30150 }
30151
30152 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30153 }
30154
30155 context.closePath();
30156}; // Round rectangle drawing
30157
30158
30159CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30160 var halfWidth = width / 2;
30161 var halfHeight = height / 2;
30162 var cornerRadius = getRoundRectangleRadius(width, height);
30163
30164 if (context.beginPath) {
30165 context.beginPath();
30166 } // Start at top middle
30167
30168
30169 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30170
30171 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30172
30173 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30174
30175 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30176
30177 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30178
30179 context.lineTo(x, y - halfHeight);
30180 context.closePath();
30181};
30182
30183CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30184 var halfWidth = width / 2;
30185 var halfHeight = height / 2;
30186 var cornerRadius = getRoundRectangleRadius(width, height);
30187
30188 if (context.beginPath) {
30189 context.beginPath();
30190 } // Start at top middle
30191
30192
30193 context.moveTo(x, y - halfHeight);
30194 context.lineTo(x + halfWidth, y - halfHeight);
30195 context.lineTo(x + halfWidth, y);
30196 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30197 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30198 context.lineTo(x - halfWidth, y - halfHeight);
30199 context.lineTo(x, y - halfHeight);
30200 context.closePath();
30201};
30202
30203CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30204 var halfWidth = width / 2;
30205 var halfHeight = height / 2;
30206 var cornerLength = getCutRectangleCornerLength();
30207
30208 if (context.beginPath) {
30209 context.beginPath();
30210 }
30211
30212 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30213 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30214 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30215 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30216 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30217 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30218 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30219 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30220 context.closePath();
30221};
30222
30223CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30224 var halfWidth = width / 2;
30225 var halfHeight = height / 2;
30226 var xBegin = x - halfWidth;
30227 var xEnd = x + halfWidth;
30228 var yBegin = y - halfHeight;
30229 var yEnd = y + halfHeight;
30230 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30231 var wOffset = barrelCurveConstants.widthOffset;
30232 var hOffset = barrelCurveConstants.heightOffset;
30233 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30234
30235 if (context.beginPath) {
30236 context.beginPath();
30237 }
30238
30239 context.moveTo(xBegin, yBegin + hOffset);
30240 context.lineTo(xBegin, yEnd - hOffset);
30241 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30242 context.lineTo(xEnd - wOffset, yEnd);
30243 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30244 context.lineTo(xEnd, yBegin + hOffset);
30245 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30246 context.lineTo(xBegin + wOffset, yBegin);
30247 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30248 context.closePath();
30249};
30250
30251var sin0 = Math.sin(0);
30252var cos0 = Math.cos(0);
30253var sin = {};
30254var cos = {};
30255var ellipseStepSize = Math.PI / 40;
30256
30257for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30258 sin[i] = Math.sin(i);
30259 cos[i] = Math.cos(i);
30260}
30261
30262CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30263 if (context.beginPath) {
30264 context.beginPath();
30265 }
30266
30267 if (context.ellipse) {
30268 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30269 } else {
30270 var xPos, yPos;
30271 var rw = width / 2;
30272 var rh = height / 2;
30273
30274 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30275 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30276 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30277
30278 if (i === 0) {
30279 context.moveTo(xPos, yPos);
30280 } else {
30281 context.lineTo(xPos, yPos);
30282 }
30283 }
30284 }
30285
30286 context.closePath();
30287};
30288
30289/* global atob, ArrayBuffer, Uint8Array, Blob */
30290var CRp$8 = {};
30291
30292CRp$8.createBuffer = function (w, h) {
30293 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30294
30295 buffer.width = w;
30296 buffer.height = h;
30297 return [buffer, buffer.getContext('2d')];
30298};
30299
30300CRp$8.bufferCanvasImage = function (options) {
30301 var cy = this.cy;
30302 var eles = cy.mutableElements();
30303 var bb = eles.boundingBox();
30304 var ctrRect = this.findContainerClientCoords();
30305 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30306 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30307 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30308 var pxRatio = this.getPixelRatio();
30309 var scale = 1;
30310
30311 if (options.scale !== undefined) {
30312 width *= options.scale;
30313 height *= options.scale;
30314 scale = options.scale;
30315 } else if (specdMaxDims) {
30316 var maxScaleW = Infinity;
30317 var maxScaleH = Infinity;
30318
30319 if (number(options.maxWidth)) {
30320 maxScaleW = scale * options.maxWidth / width;
30321 }
30322
30323 if (number(options.maxHeight)) {
30324 maxScaleH = scale * options.maxHeight / height;
30325 }
30326
30327 scale = Math.min(maxScaleW, maxScaleH);
30328 width *= scale;
30329 height *= scale;
30330 }
30331
30332 if (!specdMaxDims) {
30333 width *= pxRatio;
30334 height *= pxRatio;
30335 scale *= pxRatio;
30336 }
30337
30338 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30339
30340 buffCanvas.width = width;
30341 buffCanvas.height = height;
30342 buffCanvas.style.width = width + 'px';
30343 buffCanvas.style.height = height + 'px';
30344 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30345
30346 if (width > 0 && height > 0) {
30347 buffCxt.clearRect(0, 0, width, height);
30348 buffCxt.globalCompositeOperation = 'source-over';
30349 var zsortedEles = this.getCachedZSortedEles();
30350
30351 if (options.full) {
30352 // draw the full bounds of the graph
30353 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30354 buffCxt.scale(scale, scale);
30355 this.drawElements(buffCxt, zsortedEles);
30356 buffCxt.scale(1 / scale, 1 / scale);
30357 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30358 } else {
30359 // draw the current view
30360 var pan = cy.pan();
30361 var translation = {
30362 x: pan.x * scale,
30363 y: pan.y * scale
30364 };
30365 scale *= cy.zoom();
30366 buffCxt.translate(translation.x, translation.y);
30367 buffCxt.scale(scale, scale);
30368 this.drawElements(buffCxt, zsortedEles);
30369 buffCxt.scale(1 / scale, 1 / scale);
30370 buffCxt.translate(-translation.x, -translation.y);
30371 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30372
30373
30374 if (options.bg) {
30375 buffCxt.globalCompositeOperation = 'destination-over';
30376 buffCxt.fillStyle = options.bg;
30377 buffCxt.rect(0, 0, width, height);
30378 buffCxt.fill();
30379 }
30380 }
30381
30382 return buffCanvas;
30383};
30384
30385function b64ToBlob(b64, mimeType) {
30386 var bytes = atob(b64);
30387 var buff = new ArrayBuffer(bytes.length);
30388 var buffUint8 = new Uint8Array(buff);
30389
30390 for (var i = 0; i < bytes.length; i++) {
30391 buffUint8[i] = bytes.charCodeAt(i);
30392 }
30393
30394 return new Blob([buff], {
30395 type: mimeType
30396 });
30397}
30398
30399function b64UriToB64(b64uri) {
30400 var i = b64uri.indexOf(',');
30401 return b64uri.substr(i + 1);
30402}
30403
30404function output(options, canvas, mimeType) {
30405 var getB64Uri = function getB64Uri() {
30406 return canvas.toDataURL(mimeType, options.quality);
30407 };
30408
30409 switch (options.output) {
30410 case 'blob-promise':
30411 return new Promise$1(function (resolve, reject) {
30412 try {
30413 canvas.toBlob(function (blob) {
30414 if (blob != null) {
30415 resolve(blob);
30416 } else {
30417 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30418 }
30419 }, mimeType, options.quality);
30420 } catch (err) {
30421 reject(err);
30422 }
30423 });
30424
30425 case 'blob':
30426 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30427
30428 case 'base64':
30429 return b64UriToB64(getB64Uri());
30430
30431 case 'base64uri':
30432 default:
30433 return getB64Uri();
30434 }
30435}
30436
30437CRp$8.png = function (options) {
30438 return output(options, this.bufferCanvasImage(options), 'image/png');
30439};
30440
30441CRp$8.jpg = function (options) {
30442 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30443};
30444
30445var CRp$9 = {};
30446
30447CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30448 switch (name) {
30449 case 'ellipse':
30450 return this.drawEllipsePath(context, centerX, centerY, width, height);
30451
30452 case 'polygon':
30453 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30454
30455 case 'round-polygon':
30456 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30457
30458 case 'roundrectangle':
30459 case 'round-rectangle':
30460 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30461
30462 case 'cutrectangle':
30463 case 'cut-rectangle':
30464 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30465
30466 case 'bottomroundrectangle':
30467 case 'bottom-round-rectangle':
30468 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30469
30470 case 'barrel':
30471 return this.drawBarrelPath(context, centerX, centerY, width, height);
30472 }
30473};
30474
30475var CR = CanvasRenderer;
30476var CRp$a = CanvasRenderer.prototype;
30477CRp$a.CANVAS_LAYERS = 3; //
30478
30479CRp$a.SELECT_BOX = 0;
30480CRp$a.DRAG = 1;
30481CRp$a.NODE = 2;
30482CRp$a.BUFFER_COUNT = 3; //
30483
30484CRp$a.TEXTURE_BUFFER = 0;
30485CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30486CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30487
30488function CanvasRenderer(options) {
30489 var r = this;
30490 r.data = {
30491 canvases: new Array(CRp$a.CANVAS_LAYERS),
30492 contexts: new Array(CRp$a.CANVAS_LAYERS),
30493 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30494 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30495 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30496 };
30497 var tapHlOffAttr = '-webkit-tap-highlight-color';
30498 var tapHlOffStyle = 'rgba(0,0,0,0)';
30499 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30500
30501 var containerStyle = r.data.canvasContainer.style;
30502 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30503 containerStyle.position = 'relative';
30504 containerStyle.zIndex = '0';
30505 containerStyle.overflow = 'hidden';
30506 var container = options.cy.container();
30507 container.appendChild(r.data.canvasContainer);
30508 container.style[tapHlOffAttr] = tapHlOffStyle;
30509 var styleMap = {
30510 '-webkit-user-select': 'none',
30511 '-moz-user-select': '-moz-none',
30512 'user-select': 'none',
30513 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30514 'outline-style': 'none'
30515 };
30516
30517 if (ms()) {
30518 styleMap['-ms-touch-action'] = 'none';
30519 styleMap['touch-action'] = 'none';
30520 }
30521
30522 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30523 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30524
30525 r.data.contexts[i] = canvas.getContext('2d');
30526 Object.keys(styleMap).forEach(function (k) {
30527 canvas.style[k] = styleMap[k];
30528 });
30529 canvas.style.position = 'absolute';
30530 canvas.setAttribute('data-id', 'layer' + i);
30531 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30532 r.data.canvasContainer.appendChild(canvas);
30533 r.data.canvasNeedsRedraw[i] = false;
30534 }
30535
30536 r.data.topCanvas = r.data.canvases[0];
30537 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30538 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30539 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30540
30541 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30542 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30543
30544 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30545 r.data.bufferCanvases[i].style.position = 'absolute';
30546 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30547 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30548 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30549 }
30550
30551 r.pathsEnabled = true;
30552 var emptyBb = makeBoundingBox();
30553
30554 var getBoxCenter = function getBoxCenter(bb) {
30555 return {
30556 x: (bb.x1 + bb.x2) / 2,
30557 y: (bb.y1 + bb.y2) / 2
30558 };
30559 };
30560
30561 var getCenterOffset = function getCenterOffset(bb) {
30562 return {
30563 x: -bb.w / 2,
30564 y: -bb.h / 2
30565 };
30566 };
30567
30568 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
30569 var _p = ele[0]._private;
30570 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
30571 return !same;
30572 };
30573
30574 var getStyleKey = function getStyleKey(ele) {
30575 return ele[0]._private.nodeKey;
30576 };
30577
30578 var getLabelKey = function getLabelKey(ele) {
30579 return ele[0]._private.labelStyleKey;
30580 };
30581
30582 var getSourceLabelKey = function getSourceLabelKey(ele) {
30583 return ele[0]._private.sourceLabelStyleKey;
30584 };
30585
30586 var getTargetLabelKey = function getTargetLabelKey(ele) {
30587 return ele[0]._private.targetLabelStyleKey;
30588 };
30589
30590 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
30591 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
30592 };
30593
30594 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30595 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
30596 };
30597
30598 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30599 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
30600 };
30601
30602 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30603 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
30604 };
30605
30606 var getElementBox = function getElementBox(ele) {
30607 ele.boundingBox();
30608 return ele[0]._private.bodyBounds;
30609 };
30610
30611 var getLabelBox = function getLabelBox(ele) {
30612 ele.boundingBox();
30613 return ele[0]._private.labelBounds.main || emptyBb;
30614 };
30615
30616 var getSourceLabelBox = function getSourceLabelBox(ele) {
30617 ele.boundingBox();
30618 return ele[0]._private.labelBounds.source || emptyBb;
30619 };
30620
30621 var getTargetLabelBox = function getTargetLabelBox(ele) {
30622 ele.boundingBox();
30623 return ele[0]._private.labelBounds.target || emptyBb;
30624 };
30625
30626 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
30627 return scaledLabelShown;
30628 };
30629
30630 var getElementRotationPoint = function getElementRotationPoint(ele) {
30631 return getBoxCenter(getElementBox(ele));
30632 };
30633
30634 var addTextMargin = function addTextMargin(prefix, pt, ele) {
30635 var pre = prefix ? prefix + '-' : '';
30636 return {
30637 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
30638 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
30639 };
30640 };
30641
30642 var getRsPt = function getRsPt(ele, x, y) {
30643 var rs = ele[0]._private.rscratch;
30644 return {
30645 x: rs[x],
30646 y: rs[y]
30647 };
30648 };
30649
30650 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
30651 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
30652 };
30653
30654 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
30655 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
30656 };
30657
30658 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
30659 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
30660 };
30661
30662 var getElementRotationOffset = function getElementRotationOffset(ele) {
30663 return getCenterOffset(getElementBox(ele));
30664 };
30665
30666 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
30667 return getCenterOffset(getSourceLabelBox(ele));
30668 };
30669
30670 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
30671 return getCenterOffset(getTargetLabelBox(ele));
30672 };
30673
30674 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
30675 var bb = getLabelBox(ele);
30676 var p = getCenterOffset(getLabelBox(ele));
30677
30678 if (ele.isNode()) {
30679 switch (ele.pstyle('text-halign').value) {
30680 case 'left':
30681 p.x = -bb.w;
30682 break;
30683
30684 case 'right':
30685 p.x = 0;
30686 break;
30687 }
30688
30689 switch (ele.pstyle('text-valign').value) {
30690 case 'top':
30691 p.y = -bb.h;
30692 break;
30693
30694 case 'bottom':
30695 p.y = 0;
30696 break;
30697 }
30698 }
30699
30700 return p;
30701 };
30702
30703 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
30704 getKey: getStyleKey,
30705 doesEleInvalidateKey: backgroundTimestampHasChanged,
30706 drawElement: drawElement,
30707 getBoundingBox: getElementBox,
30708 getRotationPoint: getElementRotationPoint,
30709 getRotationOffset: getElementRotationOffset,
30710 allowEdgeTxrCaching: false,
30711 allowParentTxrCaching: false
30712 });
30713 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
30714 getKey: getLabelKey,
30715 drawElement: drawLabel,
30716 getBoundingBox: getLabelBox,
30717 getRotationPoint: getLabelRotationPoint,
30718 getRotationOffset: getLabelRotationOffset,
30719 isVisible: isLabelVisibleAtScale
30720 });
30721 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
30722 getKey: getSourceLabelKey,
30723 drawElement: drawSourceLabel,
30724 getBoundingBox: getSourceLabelBox,
30725 getRotationPoint: getSourceLabelRotationPoint,
30726 getRotationOffset: getSourceLabelRotationOffset,
30727 isVisible: isLabelVisibleAtScale
30728 });
30729 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
30730 getKey: getTargetLabelKey,
30731 drawElement: drawTargetLabel,
30732 getBoundingBox: getTargetLabelBox,
30733 getRotationPoint: getTargetLabelRotationPoint,
30734 getRotationOffset: getTargetLabelRotationOffset,
30735 isVisible: isLabelVisibleAtScale
30736 });
30737 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
30738 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
30739 // each cache should check for sub-key diff to see that the update affects that cache particularly
30740 eleTxrCache.invalidateElements(eles);
30741 lblTxrCache.invalidateElements(eles);
30742 slbTxrCache.invalidateElements(eles);
30743 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
30744
30745 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
30746
30747 for (var _i = 0; _i < eles.length; _i++) {
30748 var _p = eles[_i]._private;
30749 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
30750 }
30751 });
30752
30753 var refineInLayers = function refineInLayers(reqs) {
30754 for (var i = 0; i < reqs.length; i++) {
30755 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
30756 }
30757 };
30758
30759 eleTxrCache.onDequeue(refineInLayers);
30760 lblTxrCache.onDequeue(refineInLayers);
30761 slbTxrCache.onDequeue(refineInLayers);
30762 tlbTxrCache.onDequeue(refineInLayers);
30763}
30764
30765CRp$a.redrawHint = function (group, bool) {
30766 var r = this;
30767
30768 switch (group) {
30769 case 'eles':
30770 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
30771 break;
30772
30773 case 'drag':
30774 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
30775 break;
30776
30777 case 'select':
30778 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
30779 break;
30780 }
30781}; // whether to use Path2D caching for drawing
30782
30783
30784var pathsImpld = typeof Path2D !== 'undefined';
30785
30786CRp$a.path2dEnabled = function (on) {
30787 if (on === undefined) {
30788 return this.pathsEnabled;
30789 }
30790
30791 this.pathsEnabled = on ? true : false;
30792};
30793
30794CRp$a.usePaths = function () {
30795 return pathsImpld && this.pathsEnabled;
30796};
30797
30798CRp$a.setImgSmoothing = function (context, bool) {
30799 if (context.imageSmoothingEnabled != null) {
30800 context.imageSmoothingEnabled = bool;
30801 } else {
30802 context.webkitImageSmoothingEnabled = bool;
30803 context.mozImageSmoothingEnabled = bool;
30804 context.msImageSmoothingEnabled = bool;
30805 }
30806};
30807
30808CRp$a.getImgSmoothing = function (context) {
30809 if (context.imageSmoothingEnabled != null) {
30810 return context.imageSmoothingEnabled;
30811 } else {
30812 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
30813 }
30814};
30815
30816CRp$a.makeOffscreenCanvas = function (width, height) {
30817 var canvas;
30818
30819 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
30820 canvas = new OffscreenCanvas(width, height);
30821 } else {
30822 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
30823
30824 canvas.width = width;
30825 canvas.height = height;
30826 }
30827
30828 return canvas;
30829};
30830
30831[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
30832 extend(CRp$a, props);
30833});
30834
30835var renderer = [{
30836 name: 'null',
30837 impl: NullRenderer
30838}, {
30839 name: 'base',
30840 impl: BR
30841}, {
30842 name: 'canvas',
30843 impl: CR
30844}];
30845
30846var incExts = [{
30847 type: 'layout',
30848 extensions: layout
30849}, {
30850 type: 'renderer',
30851 extensions: renderer
30852}];
30853
30854var extensions = {}; // registered modules for extensions, indexed by name
30855
30856var modules = {};
30857
30858function setExtension(type, name, registrant) {
30859 var ext = registrant;
30860
30861 var overrideErr = function overrideErr(field) {
30862 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
30863 };
30864
30865 if (type === 'core') {
30866 if (Core.prototype[name]) {
30867 return overrideErr(name);
30868 } else {
30869 Core.prototype[name] = registrant;
30870 }
30871 } else if (type === 'collection') {
30872 if (Collection.prototype[name]) {
30873 return overrideErr(name);
30874 } else {
30875 Collection.prototype[name] = registrant;
30876 }
30877 } else if (type === 'layout') {
30878 // fill in missing layout functions in the prototype
30879 var Layout = function Layout(options) {
30880 this.options = options;
30881 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
30882
30883 if (!plainObject(this._private)) {
30884 this._private = {};
30885 }
30886
30887 this._private.cy = options.cy;
30888 this._private.listeners = [];
30889 this.createEmitter();
30890 };
30891
30892 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
30893 var optLayoutFns = [];
30894
30895 for (var i = 0; i < optLayoutFns.length; i++) {
30896 var fnName = optLayoutFns[i];
30897
30898 layoutProto[fnName] = layoutProto[fnName] || function () {
30899 return this;
30900 };
30901 } // either .start() or .run() is defined, so autogen the other
30902
30903
30904 if (layoutProto.start && !layoutProto.run) {
30905 layoutProto.run = function () {
30906 this.start();
30907 return this;
30908 };
30909 } else if (!layoutProto.start && layoutProto.run) {
30910 layoutProto.start = function () {
30911 this.run();
30912 return this;
30913 };
30914 }
30915
30916 var regStop = registrant.prototype.stop;
30917
30918 layoutProto.stop = function () {
30919 var opts = this.options;
30920
30921 if (opts && opts.animate) {
30922 var anis = this.animations;
30923
30924 if (anis) {
30925 for (var _i = 0; _i < anis.length; _i++) {
30926 anis[_i].stop();
30927 }
30928 }
30929 }
30930
30931 if (regStop) {
30932 regStop.call(this);
30933 } else {
30934 this.emit('layoutstop');
30935 }
30936
30937 return this;
30938 };
30939
30940 if (!layoutProto.destroy) {
30941 layoutProto.destroy = function () {
30942 return this;
30943 };
30944 }
30945
30946 layoutProto.cy = function () {
30947 return this._private.cy;
30948 };
30949
30950 var getCy = function getCy(layout) {
30951 return layout._private.cy;
30952 };
30953
30954 var emitterOpts = {
30955 addEventFields: function addEventFields(layout, evt) {
30956 evt.layout = layout;
30957 evt.cy = getCy(layout);
30958 evt.target = layout;
30959 },
30960 bubble: function bubble() {
30961 return true;
30962 },
30963 parent: function parent(layout) {
30964 return getCy(layout);
30965 }
30966 };
30967 extend(layoutProto, {
30968 createEmitter: function createEmitter() {
30969 this._private.emitter = new Emitter(emitterOpts, this);
30970 return this;
30971 },
30972 emitter: function emitter() {
30973 return this._private.emitter;
30974 },
30975 on: function on(evt, cb) {
30976 this.emitter().on(evt, cb);
30977 return this;
30978 },
30979 one: function one(evt, cb) {
30980 this.emitter().one(evt, cb);
30981 return this;
30982 },
30983 once: function once(evt, cb) {
30984 this.emitter().one(evt, cb);
30985 return this;
30986 },
30987 removeListener: function removeListener(evt, cb) {
30988 this.emitter().removeListener(evt, cb);
30989 return this;
30990 },
30991 removeAllListeners: function removeAllListeners() {
30992 this.emitter().removeAllListeners();
30993 return this;
30994 },
30995 emit: function emit(evt, params) {
30996 this.emitter().emit(evt, params);
30997 return this;
30998 }
30999 });
31000 define$3.eventAliasesOn(layoutProto);
31001 ext = Layout; // replace with our wrapped layout
31002 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31003 // user registered renderers inherit from base
31004 var BaseRenderer = getExtension('renderer', 'base');
31005 var bProto = BaseRenderer.prototype;
31006 var RegistrantRenderer = registrant;
31007 var rProto = registrant.prototype;
31008
31009 var Renderer = function Renderer() {
31010 BaseRenderer.apply(this, arguments);
31011 RegistrantRenderer.apply(this, arguments);
31012 };
31013
31014 var proto = Renderer.prototype;
31015
31016 for (var pName in bProto) {
31017 var pVal = bProto[pName];
31018 var existsInR = rProto[pName] != null;
31019
31020 if (existsInR) {
31021 return overrideErr(pName);
31022 }
31023
31024 proto[pName] = pVal; // take impl from base
31025 }
31026
31027 for (var _pName in rProto) {
31028 proto[_pName] = rProto[_pName]; // take impl from registrant
31029 }
31030
31031 bProto.clientFunctions.forEach(function (name) {
31032 proto[name] = proto[name] || function () {
31033 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31034 };
31035 });
31036 ext = Renderer;
31037 }
31038
31039 return setMap({
31040 map: extensions,
31041 keys: [type, name],
31042 value: ext
31043 });
31044}
31045
31046function getExtension(type, name) {
31047 return getMap({
31048 map: extensions,
31049 keys: [type, name]
31050 });
31051}
31052
31053function setModule(type, name, moduleType, moduleName, registrant) {
31054 return setMap({
31055 map: modules,
31056 keys: [type, name, moduleType, moduleName],
31057 value: registrant
31058 });
31059}
31060
31061function getModule(type, name, moduleType, moduleName) {
31062 return getMap({
31063 map: modules,
31064 keys: [type, name, moduleType, moduleName]
31065 });
31066}
31067
31068var extension = function extension() {
31069 // e.g. extension('renderer', 'svg')
31070 if (arguments.length === 2) {
31071 return getExtension.apply(null, arguments);
31072 } // e.g. extension('renderer', 'svg', { ... })
31073 else if (arguments.length === 3) {
31074 return setExtension.apply(null, arguments);
31075 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31076 else if (arguments.length === 4) {
31077 return getModule.apply(null, arguments);
31078 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31079 else if (arguments.length === 5) {
31080 return setModule.apply(null, arguments);
31081 } else {
31082 error('Invalid extension access syntax');
31083 }
31084}; // allows a core instance to access extensions internally
31085
31086
31087Core.prototype.extension = extension; // included extensions
31088
31089incExts.forEach(function (group) {
31090 group.extensions.forEach(function (ext) {
31091 setExtension(group.type, ext.name, ext.impl);
31092 });
31093});
31094
31095// (useful for init)
31096
31097var Stylesheet = function Stylesheet() {
31098 if (!(this instanceof Stylesheet)) {
31099 return new Stylesheet();
31100 }
31101
31102 this.length = 0;
31103};
31104
31105var sheetfn = Stylesheet.prototype;
31106
31107sheetfn.instanceString = function () {
31108 return 'stylesheet';
31109}; // just store the selector to be parsed later
31110
31111
31112sheetfn.selector = function (selector) {
31113 var i = this.length++;
31114 this[i] = {
31115 selector: selector,
31116 properties: []
31117 };
31118 return this; // chaining
31119}; // just store the property to be parsed later
31120
31121
31122sheetfn.css = function (name, value) {
31123 var i = this.length - 1;
31124
31125 if (string(name)) {
31126 this[i].properties.push({
31127 name: name,
31128 value: value
31129 });
31130 } else if (plainObject(name)) {
31131 var map = name;
31132 var propNames = Object.keys(map);
31133
31134 for (var j = 0; j < propNames.length; j++) {
31135 var key = propNames[j];
31136 var mapVal = map[key];
31137
31138 if (mapVal == null) {
31139 continue;
31140 }
31141
31142 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31143
31144 if (prop == null) {
31145 continue;
31146 }
31147
31148 var _name = prop.name;
31149 var _value = mapVal;
31150 this[i].properties.push({
31151 name: _name,
31152 value: _value
31153 });
31154 }
31155 }
31156
31157 return this; // chaining
31158};
31159
31160sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31161
31162sheetfn.generateStyle = function (cy) {
31163 var style = new Style(cy);
31164 return this.appendToStyle(style);
31165}; // append a dummy stylesheet object on a real style object
31166
31167
31168sheetfn.appendToStyle = function (style) {
31169 for (var i = 0; i < this.length; i++) {
31170 var context = this[i];
31171 var selector = context.selector;
31172 var props = context.properties;
31173 style.selector(selector); // apply selector
31174
31175 for (var j = 0; j < props.length; j++) {
31176 var prop = props[j];
31177 style.css(prop.name, prop.value); // apply property
31178 }
31179 }
31180
31181 return style;
31182};
31183
31184var version = "3.12.0";
31185
31186var cytoscape = function cytoscape(options) {
31187 // if no options specified, use default
31188 if (options === undefined) {
31189 options = {};
31190 } // create instance
31191
31192
31193 if (plainObject(options)) {
31194 return new Core(options);
31195 } // allow for registration of extensions
31196 else if (string(options)) {
31197 return extension.apply(extension, arguments);
31198 }
31199}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31200
31201
31202cytoscape.use = function (ext) {
31203 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31204
31205 args.unshift(cytoscape); // cytoscape is first arg to ext
31206
31207 ext.apply(null, args);
31208 return this;
31209};
31210
31211cytoscape.warnings = function (bool) {
31212 return warnings(bool);
31213}; // replaced by build system
31214
31215
31216cytoscape.version = version; // expose public apis (mostly for extensions)
31217
31218cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31219
31220module.exports = cytoscape;