UNPKG

873 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2022, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23'use strict';
24
25var debounce = require('lodash/debounce');
26var Heap = require('heap');
27var get = require('lodash/get');
28var set = require('lodash/set');
29var toPath = require('lodash/toPath');
30
31function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
32
33var debounce__default = /*#__PURE__*/_interopDefaultLegacy(debounce);
34var Heap__default = /*#__PURE__*/_interopDefaultLegacy(Heap);
35var get__default = /*#__PURE__*/_interopDefaultLegacy(get);
36var set__default = /*#__PURE__*/_interopDefaultLegacy(set);
37var toPath__default = /*#__PURE__*/_interopDefaultLegacy(toPath);
38
39function _typeof(obj) {
40 "@babel/helpers - typeof";
41
42 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
43 return typeof obj;
44 } : function (obj) {
45 return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
46 }, _typeof(obj);
47}
48
49function _classCallCheck(instance, Constructor) {
50 if (!(instance instanceof Constructor)) {
51 throw new TypeError("Cannot call a class as a function");
52 }
53}
54
55function _defineProperties(target, props) {
56 for (var i = 0; i < props.length; i++) {
57 var descriptor = props[i];
58 descriptor.enumerable = descriptor.enumerable || false;
59 descriptor.configurable = true;
60 if ("value" in descriptor) descriptor.writable = true;
61 Object.defineProperty(target, descriptor.key, descriptor);
62 }
63}
64
65function _createClass(Constructor, protoProps, staticProps) {
66 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
67 if (staticProps) _defineProperties(Constructor, staticProps);
68 Object.defineProperty(Constructor, "prototype", {
69 writable: false
70 });
71 return Constructor;
72}
73
74function _defineProperty(obj, key, value) {
75 if (key in obj) {
76 Object.defineProperty(obj, key, {
77 value: value,
78 enumerable: true,
79 configurable: true,
80 writable: true
81 });
82 } else {
83 obj[key] = value;
84 }
85
86 return obj;
87}
88
89function _slicedToArray(arr, i) {
90 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
91}
92
93function _arrayWithHoles(arr) {
94 if (Array.isArray(arr)) return arr;
95}
96
97function _iterableToArrayLimit(arr, i) {
98 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
99
100 if (_i == null) return;
101 var _arr = [];
102 var _n = true;
103 var _d = false;
104
105 var _s, _e;
106
107 try {
108 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
109 _arr.push(_s.value);
110
111 if (i && _arr.length === i) break;
112 }
113 } catch (err) {
114 _d = true;
115 _e = err;
116 } finally {
117 try {
118 if (!_n && _i["return"] != null) _i["return"]();
119 } finally {
120 if (_d) throw _e;
121 }
122 }
123
124 return _arr;
125}
126
127function _unsupportedIterableToArray(o, minLen) {
128 if (!o) return;
129 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
130 var n = Object.prototype.toString.call(o).slice(8, -1);
131 if (n === "Object" && o.constructor) n = o.constructor.name;
132 if (n === "Map" || n === "Set") return Array.from(o);
133 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
134}
135
136function _arrayLikeToArray(arr, len) {
137 if (len == null || len > arr.length) len = arr.length;
138
139 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
140
141 return arr2;
142}
143
144function _nonIterableRest() {
145 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
146}
147
148var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
149
150var navigator = window$1 ? window$1.navigator : null;
151window$1 ? window$1.document : null;
152
153var typeofstr = _typeof('');
154
155var typeofobj = _typeof({});
156
157var typeoffn = _typeof(function () {});
158
159var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
160
161var instanceStr = function instanceStr(obj) {
162 return obj && obj.instanceString && fn$6(obj.instanceString) ? obj.instanceString() : null;
163};
164
165var string = function string(obj) {
166 return obj != null && _typeof(obj) == typeofstr;
167};
168var fn$6 = function fn(obj) {
169 return obj != null && _typeof(obj) === typeoffn;
170};
171var array = function array(obj) {
172 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
173};
174var plainObject = function plainObject(obj) {
175 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
176};
177var object = function object(obj) {
178 return obj != null && _typeof(obj) === typeofobj;
179};
180var number$1 = function number(obj) {
181 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
182};
183var integer = function integer(obj) {
184 return number$1(obj) && Math.floor(obj) === obj;
185};
186var htmlElement = function htmlElement(obj) {
187 if ('undefined' === typeofhtmlele) {
188 return undefined;
189 } else {
190 return null != obj && obj instanceof HTMLElement;
191 }
192};
193var elementOrCollection = function elementOrCollection(obj) {
194 return element(obj) || collection(obj);
195};
196var element = function element(obj) {
197 return instanceStr(obj) === 'collection' && obj._private.single;
198};
199var collection = function collection(obj) {
200 return instanceStr(obj) === 'collection' && !obj._private.single;
201};
202var core = function core(obj) {
203 return instanceStr(obj) === 'core';
204};
205var stylesheet = function stylesheet(obj) {
206 return instanceStr(obj) === 'stylesheet';
207};
208var event = function event(obj) {
209 return instanceStr(obj) === 'event';
210};
211var emptyString = function emptyString(obj) {
212 if (obj === undefined || obj === null) {
213 // null is empty
214 return true;
215 } else if (obj === '' || obj.match(/^\s+$/)) {
216 return true; // empty string is empty
217 }
218
219 return false; // otherwise, we don't know what we've got
220};
221var domElement = function domElement(obj) {
222 if (typeof HTMLElement === 'undefined') {
223 return false; // we're not in a browser so it doesn't matter
224 } else {
225 return obj instanceof HTMLElement;
226 }
227};
228var boundingBox = function boundingBox(obj) {
229 return plainObject(obj) && number$1(obj.x1) && number$1(obj.x2) && number$1(obj.y1) && number$1(obj.y2);
230};
231var promise = function promise(obj) {
232 return object(obj) && fn$6(obj.then);
233};
234var ms = function ms() {
235 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
236}; // probably a better way to detect this...
237
238var memoize = function memoize(fn, keyFn) {
239 if (!keyFn) {
240 keyFn = function keyFn() {
241 if (arguments.length === 1) {
242 return arguments[0];
243 } else if (arguments.length === 0) {
244 return 'undefined';
245 }
246
247 var args = [];
248
249 for (var i = 0; i < arguments.length; i++) {
250 args.push(arguments[i]);
251 }
252
253 return args.join('$');
254 };
255 }
256
257 var memoizedFn = function memoizedFn() {
258 var self = this;
259 var args = arguments;
260 var ret;
261 var k = keyFn.apply(self, args);
262 var cache = memoizedFn.cache;
263
264 if (!(ret = cache[k])) {
265 ret = cache[k] = fn.apply(self, args);
266 }
267
268 return ret;
269 };
270
271 memoizedFn.cache = {};
272 return memoizedFn;
273};
274
275var camel2dash = memoize(function (str) {
276 return str.replace(/([A-Z])/g, function (v) {
277 return '-' + v.toLowerCase();
278 });
279});
280var dash2camel = memoize(function (str) {
281 return str.replace(/(-\w)/g, function (v) {
282 return v[1].toUpperCase();
283 });
284});
285var prependCamel = memoize(function (prefix, str) {
286 return prefix + str[0].toUpperCase() + str.substring(1);
287}, function (prefix, str) {
288 return prefix + '$' + str;
289});
290var capitalize = function capitalize(str) {
291 if (emptyString(str)) {
292 return str;
293 }
294
295 return str.charAt(0).toUpperCase() + str.substring(1);
296};
297
298var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
299var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)';
300var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)';
301var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)';
302var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)';
303var hex3 = '\\#[0-9a-fA-F]{3}';
304var hex6 = '\\#[0-9a-fA-F]{6}';
305
306var ascending = function ascending(a, b) {
307 if (a < b) {
308 return -1;
309 } else if (a > b) {
310 return 1;
311 } else {
312 return 0;
313 }
314};
315var descending = function descending(a, b) {
316 return -1 * ascending(a, b);
317};
318
319var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
320 var args = arguments;
321
322 for (var i = 1; i < args.length; i++) {
323 var obj = args[i];
324
325 if (obj == null) {
326 continue;
327 }
328
329 var keys = Object.keys(obj);
330
331 for (var j = 0; j < keys.length; j++) {
332 var k = keys[j];
333 tgt[k] = obj[k];
334 }
335 }
336
337 return tgt;
338};
339
340var hex2tuple = function hex2tuple(hex) {
341 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
342 return;
343 }
344
345 var shortHex = hex.length === 4;
346 var r, g, b;
347 var base = 16;
348
349 if (shortHex) {
350 r = parseInt(hex[1] + hex[1], base);
351 g = parseInt(hex[2] + hex[2], base);
352 b = parseInt(hex[3] + hex[3], base);
353 } else {
354 r = parseInt(hex[1] + hex[2], base);
355 g = parseInt(hex[3] + hex[4], base);
356 b = parseInt(hex[5] + hex[6], base);
357 }
358
359 return [r, g, b];
360}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
361
362var hsl2tuple = function hsl2tuple(hsl) {
363 var ret;
364 var h, s, l, a, r, g, b;
365
366 function hue2rgb(p, q, t) {
367 if (t < 0) t += 1;
368 if (t > 1) t -= 1;
369 if (t < 1 / 6) return p + (q - p) * 6 * t;
370 if (t < 1 / 2) return q;
371 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
372 return p;
373 }
374
375 var m = new RegExp('^' + hsla + '$').exec(hsl);
376
377 if (m) {
378 // get hue
379 h = parseInt(m[1]);
380
381 if (h < 0) {
382 h = (360 - -1 * h % 360) % 360;
383 } else if (h > 360) {
384 h = h % 360;
385 }
386
387 h /= 360; // normalise on [0, 1]
388
389 s = parseFloat(m[2]);
390
391 if (s < 0 || s > 100) {
392 return;
393 } // saturation is [0, 100]
394
395
396 s = s / 100; // normalise on [0, 1]
397
398 l = parseFloat(m[3]);
399
400 if (l < 0 || l > 100) {
401 return;
402 } // lightness is [0, 100]
403
404
405 l = l / 100; // normalise on [0, 1]
406
407 a = m[4];
408
409 if (a !== undefined) {
410 a = parseFloat(a);
411
412 if (a < 0 || a > 1) {
413 return;
414 } // alpha is [0, 1]
415
416 } // now, convert to rgb
417 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
418
419
420 if (s === 0) {
421 r = g = b = Math.round(l * 255); // achromatic
422 } else {
423 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
424 var p = 2 * l - q;
425 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
426 g = Math.round(255 * hue2rgb(p, q, h));
427 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
428 }
429
430 ret = [r, g, b, a];
431 }
432
433 return ret;
434}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
435
436var rgb2tuple = function rgb2tuple(rgb) {
437 var ret;
438 var m = new RegExp('^' + rgba + '$').exec(rgb);
439
440 if (m) {
441 ret = [];
442 var isPct = [];
443
444 for (var i = 1; i <= 3; i++) {
445 var channel = m[i];
446
447 if (channel[channel.length - 1] === '%') {
448 isPct[i] = true;
449 }
450
451 channel = parseFloat(channel);
452
453 if (isPct[i]) {
454 channel = channel / 100 * 255; // normalise to [0, 255]
455 }
456
457 if (channel < 0 || channel > 255) {
458 return;
459 } // invalid channel value
460
461
462 ret.push(Math.floor(channel));
463 }
464
465 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
466 var allArePct = isPct[1] && isPct[2] && isPct[3];
467
468 if (atLeastOneIsPct && !allArePct) {
469 return;
470 } // must all be percent values if one is
471
472
473 var alpha = m[4];
474
475 if (alpha !== undefined) {
476 alpha = parseFloat(alpha);
477
478 if (alpha < 0 || alpha > 1) {
479 return;
480 } // invalid alpha value
481
482
483 ret.push(alpha);
484 }
485 }
486
487 return ret;
488};
489var colorname2tuple = function colorname2tuple(color) {
490 return colors[color.toLowerCase()];
491};
492var color2tuple = function color2tuple(color) {
493 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
494};
495var colors = {
496 // special colour names
497 transparent: [0, 0, 0, 0],
498 // NB alpha === 0
499 // regular colours
500 aliceblue: [240, 248, 255],
501 antiquewhite: [250, 235, 215],
502 aqua: [0, 255, 255],
503 aquamarine: [127, 255, 212],
504 azure: [240, 255, 255],
505 beige: [245, 245, 220],
506 bisque: [255, 228, 196],
507 black: [0, 0, 0],
508 blanchedalmond: [255, 235, 205],
509 blue: [0, 0, 255],
510 blueviolet: [138, 43, 226],
511 brown: [165, 42, 42],
512 burlywood: [222, 184, 135],
513 cadetblue: [95, 158, 160],
514 chartreuse: [127, 255, 0],
515 chocolate: [210, 105, 30],
516 coral: [255, 127, 80],
517 cornflowerblue: [100, 149, 237],
518 cornsilk: [255, 248, 220],
519 crimson: [220, 20, 60],
520 cyan: [0, 255, 255],
521 darkblue: [0, 0, 139],
522 darkcyan: [0, 139, 139],
523 darkgoldenrod: [184, 134, 11],
524 darkgray: [169, 169, 169],
525 darkgreen: [0, 100, 0],
526 darkgrey: [169, 169, 169],
527 darkkhaki: [189, 183, 107],
528 darkmagenta: [139, 0, 139],
529 darkolivegreen: [85, 107, 47],
530 darkorange: [255, 140, 0],
531 darkorchid: [153, 50, 204],
532 darkred: [139, 0, 0],
533 darksalmon: [233, 150, 122],
534 darkseagreen: [143, 188, 143],
535 darkslateblue: [72, 61, 139],
536 darkslategray: [47, 79, 79],
537 darkslategrey: [47, 79, 79],
538 darkturquoise: [0, 206, 209],
539 darkviolet: [148, 0, 211],
540 deeppink: [255, 20, 147],
541 deepskyblue: [0, 191, 255],
542 dimgray: [105, 105, 105],
543 dimgrey: [105, 105, 105],
544 dodgerblue: [30, 144, 255],
545 firebrick: [178, 34, 34],
546 floralwhite: [255, 250, 240],
547 forestgreen: [34, 139, 34],
548 fuchsia: [255, 0, 255],
549 gainsboro: [220, 220, 220],
550 ghostwhite: [248, 248, 255],
551 gold: [255, 215, 0],
552 goldenrod: [218, 165, 32],
553 gray: [128, 128, 128],
554 grey: [128, 128, 128],
555 green: [0, 128, 0],
556 greenyellow: [173, 255, 47],
557 honeydew: [240, 255, 240],
558 hotpink: [255, 105, 180],
559 indianred: [205, 92, 92],
560 indigo: [75, 0, 130],
561 ivory: [255, 255, 240],
562 khaki: [240, 230, 140],
563 lavender: [230, 230, 250],
564 lavenderblush: [255, 240, 245],
565 lawngreen: [124, 252, 0],
566 lemonchiffon: [255, 250, 205],
567 lightblue: [173, 216, 230],
568 lightcoral: [240, 128, 128],
569 lightcyan: [224, 255, 255],
570 lightgoldenrodyellow: [250, 250, 210],
571 lightgray: [211, 211, 211],
572 lightgreen: [144, 238, 144],
573 lightgrey: [211, 211, 211],
574 lightpink: [255, 182, 193],
575 lightsalmon: [255, 160, 122],
576 lightseagreen: [32, 178, 170],
577 lightskyblue: [135, 206, 250],
578 lightslategray: [119, 136, 153],
579 lightslategrey: [119, 136, 153],
580 lightsteelblue: [176, 196, 222],
581 lightyellow: [255, 255, 224],
582 lime: [0, 255, 0],
583 limegreen: [50, 205, 50],
584 linen: [250, 240, 230],
585 magenta: [255, 0, 255],
586 maroon: [128, 0, 0],
587 mediumaquamarine: [102, 205, 170],
588 mediumblue: [0, 0, 205],
589 mediumorchid: [186, 85, 211],
590 mediumpurple: [147, 112, 219],
591 mediumseagreen: [60, 179, 113],
592 mediumslateblue: [123, 104, 238],
593 mediumspringgreen: [0, 250, 154],
594 mediumturquoise: [72, 209, 204],
595 mediumvioletred: [199, 21, 133],
596 midnightblue: [25, 25, 112],
597 mintcream: [245, 255, 250],
598 mistyrose: [255, 228, 225],
599 moccasin: [255, 228, 181],
600 navajowhite: [255, 222, 173],
601 navy: [0, 0, 128],
602 oldlace: [253, 245, 230],
603 olive: [128, 128, 0],
604 olivedrab: [107, 142, 35],
605 orange: [255, 165, 0],
606 orangered: [255, 69, 0],
607 orchid: [218, 112, 214],
608 palegoldenrod: [238, 232, 170],
609 palegreen: [152, 251, 152],
610 paleturquoise: [175, 238, 238],
611 palevioletred: [219, 112, 147],
612 papayawhip: [255, 239, 213],
613 peachpuff: [255, 218, 185],
614 peru: [205, 133, 63],
615 pink: [255, 192, 203],
616 plum: [221, 160, 221],
617 powderblue: [176, 224, 230],
618 purple: [128, 0, 128],
619 red: [255, 0, 0],
620 rosybrown: [188, 143, 143],
621 royalblue: [65, 105, 225],
622 saddlebrown: [139, 69, 19],
623 salmon: [250, 128, 114],
624 sandybrown: [244, 164, 96],
625 seagreen: [46, 139, 87],
626 seashell: [255, 245, 238],
627 sienna: [160, 82, 45],
628 silver: [192, 192, 192],
629 skyblue: [135, 206, 235],
630 slateblue: [106, 90, 205],
631 slategray: [112, 128, 144],
632 slategrey: [112, 128, 144],
633 snow: [255, 250, 250],
634 springgreen: [0, 255, 127],
635 steelblue: [70, 130, 180],
636 tan: [210, 180, 140],
637 teal: [0, 128, 128],
638 thistle: [216, 191, 216],
639 tomato: [255, 99, 71],
640 turquoise: [64, 224, 208],
641 violet: [238, 130, 238],
642 wheat: [245, 222, 179],
643 white: [255, 255, 255],
644 whitesmoke: [245, 245, 245],
645 yellow: [255, 255, 0],
646 yellowgreen: [154, 205, 50]
647};
648
649var setMap = function setMap(options) {
650 var obj = options.map;
651 var keys = options.keys;
652 var l = keys.length;
653
654 for (var i = 0; i < l; i++) {
655 var key = keys[i];
656
657 if (plainObject(key)) {
658 throw Error('Tried to set map with object key');
659 }
660
661 if (i < keys.length - 1) {
662 // extend the map if necessary
663 if (obj[key] == null) {
664 obj[key] = {};
665 }
666
667 obj = obj[key];
668 } else {
669 // set the value
670 obj[key] = options.value;
671 }
672 }
673}; // gets the value in a map even if it's not built in places
674
675var getMap = function getMap(options) {
676 var obj = options.map;
677 var keys = options.keys;
678 var l = keys.length;
679
680 for (var i = 0; i < l; i++) {
681 var key = keys[i];
682
683 if (plainObject(key)) {
684 throw Error('Tried to get map with object key');
685 }
686
687 obj = obj[key];
688
689 if (obj == null) {
690 return obj;
691 }
692 }
693
694 return obj;
695}; // deletes the entry in the map
696
697var performance = window$1 ? window$1.performance : null;
698var pnow = performance && performance.now ? function () {
699 return performance.now();
700} : function () {
701 return Date.now();
702};
703
704var raf = function () {
705 if (window$1) {
706 if (window$1.requestAnimationFrame) {
707 return function (fn) {
708 window$1.requestAnimationFrame(fn);
709 };
710 } else if (window$1.mozRequestAnimationFrame) {
711 return function (fn) {
712 window$1.mozRequestAnimationFrame(fn);
713 };
714 } else if (window$1.webkitRequestAnimationFrame) {
715 return function (fn) {
716 window$1.webkitRequestAnimationFrame(fn);
717 };
718 } else if (window$1.msRequestAnimationFrame) {
719 return function (fn) {
720 window$1.msRequestAnimationFrame(fn);
721 };
722 }
723 }
724
725 return function (fn) {
726 if (fn) {
727 setTimeout(function () {
728 fn(pnow());
729 }, 1000 / 60);
730 }
731 };
732}();
733
734var requestAnimationFrame = function requestAnimationFrame(fn) {
735 return raf(fn);
736};
737var performanceNow = pnow;
738
739var DEFAULT_HASH_SEED = 9261;
740var K = 65599; // 37 also works pretty well
741
742var DEFAULT_HASH_SEED_ALT = 5381;
743var hashIterableInts = function hashIterableInts(iterator) {
744 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
745 // sdbm/string-hash
746 var hash = seed;
747 var entry;
748
749 for (;;) {
750 entry = iterator.next();
751
752 if (entry.done) {
753 break;
754 }
755
756 hash = hash * K + entry.value | 0;
757 }
758
759 return hash;
760};
761var hashInt = function hashInt(num) {
762 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
763 // sdbm/string-hash
764 return seed * K + num | 0;
765};
766var hashIntAlt = function hashIntAlt(num) {
767 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
768 // djb2/string-hash
769 return (seed << 5) + seed + num | 0;
770};
771var combineHashes = function combineHashes(hash1, hash2) {
772 return hash1 * 0x200000 + hash2;
773};
774var combineHashesArray = function combineHashesArray(hashes) {
775 return hashes[0] * 0x200000 + hashes[1];
776};
777var hashArrays = function hashArrays(hashes1, hashes2) {
778 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
779};
780var hashIntsArray = function hashIntsArray(ints, seed) {
781 var entry = {
782 value: 0,
783 done: false
784 };
785 var i = 0;
786 var length = ints.length;
787 var iterator = {
788 next: function next() {
789 if (i < length) {
790 entry.value = ints[i++];
791 } else {
792 entry.done = true;
793 }
794
795 return entry;
796 }
797 };
798 return hashIterableInts(iterator, seed);
799};
800var hashString = function hashString(str, seed) {
801 var entry = {
802 value: 0,
803 done: false
804 };
805 var i = 0;
806 var length = str.length;
807 var iterator = {
808 next: function next() {
809 if (i < length) {
810 entry.value = str.charCodeAt(i++);
811 } else {
812 entry.done = true;
813 }
814
815 return entry;
816 }
817 };
818 return hashIterableInts(iterator, seed);
819};
820var hashStrings = function hashStrings() {
821 return hashStringsArray(arguments);
822};
823var hashStringsArray = function hashStringsArray(strs) {
824 var hash;
825
826 for (var i = 0; i < strs.length; i++) {
827 var str = strs[i];
828
829 if (i === 0) {
830 hash = hashString(str);
831 } else {
832 hash = hashString(str, hash);
833 }
834 }
835
836 return hash;
837};
838
839/*global console */
840var warningsEnabled = true;
841var warnSupported = console.warn != null; // eslint-disable-line no-console
842
843var traceSupported = console.trace != null; // eslint-disable-line no-console
844
845var MAX_INT$1 = Number.MAX_SAFE_INTEGER || 9007199254740991;
846var trueify = function trueify() {
847 return true;
848};
849var falsify = function falsify() {
850 return false;
851};
852var zeroify = function zeroify() {
853 return 0;
854};
855var noop$1 = function noop() {};
856var error = function error(msg) {
857 throw new Error(msg);
858};
859var warnings = function warnings(enabled) {
860 if (enabled !== undefined) {
861 warningsEnabled = !!enabled;
862 } else {
863 return warningsEnabled;
864 }
865};
866var warn = function warn(msg) {
867 /* eslint-disable no-console */
868 if (!warnings()) {
869 return;
870 }
871
872 if (warnSupported) {
873 console.warn(msg);
874 } else {
875 console.log(msg);
876
877 if (traceSupported) {
878 console.trace();
879 }
880 }
881};
882/* eslint-enable */
883
884var clone = function clone(obj) {
885 return extend({}, obj);
886}; // gets a shallow copy of the argument
887
888var copy = function copy(obj) {
889 if (obj == null) {
890 return obj;
891 }
892
893 if (array(obj)) {
894 return obj.slice();
895 } else if (plainObject(obj)) {
896 return clone(obj);
897 } else {
898 return obj;
899 }
900};
901var copyArray = function copyArray(arr) {
902 return arr.slice();
903};
904var uuid = function uuid(a, b
905/* placeholders */
906) {
907 for ( // loop :)
908 b = a = ''; // b - result , a - numeric letiable
909 a++ < 36; //
910 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
911 ? // return a random number or 4
912 (a ^ 15 // if "a" is not 15
913 ? // generate a random number from 0 to 15
914 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
915 : 4 // otherwise 4
916 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
917 ) {
918 }
919
920 return b;
921};
922var _staticEmptyObject = {};
923var staticEmptyObject = function staticEmptyObject() {
924 return _staticEmptyObject;
925};
926var defaults$g = function defaults(_defaults) {
927 var keys = Object.keys(_defaults);
928 return function (opts) {
929 var filledOpts = {};
930
931 for (var i = 0; i < keys.length; i++) {
932 var key = keys[i];
933 var optVal = opts == null ? undefined : opts[key];
934 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
935 }
936
937 return filledOpts;
938 };
939};
940var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
941 for (var i = arr.length - 1; i >= 0; i--) {
942 if (arr[i] === ele) {
943 arr.splice(i, 1);
944
945 if (oneCopy) {
946 break;
947 }
948 }
949 }
950};
951var clearArray = function clearArray(arr) {
952 arr.splice(0, arr.length);
953};
954var push = function push(arr, otherArr) {
955 for (var i = 0; i < otherArr.length; i++) {
956 var el = otherArr[i];
957 arr.push(el);
958 }
959};
960var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
961 if (prefix) {
962 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
963 }
964
965 return obj[propName];
966};
967var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
968 if (prefix) {
969 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
970 }
971
972 obj[propName] = value;
973};
974
975/* global Map */
976var ObjectMap = /*#__PURE__*/function () {
977 function ObjectMap() {
978 _classCallCheck(this, ObjectMap);
979
980 this._obj = {};
981 }
982
983 _createClass(ObjectMap, [{
984 key: "set",
985 value: function set(key, val) {
986 this._obj[key] = val;
987 return this;
988 }
989 }, {
990 key: "delete",
991 value: function _delete(key) {
992 this._obj[key] = undefined;
993 return this;
994 }
995 }, {
996 key: "clear",
997 value: function clear() {
998 this._obj = {};
999 }
1000 }, {
1001 key: "has",
1002 value: function has(key) {
1003 return this._obj[key] !== undefined;
1004 }
1005 }, {
1006 key: "get",
1007 value: function get(key) {
1008 return this._obj[key];
1009 }
1010 }]);
1011
1012 return ObjectMap;
1013}();
1014
1015var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
1016
1017/* global Set */
1018var undef = "undefined" ;
1019
1020var ObjectSet = /*#__PURE__*/function () {
1021 function ObjectSet(arrayOrObjectSet) {
1022 _classCallCheck(this, ObjectSet);
1023
1024 this._obj = Object.create(null);
1025 this.size = 0;
1026
1027 if (arrayOrObjectSet != null) {
1028 var arr;
1029
1030 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1031 arr = arrayOrObjectSet.toArray();
1032 } else {
1033 arr = arrayOrObjectSet;
1034 }
1035
1036 for (var i = 0; i < arr.length; i++) {
1037 this.add(arr[i]);
1038 }
1039 }
1040 }
1041
1042 _createClass(ObjectSet, [{
1043 key: "instanceString",
1044 value: function instanceString() {
1045 return 'set';
1046 }
1047 }, {
1048 key: "add",
1049 value: function add(val) {
1050 var o = this._obj;
1051
1052 if (o[val] !== 1) {
1053 o[val] = 1;
1054 this.size++;
1055 }
1056 }
1057 }, {
1058 key: "delete",
1059 value: function _delete(val) {
1060 var o = this._obj;
1061
1062 if (o[val] === 1) {
1063 o[val] = 0;
1064 this.size--;
1065 }
1066 }
1067 }, {
1068 key: "clear",
1069 value: function clear() {
1070 this._obj = Object.create(null);
1071 }
1072 }, {
1073 key: "has",
1074 value: function has(val) {
1075 return this._obj[val] === 1;
1076 }
1077 }, {
1078 key: "toArray",
1079 value: function toArray() {
1080 var _this = this;
1081
1082 return Object.keys(this._obj).filter(function (key) {
1083 return _this.has(key);
1084 });
1085 }
1086 }, {
1087 key: "forEach",
1088 value: function forEach(callback, thisArg) {
1089 return this.toArray().forEach(callback, thisArg);
1090 }
1091 }]);
1092
1093 return ObjectSet;
1094}();
1095
1096var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1097
1098var Element = function Element(cy, params) {
1099 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1100
1101 if (cy === undefined || params === undefined || !core(cy)) {
1102 error('An element must have a core reference and parameters set');
1103 return;
1104 }
1105
1106 var group = params.group; // try to automatically infer the group if unspecified
1107
1108 if (group == null) {
1109 if (params.data && params.data.source != null && params.data.target != null) {
1110 group = 'edges';
1111 } else {
1112 group = 'nodes';
1113 }
1114 } // validate group
1115
1116
1117 if (group !== 'nodes' && group !== 'edges') {
1118 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1119 return;
1120 } // make the element array-like, just like a collection
1121
1122
1123 this.length = 1;
1124 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1125
1126 var _p = this._private = {
1127 cy: cy,
1128 single: true,
1129 // indicates this is an element
1130 data: params.data || {},
1131 // data object
1132 position: params.position || {
1133 x: 0,
1134 y: 0
1135 },
1136 // (x, y) position pair
1137 autoWidth: undefined,
1138 // width and height of nodes calculated by the renderer when set to special 'auto' value
1139 autoHeight: undefined,
1140 autoPadding: undefined,
1141 compoundBoundsClean: false,
1142 // whether the compound dimensions need to be recalculated the next time dimensions are read
1143 listeners: [],
1144 // array of bound listeners
1145 group: group,
1146 // string; 'nodes' or 'edges'
1147 style: {},
1148 // properties as set by the style
1149 rstyle: {},
1150 // properties for style sent from the renderer to the core
1151 styleCxts: [],
1152 // applied style contexts from the styler
1153 styleKeys: {},
1154 // per-group keys of style property values
1155 removed: true,
1156 // whether it's inside the vis; true if removed (set true here since we call restore)
1157 selected: params.selected ? true : false,
1158 // whether it's selected
1159 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1160 // whether it's selectable
1161 locked: params.locked ? true : false,
1162 // whether the element is locked (cannot be moved)
1163 grabbed: false,
1164 // whether the element is grabbed by the mouse; renderer sets this privately
1165 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1166 // whether the element can be grabbed
1167 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1168 // whether the element has passthrough panning enabled
1169 active: false,
1170 // whether the element is active from user interaction
1171 classes: new Set$1(),
1172 // map ( className => true )
1173 animation: {
1174 // object for currently-running animations
1175 current: [],
1176 queue: []
1177 },
1178 rscratch: {},
1179 // object in which the renderer can store information
1180 scratch: params.scratch || {},
1181 // scratch objects
1182 edges: [],
1183 // array of connected edges
1184 children: [],
1185 // array of children
1186 parent: params.parent && params.parent.isNode() ? params.parent : null,
1187 // parent ref
1188 traversalCache: {},
1189 // cache of output of traversal functions
1190 backgrounding: false,
1191 // whether background images are loading
1192 bbCache: null,
1193 // cache of the current bounding box
1194 bbCacheShift: {
1195 x: 0,
1196 y: 0
1197 },
1198 // shift applied to cached bb to be applied on next get
1199 bodyBounds: null,
1200 // bounds cache of element body, w/o overlay
1201 overlayBounds: null,
1202 // bounds cache of element body, including overlay
1203 labelBounds: {
1204 // bounds cache of labels
1205 all: null,
1206 source: null,
1207 target: null,
1208 main: null
1209 },
1210 arrowBounds: {
1211 // bounds cache of edge arrows
1212 source: null,
1213 target: null,
1214 'mid-source': null,
1215 'mid-target': null
1216 }
1217 };
1218
1219 if (_p.position.x == null) {
1220 _p.position.x = 0;
1221 }
1222
1223 if (_p.position.y == null) {
1224 _p.position.y = 0;
1225 } // renderedPosition overrides if specified
1226
1227
1228 if (params.renderedPosition) {
1229 var rpos = params.renderedPosition;
1230 var pan = cy.pan();
1231 var zoom = cy.zoom();
1232 _p.position = {
1233 x: (rpos.x - pan.x) / zoom,
1234 y: (rpos.y - pan.y) / zoom
1235 };
1236 }
1237
1238 var classes = [];
1239
1240 if (array(params.classes)) {
1241 classes = params.classes;
1242 } else if (string(params.classes)) {
1243 classes = params.classes.split(/\s+/);
1244 }
1245
1246 for (var i = 0, l = classes.length; i < l; i++) {
1247 var cls = classes[i];
1248
1249 if (!cls || cls === '') {
1250 continue;
1251 }
1252
1253 _p.classes.add(cls);
1254 }
1255
1256 this.createEmitter();
1257 var bypass = params.style || params.css;
1258
1259 if (bypass) {
1260 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1261 this.style(bypass);
1262 }
1263
1264 if (restore === undefined || restore) {
1265 this.restore();
1266 }
1267};
1268
1269var defineSearch = function defineSearch(params) {
1270 params = {
1271 bfs: params.bfs || !params.dfs,
1272 dfs: params.dfs || !params.bfs
1273 }; // from pseudocode on wikipedia
1274
1275 return function searchFn(roots, fn, directed) {
1276 var options;
1277
1278 if (plainObject(roots) && !elementOrCollection(roots)) {
1279 options = roots;
1280 roots = options.roots || options.root;
1281 fn = options.visit;
1282 directed = options.directed;
1283 }
1284
1285 directed = arguments.length === 2 && !fn$6(fn) ? fn : directed;
1286 fn = fn$6(fn) ? fn : function () {};
1287 var cy = this._private.cy;
1288 var v = roots = string(roots) ? this.filter(roots) : roots;
1289 var Q = [];
1290 var connectedNodes = [];
1291 var connectedBy = {};
1292 var id2depth = {};
1293 var V = {};
1294 var j = 0;
1295 var found;
1296
1297 var _this$byGroup = this.byGroup(),
1298 nodes = _this$byGroup.nodes,
1299 edges = _this$byGroup.edges; // enqueue v
1300
1301
1302 for (var i = 0; i < v.length; i++) {
1303 var vi = v[i];
1304 var viId = vi.id();
1305
1306 if (vi.isNode()) {
1307 Q.unshift(vi);
1308
1309 if (params.bfs) {
1310 V[viId] = true;
1311 connectedNodes.push(vi);
1312 }
1313
1314 id2depth[viId] = 0;
1315 }
1316 }
1317
1318 var _loop = function _loop() {
1319 var v = params.bfs ? Q.shift() : Q.pop();
1320 var vId = v.id();
1321
1322 if (params.dfs) {
1323 if (V[vId]) {
1324 return "continue";
1325 }
1326
1327 V[vId] = true;
1328 connectedNodes.push(v);
1329 }
1330
1331 var depth = id2depth[vId];
1332 var prevEdge = connectedBy[vId];
1333 var src = prevEdge != null ? prevEdge.source() : null;
1334 var tgt = prevEdge != null ? prevEdge.target() : null;
1335 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1336 var ret = void 0;
1337 ret = fn(v, prevEdge, prevNode, j++, depth);
1338
1339 if (ret === true) {
1340 found = v;
1341 return "break";
1342 }
1343
1344 if (ret === false) {
1345 return "break";
1346 }
1347
1348 var vwEdges = v.connectedEdges().filter(function (e) {
1349 return (!directed || e.source().same(v)) && edges.has(e);
1350 });
1351
1352 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1353 var e = vwEdges[_i2];
1354 var w = e.connectedNodes().filter(function (n) {
1355 return !n.same(v) && nodes.has(n);
1356 });
1357 var wId = w.id();
1358
1359 if (w.length !== 0 && !V[wId]) {
1360 w = w[0];
1361 Q.push(w);
1362
1363 if (params.bfs) {
1364 V[wId] = true;
1365 connectedNodes.push(w);
1366 }
1367
1368 connectedBy[wId] = e;
1369 id2depth[wId] = id2depth[vId] + 1;
1370 }
1371 }
1372 };
1373
1374 while (Q.length !== 0) {
1375 var _ret = _loop();
1376
1377 if (_ret === "continue") continue;
1378 if (_ret === "break") break;
1379 }
1380
1381 var connectedEles = cy.collection();
1382
1383 for (var _i = 0; _i < connectedNodes.length; _i++) {
1384 var node = connectedNodes[_i];
1385 var edge = connectedBy[node.id()];
1386
1387 if (edge != null) {
1388 connectedEles.push(edge);
1389 }
1390
1391 connectedEles.push(node);
1392 }
1393
1394 return {
1395 path: cy.collection(connectedEles),
1396 found: cy.collection(found)
1397 };
1398 };
1399}; // search, spanning trees, etc
1400
1401
1402var elesfn$v = {
1403 breadthFirstSearch: defineSearch({
1404 bfs: true
1405 }),
1406 depthFirstSearch: defineSearch({
1407 dfs: true
1408 })
1409}; // nice, short mathematical alias
1410
1411elesfn$v.bfs = elesfn$v.breadthFirstSearch;
1412elesfn$v.dfs = elesfn$v.depthFirstSearch;
1413
1414var dijkstraDefaults = defaults$g({
1415 root: null,
1416 weight: function weight(edge) {
1417 return 1;
1418 },
1419 directed: false
1420});
1421var elesfn$u = {
1422 dijkstra: function dijkstra(options) {
1423 if (!plainObject(options)) {
1424 var args = arguments;
1425 options = {
1426 root: args[0],
1427 weight: args[1],
1428 directed: args[2]
1429 };
1430 }
1431
1432 var _dijkstraDefaults = dijkstraDefaults(options),
1433 root = _dijkstraDefaults.root,
1434 weight = _dijkstraDefaults.weight,
1435 directed = _dijkstraDefaults.directed;
1436
1437 var eles = this;
1438 var weightFn = weight;
1439 var source = string(root) ? this.filter(root)[0] : root[0];
1440 var dist = {};
1441 var prev = {};
1442 var knownDist = {};
1443
1444 var _this$byGroup = this.byGroup(),
1445 nodes = _this$byGroup.nodes,
1446 edges = _this$byGroup.edges;
1447
1448 edges.unmergeBy(function (ele) {
1449 return ele.isLoop();
1450 });
1451
1452 var getDist = function getDist(node) {
1453 return dist[node.id()];
1454 };
1455
1456 var setDist = function setDist(node, d) {
1457 dist[node.id()] = d;
1458 Q.updateItem(node);
1459 };
1460
1461 var Q = new Heap__default["default"](function (a, b) {
1462 return getDist(a) - getDist(b);
1463 });
1464
1465 for (var i = 0; i < nodes.length; i++) {
1466 var node = nodes[i];
1467 dist[node.id()] = node.same(source) ? 0 : Infinity;
1468 Q.push(node);
1469 }
1470
1471 var distBetween = function distBetween(u, v) {
1472 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1473 var smallestDistance = Infinity;
1474 var smallestEdge;
1475
1476 for (var _i = 0; _i < uvs.length; _i++) {
1477 var edge = uvs[_i];
1478
1479 var _weight = weightFn(edge);
1480
1481 if (_weight < smallestDistance || !smallestEdge) {
1482 smallestDistance = _weight;
1483 smallestEdge = edge;
1484 }
1485 }
1486
1487 return {
1488 edge: smallestEdge,
1489 dist: smallestDistance
1490 };
1491 };
1492
1493 while (Q.size() > 0) {
1494 var u = Q.pop();
1495 var smalletsDist = getDist(u);
1496 var uid = u.id();
1497 knownDist[uid] = smalletsDist;
1498
1499 if (smalletsDist === Infinity) {
1500 continue;
1501 }
1502
1503 var neighbors = u.neighborhood().intersect(nodes);
1504
1505 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1506 var v = neighbors[_i2];
1507 var vid = v.id();
1508 var vDist = distBetween(u, v);
1509 var alt = smalletsDist + vDist.dist;
1510
1511 if (alt < getDist(v)) {
1512 setDist(v, alt);
1513 prev[vid] = {
1514 node: u,
1515 edge: vDist.edge
1516 };
1517 }
1518 } // for
1519
1520 } // while
1521
1522
1523 return {
1524 distanceTo: function distanceTo(node) {
1525 var target = string(node) ? nodes.filter(node)[0] : node[0];
1526 return knownDist[target.id()];
1527 },
1528 pathTo: function pathTo(node) {
1529 var target = string(node) ? nodes.filter(node)[0] : node[0];
1530 var S = [];
1531 var u = target;
1532 var uid = u.id();
1533
1534 if (target.length > 0) {
1535 S.unshift(target);
1536
1537 while (prev[uid]) {
1538 var p = prev[uid];
1539 S.unshift(p.edge);
1540 S.unshift(p.node);
1541 u = p.node;
1542 uid = u.id();
1543 }
1544 }
1545
1546 return eles.spawn(S);
1547 }
1548 };
1549 }
1550};
1551
1552var elesfn$t = {
1553 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1554 // implemented from pseudocode from wikipedia
1555 kruskal: function kruskal(weightFn) {
1556 weightFn = weightFn || function (edge) {
1557 return 1;
1558 };
1559
1560 var _this$byGroup = this.byGroup(),
1561 nodes = _this$byGroup.nodes,
1562 edges = _this$byGroup.edges;
1563
1564 var numNodes = nodes.length;
1565 var forest = new Array(numNodes);
1566 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1567
1568 var findSetIndex = function findSetIndex(ele) {
1569 for (var i = 0; i < forest.length; i++) {
1570 var eles = forest[i];
1571
1572 if (eles.has(ele)) {
1573 return i;
1574 }
1575 }
1576 }; // start with one forest per node
1577
1578
1579 for (var i = 0; i < numNodes; i++) {
1580 forest[i] = this.spawn(nodes[i]);
1581 }
1582
1583 var S = edges.sort(function (a, b) {
1584 return weightFn(a) - weightFn(b);
1585 });
1586
1587 for (var _i = 0; _i < S.length; _i++) {
1588 var edge = S[_i];
1589 var u = edge.source()[0];
1590 var v = edge.target()[0];
1591 var setUIndex = findSetIndex(u);
1592 var setVIndex = findSetIndex(v);
1593 var setU = forest[setUIndex];
1594 var setV = forest[setVIndex];
1595
1596 if (setUIndex !== setVIndex) {
1597 A.merge(edge); // combine forests for u and v
1598
1599 setU.merge(setV);
1600 forest.splice(setVIndex, 1);
1601 }
1602 }
1603
1604 return A;
1605 }
1606};
1607
1608var aStarDefaults = defaults$g({
1609 root: null,
1610 goal: null,
1611 weight: function weight(edge) {
1612 return 1;
1613 },
1614 heuristic: function heuristic(edge) {
1615 return 0;
1616 },
1617 directed: false
1618});
1619var elesfn$s = {
1620 // Implemented from pseudocode from wikipedia
1621 aStar: function aStar(options) {
1622 var cy = this.cy();
1623
1624 var _aStarDefaults = aStarDefaults(options),
1625 root = _aStarDefaults.root,
1626 goal = _aStarDefaults.goal,
1627 heuristic = _aStarDefaults.heuristic,
1628 directed = _aStarDefaults.directed,
1629 weight = _aStarDefaults.weight;
1630
1631 root = cy.collection(root)[0];
1632 goal = cy.collection(goal)[0];
1633 var sid = root.id();
1634 var tid = goal.id();
1635 var gScore = {};
1636 var fScore = {};
1637 var closedSetIds = {};
1638 var openSet = new Heap__default["default"](function (a, b) {
1639 return fScore[a.id()] - fScore[b.id()];
1640 });
1641 var openSetIds = new Set$1();
1642 var cameFrom = {};
1643 var cameFromEdge = {};
1644
1645 var addToOpenSet = function addToOpenSet(ele, id) {
1646 openSet.push(ele);
1647 openSetIds.add(id);
1648 };
1649
1650 var cMin, cMinId;
1651
1652 var popFromOpenSet = function popFromOpenSet() {
1653 cMin = openSet.pop();
1654 cMinId = cMin.id();
1655 openSetIds["delete"](cMinId);
1656 };
1657
1658 var isInOpenSet = function isInOpenSet(id) {
1659 return openSetIds.has(id);
1660 };
1661
1662 addToOpenSet(root, sid);
1663 gScore[sid] = 0;
1664 fScore[sid] = heuristic(root); // Counter
1665
1666 var steps = 0; // Main loop
1667
1668 while (openSet.size() > 0) {
1669 popFromOpenSet();
1670 steps++; // If we've found our goal, then we are done
1671
1672 if (cMinId === tid) {
1673 var path = [];
1674 var pathNode = goal;
1675 var pathNodeId = tid;
1676 var pathEdge = cameFromEdge[pathNodeId];
1677
1678 for (;;) {
1679 path.unshift(pathNode);
1680
1681 if (pathEdge != null) {
1682 path.unshift(pathEdge);
1683 }
1684
1685 pathNode = cameFrom[pathNodeId];
1686
1687 if (pathNode == null) {
1688 break;
1689 }
1690
1691 pathNodeId = pathNode.id();
1692 pathEdge = cameFromEdge[pathNodeId];
1693 }
1694
1695 return {
1696 found: true,
1697 distance: gScore[cMinId],
1698 path: this.spawn(path),
1699 steps: steps
1700 };
1701 } // Add cMin to processed nodes
1702
1703
1704 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1705 // Take into account if graph is directed or not
1706
1707 var vwEdges = cMin._private.edges;
1708
1709 for (var i = 0; i < vwEdges.length; i++) {
1710 var e = vwEdges[i]; // edge must be in set of calling eles
1711
1712 if (!this.hasElementWithId(e.id())) {
1713 continue;
1714 } // cMin must be the source of edge if directed
1715
1716
1717 if (directed && e.data('source') !== cMinId) {
1718 continue;
1719 }
1720
1721 var wSrc = e.source();
1722 var wTgt = e.target();
1723 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1724 var wid = w.id(); // node must be in set of calling eles
1725
1726 if (!this.hasElementWithId(wid)) {
1727 continue;
1728 } // if node is in closedSet, ignore it
1729
1730
1731 if (closedSetIds[wid]) {
1732 continue;
1733 } // New tentative score for node w
1734
1735
1736 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1737 // w not present in openSet
1738 // OR
1739 // tentative gScore is less than previous value
1740 // w not in openSet
1741
1742 if (!isInOpenSet(wid)) {
1743 gScore[wid] = tempScore;
1744 fScore[wid] = tempScore + heuristic(w);
1745 addToOpenSet(w, wid);
1746 cameFrom[wid] = cMin;
1747 cameFromEdge[wid] = e;
1748 continue;
1749 } // w already in openSet, but with greater gScore
1750
1751
1752 if (tempScore < gScore[wid]) {
1753 gScore[wid] = tempScore;
1754 fScore[wid] = tempScore + heuristic(w);
1755 cameFrom[wid] = cMin;
1756 cameFromEdge[wid] = e;
1757 }
1758 } // End of neighbors update
1759
1760 } // End of main loop
1761 // If we've reached here, then we've not reached our goal
1762
1763
1764 return {
1765 found: false,
1766 distance: undefined,
1767 path: undefined,
1768 steps: steps
1769 };
1770 }
1771}; // elesfn
1772
1773var floydWarshallDefaults = defaults$g({
1774 weight: function weight(edge) {
1775 return 1;
1776 },
1777 directed: false
1778});
1779var elesfn$r = {
1780 // Implemented from pseudocode from wikipedia
1781 floydWarshall: function floydWarshall(options) {
1782 var cy = this.cy();
1783
1784 var _floydWarshallDefault = floydWarshallDefaults(options),
1785 weight = _floydWarshallDefault.weight,
1786 directed = _floydWarshallDefault.directed;
1787
1788 var weightFn = weight;
1789
1790 var _this$byGroup = this.byGroup(),
1791 nodes = _this$byGroup.nodes,
1792 edges = _this$byGroup.edges;
1793
1794 var N = nodes.length;
1795 var Nsq = N * N;
1796
1797 var indexOf = function indexOf(node) {
1798 return nodes.indexOf(node);
1799 };
1800
1801 var atIndex = function atIndex(i) {
1802 return nodes[i];
1803 }; // Initialize distance matrix
1804
1805
1806 var dist = new Array(Nsq);
1807
1808 for (var n = 0; n < Nsq; n++) {
1809 var j = n % N;
1810 var i = (n - j) / N;
1811
1812 if (i === j) {
1813 dist[n] = 0;
1814 } else {
1815 dist[n] = Infinity;
1816 }
1817 } // Initialize matrix used for path reconstruction
1818 // Initialize distance matrix
1819
1820
1821 var next = new Array(Nsq);
1822 var edgeNext = new Array(Nsq); // Process edges
1823
1824 for (var _i = 0; _i < edges.length; _i++) {
1825 var edge = edges[_i];
1826 var src = edge.source()[0];
1827 var tgt = edge.target()[0];
1828
1829 if (src === tgt) {
1830 continue;
1831 } // exclude loops
1832
1833
1834 var s = indexOf(src);
1835 var t = indexOf(tgt);
1836 var st = s * N + t; // source to target index
1837
1838 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1839
1840
1841 if (dist[st] > _weight) {
1842 dist[st] = _weight;
1843 next[st] = t;
1844 edgeNext[st] = edge;
1845 } // If undirected graph, process 'reversed' edge
1846
1847
1848 if (!directed) {
1849 var ts = t * N + s; // target to source index
1850
1851 if (!directed && dist[ts] > _weight) {
1852 dist[ts] = _weight;
1853 next[ts] = s;
1854 edgeNext[ts] = edge;
1855 }
1856 }
1857 } // Main loop
1858
1859
1860 for (var k = 0; k < N; k++) {
1861 for (var _i2 = 0; _i2 < N; _i2++) {
1862 var ik = _i2 * N + k;
1863
1864 for (var _j = 0; _j < N; _j++) {
1865 var ij = _i2 * N + _j;
1866 var kj = k * N + _j;
1867
1868 if (dist[ik] + dist[kj] < dist[ij]) {
1869 dist[ij] = dist[ik] + dist[kj];
1870 next[ij] = next[ik];
1871 }
1872 }
1873 }
1874 }
1875
1876 var getArgEle = function getArgEle(ele) {
1877 return (string(ele) ? cy.filter(ele) : ele)[0];
1878 };
1879
1880 var indexOfArgEle = function indexOfArgEle(ele) {
1881 return indexOf(getArgEle(ele));
1882 };
1883
1884 var res = {
1885 distance: function distance(from, to) {
1886 var i = indexOfArgEle(from);
1887 var j = indexOfArgEle(to);
1888 return dist[i * N + j];
1889 },
1890 path: function path(from, to) {
1891 var i = indexOfArgEle(from);
1892 var j = indexOfArgEle(to);
1893 var fromNode = atIndex(i);
1894
1895 if (i === j) {
1896 return fromNode.collection();
1897 }
1898
1899 if (next[i * N + j] == null) {
1900 return cy.collection();
1901 }
1902
1903 var path = cy.collection();
1904 var prev = i;
1905 var edge;
1906 path.merge(fromNode);
1907
1908 while (i !== j) {
1909 prev = i;
1910 i = next[i * N + j];
1911 edge = edgeNext[prev * N + i];
1912 path.merge(edge);
1913 path.merge(atIndex(i));
1914 }
1915
1916 return path;
1917 }
1918 };
1919 return res;
1920 } // floydWarshall
1921
1922}; // elesfn
1923
1924var bellmanFordDefaults = defaults$g({
1925 weight: function weight(edge) {
1926 return 1;
1927 },
1928 directed: false,
1929 root: null
1930});
1931var elesfn$q = {
1932 // Implemented from pseudocode from wikipedia
1933 bellmanFord: function bellmanFord(options) {
1934 var _this = this;
1935
1936 var _bellmanFordDefaults = bellmanFordDefaults(options),
1937 weight = _bellmanFordDefaults.weight,
1938 directed = _bellmanFordDefaults.directed,
1939 root = _bellmanFordDefaults.root;
1940
1941 var weightFn = weight;
1942 var eles = this;
1943 var cy = this.cy();
1944
1945 var _this$byGroup = this.byGroup(),
1946 edges = _this$byGroup.edges,
1947 nodes = _this$byGroup.nodes;
1948
1949 var numNodes = nodes.length;
1950 var infoMap = new Map$1();
1951 var hasNegativeWeightCycle = false;
1952 var negativeWeightCycles = [];
1953 root = cy.collection(root)[0]; // in case selector passed
1954
1955 edges.unmergeBy(function (edge) {
1956 return edge.isLoop();
1957 });
1958 var numEdges = edges.length;
1959
1960 var getInfo = function getInfo(node) {
1961 var obj = infoMap.get(node.id());
1962
1963 if (!obj) {
1964 obj = {};
1965 infoMap.set(node.id(), obj);
1966 }
1967
1968 return obj;
1969 };
1970
1971 var getNodeFromTo = function getNodeFromTo(to) {
1972 return (string(to) ? cy.$(to) : to)[0];
1973 };
1974
1975 var distanceTo = function distanceTo(to) {
1976 return getInfo(getNodeFromTo(to)).dist;
1977 };
1978
1979 var pathTo = function pathTo(to) {
1980 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1981 var end = getNodeFromTo(to);
1982 var path = [];
1983 var node = end;
1984
1985 for (;;) {
1986 if (node == null) {
1987 return _this.spawn();
1988 }
1989
1990 var _getInfo = getInfo(node),
1991 edge = _getInfo.edge,
1992 pred = _getInfo.pred;
1993
1994 path.unshift(node[0]);
1995
1996 if (node.same(thisStart) && path.length > 0) {
1997 break;
1998 }
1999
2000 if (edge != null) {
2001 path.unshift(edge);
2002 }
2003
2004 node = pred;
2005 }
2006
2007 return eles.spawn(path);
2008 }; // Initializations { dist, pred, edge }
2009
2010
2011 for (var i = 0; i < numNodes; i++) {
2012 var node = nodes[i];
2013 var info = getInfo(node);
2014
2015 if (node.same(root)) {
2016 info.dist = 0;
2017 } else {
2018 info.dist = Infinity;
2019 }
2020
2021 info.pred = null;
2022 info.edge = null;
2023 } // Edges relaxation
2024
2025
2026 var replacedEdge = false;
2027
2028 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2029 var dist = info1.dist + weight;
2030
2031 if (dist < info2.dist && !edge.same(info1.edge)) {
2032 info2.dist = dist;
2033 info2.pred = node1;
2034 info2.edge = edge;
2035 replacedEdge = true;
2036 }
2037 };
2038
2039 for (var _i = 1; _i < numNodes; _i++) {
2040 replacedEdge = false;
2041
2042 for (var e = 0; e < numEdges; e++) {
2043 var edge = edges[e];
2044 var src = edge.source();
2045 var tgt = edge.target();
2046
2047 var _weight = weightFn(edge);
2048
2049 var srcInfo = getInfo(src);
2050 var tgtInfo = getInfo(tgt);
2051 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2052
2053 if (!directed) {
2054 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2055 }
2056 }
2057
2058 if (!replacedEdge) {
2059 break;
2060 }
2061 }
2062
2063 if (replacedEdge) {
2064 // Check for negative weight cycles
2065 var negativeWeightCycleIds = [];
2066
2067 for (var _e = 0; _e < numEdges; _e++) {
2068 var _edge = edges[_e];
2069
2070 var _src = _edge.source();
2071
2072 var _tgt = _edge.target();
2073
2074 var _weight2 = weightFn(_edge);
2075
2076 var srcDist = getInfo(_src).dist;
2077 var tgtDist = getInfo(_tgt).dist;
2078
2079 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2080 if (!hasNegativeWeightCycle) {
2081 warn('Graph contains a negative weight cycle for Bellman-Ford');
2082 hasNegativeWeightCycle = true;
2083 }
2084
2085 if (options.findNegativeWeightCycles !== false) {
2086 var negativeNodes = [];
2087
2088 if (srcDist + _weight2 < tgtDist) {
2089 negativeNodes.push(_src);
2090 }
2091
2092 if (!directed && tgtDist + _weight2 < srcDist) {
2093 negativeNodes.push(_tgt);
2094 }
2095
2096 var numNegativeNodes = negativeNodes.length;
2097
2098 for (var n = 0; n < numNegativeNodes; n++) {
2099 var start = negativeNodes[n];
2100 var cycle = [start];
2101 cycle.push(getInfo(start).edge);
2102 var _node = getInfo(start).pred;
2103
2104 while (cycle.indexOf(_node) === -1) {
2105 cycle.push(_node);
2106 cycle.push(getInfo(_node).edge);
2107 _node = getInfo(_node).pred;
2108 }
2109
2110 cycle = cycle.slice(cycle.indexOf(_node));
2111 var smallestId = cycle[0].id();
2112 var smallestIndex = 0;
2113
2114 for (var c = 2; c < cycle.length; c += 2) {
2115 if (cycle[c].id() < smallestId) {
2116 smallestId = cycle[c].id();
2117 smallestIndex = c;
2118 }
2119 }
2120
2121 cycle = cycle.slice(smallestIndex).concat(cycle.slice(0, smallestIndex));
2122 cycle.push(cycle[0]);
2123 var cycleId = cycle.map(function (el) {
2124 return el.id();
2125 }).join(",");
2126
2127 if (negativeWeightCycleIds.indexOf(cycleId) === -1) {
2128 negativeWeightCycles.push(eles.spawn(cycle));
2129 negativeWeightCycleIds.push(cycleId);
2130 }
2131 }
2132 } else {
2133 break;
2134 }
2135 }
2136 }
2137 }
2138
2139 return {
2140 distanceTo: distanceTo,
2141 pathTo: pathTo,
2142 hasNegativeWeightCycle: hasNegativeWeightCycle,
2143 negativeWeightCycles: negativeWeightCycles
2144 };
2145 } // bellmanFord
2146
2147}; // elesfn
2148
2149var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2150// Updates the remaining edge lists
2151// Receives as a paramater the edge which causes the collapse
2152
2153var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2154 if (remainingEdges.length === 0) {
2155 error("Karger-Stein must be run on a connected (sub)graph");
2156 }
2157
2158 var edgeInfo = remainingEdges[edgeIndex];
2159 var sourceIn = edgeInfo[1];
2160 var targetIn = edgeInfo[2];
2161 var partition1 = nodeMap[sourceIn];
2162 var partition2 = nodeMap[targetIn];
2163 var newEdges = remainingEdges; // re-use array
2164 // Delete all edges between partition1 and partition2
2165
2166 for (var i = newEdges.length - 1; i >= 0; i--) {
2167 var edge = newEdges[i];
2168 var src = edge[1];
2169 var tgt = edge[2];
2170
2171 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2172 newEdges.splice(i, 1);
2173 }
2174 } // All edges pointing to partition2 should now point to partition1
2175
2176
2177 for (var _i = 0; _i < newEdges.length; _i++) {
2178 var _edge = newEdges[_i];
2179
2180 if (_edge[1] === partition2) {
2181 // Check source
2182 newEdges[_i] = _edge.slice(); // copy
2183
2184 newEdges[_i][1] = partition1;
2185 } else if (_edge[2] === partition2) {
2186 // Check target
2187 newEdges[_i] = _edge.slice(); // copy
2188
2189 newEdges[_i][2] = partition1;
2190 }
2191 } // Move all nodes from partition2 to partition1
2192
2193
2194 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2195 if (nodeMap[_i2] === partition2) {
2196 nodeMap[_i2] = partition1;
2197 }
2198 }
2199
2200 return newEdges;
2201}; // Contracts a graph until we reach a certain number of meta nodes
2202
2203
2204var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2205 while (size > sizeLimit) {
2206 // Choose an edge randomly
2207 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2208
2209 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2210 size--;
2211 }
2212
2213 return remainingEdges;
2214};
2215
2216var elesfn$p = {
2217 // Computes the minimum cut of an undirected graph
2218 // Returns the correct answer with high probability
2219 kargerStein: function kargerStein() {
2220 var _this = this;
2221
2222 var _this$byGroup = this.byGroup(),
2223 nodes = _this$byGroup.nodes,
2224 edges = _this$byGroup.edges;
2225
2226 edges.unmergeBy(function (edge) {
2227 return edge.isLoop();
2228 });
2229 var numNodes = nodes.length;
2230 var numEdges = edges.length;
2231 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2232 var stopSize = Math.floor(numNodes / sqrt2);
2233
2234 if (numNodes < 2) {
2235 error('At least 2 nodes are required for Karger-Stein algorithm');
2236 return undefined;
2237 } // Now store edge destination as indexes
2238 // Format for each edge (edge index, source node index, target node index)
2239
2240
2241 var edgeIndexes = [];
2242
2243 for (var i = 0; i < numEdges; i++) {
2244 var e = edges[i];
2245 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2246 } // We will store the best cut found here
2247
2248
2249 var minCutSize = Infinity;
2250 var minCutEdgeIndexes = [];
2251 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2252
2253 var metaNodeMap = new Array(numNodes);
2254 var metaNodeMap2 = new Array(numNodes);
2255
2256 var copyNodesMap = function copyNodesMap(from, to) {
2257 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2258 to[_i3] = from[_i3];
2259 }
2260 }; // Main loop
2261
2262
2263 for (var iter = 0; iter <= numIter; iter++) {
2264 // Reset meta node partition
2265 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2266 metaNodeMap[_i4] = _i4;
2267 } // Contract until stop point (stopSize nodes)
2268
2269
2270 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2271 var edgesState2 = edgesState.slice(); // copy
2272 // Create a copy of the colapsed nodes state
2273
2274 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2275
2276 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2277 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2278
2279 if (res1.length <= res2.length && res1.length < minCutSize) {
2280 minCutSize = res1.length;
2281 minCutEdgeIndexes = res1;
2282 copyNodesMap(metaNodeMap, minCutNodeMap);
2283 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2284 minCutSize = res2.length;
2285 minCutEdgeIndexes = res2;
2286 copyNodesMap(metaNodeMap2, minCutNodeMap);
2287 }
2288 } // end of main loop
2289 // Construct result
2290
2291
2292 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2293 return edges[e[0]];
2294 }));
2295 var partition1 = this.spawn();
2296 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2297
2298 var witnessNodePartition = minCutNodeMap[0];
2299
2300 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2301 var partitionId = minCutNodeMap[_i5];
2302 var node = nodes[_i5];
2303
2304 if (partitionId === witnessNodePartition) {
2305 partition1.merge(node);
2306 } else {
2307 partition2.merge(node);
2308 }
2309 } // construct components corresponding to each disjoint subset of nodes
2310
2311
2312 var constructComponent = function constructComponent(subset) {
2313 var component = _this.spawn();
2314
2315 subset.forEach(function (node) {
2316 component.merge(node);
2317 node.connectedEdges().forEach(function (edge) {
2318 // ensure edge is within calling collection and edge is not in cut
2319 if (_this.contains(edge) && !cut.contains(edge)) {
2320 component.merge(edge);
2321 }
2322 });
2323 });
2324 return component;
2325 };
2326
2327 var components = [constructComponent(partition1), constructComponent(partition2)];
2328 var ret = {
2329 cut: cut,
2330 components: components,
2331 // n.b. partitions are included to be compatible with the old api spec
2332 // (could be removed in a future major version)
2333 partition1: partition1,
2334 partition2: partition2
2335 };
2336 return ret;
2337 }
2338}; // elesfn
2339
2340var copyPosition = function copyPosition(p) {
2341 return {
2342 x: p.x,
2343 y: p.y
2344 };
2345};
2346var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2347 return {
2348 x: p.x * zoom + pan.x,
2349 y: p.y * zoom + pan.y
2350 };
2351};
2352var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2353 return {
2354 x: (p.x - pan.x) / zoom,
2355 y: (p.y - pan.y) / zoom
2356 };
2357};
2358var array2point = function array2point(arr) {
2359 return {
2360 x: arr[0],
2361 y: arr[1]
2362 };
2363};
2364var min = function min(arr) {
2365 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2366 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2367 var min = Infinity;
2368
2369 for (var i = begin; i < end; i++) {
2370 var val = arr[i];
2371
2372 if (isFinite(val)) {
2373 min = Math.min(val, min);
2374 }
2375 }
2376
2377 return min;
2378};
2379var max = function max(arr) {
2380 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2381 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2382 var max = -Infinity;
2383
2384 for (var i = begin; i < end; i++) {
2385 var val = arr[i];
2386
2387 if (isFinite(val)) {
2388 max = Math.max(val, max);
2389 }
2390 }
2391
2392 return max;
2393};
2394var mean = function mean(arr) {
2395 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2396 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2397 var total = 0;
2398 var n = 0;
2399
2400 for (var i = begin; i < end; i++) {
2401 var val = arr[i];
2402
2403 if (isFinite(val)) {
2404 total += val;
2405 n++;
2406 }
2407 }
2408
2409 return total / n;
2410};
2411var median = function median(arr) {
2412 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2413 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2414 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2415 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2416 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2417
2418 if (copy) {
2419 arr = arr.slice(begin, end);
2420 } else {
2421 if (end < arr.length) {
2422 arr.splice(end, arr.length - end);
2423 }
2424
2425 if (begin > 0) {
2426 arr.splice(0, begin);
2427 }
2428 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2429
2430
2431 var off = 0; // offset from non-finite values
2432
2433 for (var i = arr.length - 1; i >= 0; i--) {
2434 var v = arr[i];
2435
2436 if (includeHoles) {
2437 if (!isFinite(v)) {
2438 arr[i] = -Infinity;
2439 off++;
2440 }
2441 } else {
2442 // just remove it if we don't want to consider holes
2443 arr.splice(i, 1);
2444 }
2445 }
2446
2447 if (sort) {
2448 arr.sort(function (a, b) {
2449 return a - b;
2450 }); // requires copy = true if you don't want to change the orig
2451 }
2452
2453 var len = arr.length;
2454 var mid = Math.floor(len / 2);
2455
2456 if (len % 2 !== 0) {
2457 return arr[mid + 1 + off];
2458 } else {
2459 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2460 }
2461};
2462var deg2rad = function deg2rad(deg) {
2463 return Math.PI * deg / 180;
2464};
2465var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2466 return Math.atan2(dispY, dispX) - Math.PI / 2;
2467};
2468var log2 = Math.log2 || function (n) {
2469 return Math.log(n) / Math.log(2);
2470};
2471var signum = function signum(x) {
2472 if (x > 0) {
2473 return 1;
2474 } else if (x < 0) {
2475 return -1;
2476 } else {
2477 return 0;
2478 }
2479};
2480var dist = function dist(p1, p2) {
2481 return Math.sqrt(sqdist(p1, p2));
2482};
2483var sqdist = function sqdist(p1, p2) {
2484 var dx = p2.x - p1.x;
2485 var dy = p2.y - p1.y;
2486 return dx * dx + dy * dy;
2487};
2488var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2489 var length = v.length; // First, get sum of all elements
2490
2491 var total = 0;
2492
2493 for (var i = 0; i < length; i++) {
2494 total += v[i];
2495 } // Now, divide each by the sum of all elements
2496
2497
2498 for (var _i = 0; _i < length; _i++) {
2499 v[_i] = v[_i] / total;
2500 }
2501
2502 return v;
2503};
2504
2505var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2506 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2507};
2508var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2509 return {
2510 x: qbezierAt(p0.x, p1.x, p2.x, t),
2511 y: qbezierAt(p0.y, p1.y, p2.y, t)
2512 };
2513};
2514var lineAt = function lineAt(p0, p1, t, d) {
2515 var vec = {
2516 x: p1.x - p0.x,
2517 y: p1.y - p0.y
2518 };
2519 var vecDist = dist(p0, p1);
2520 var normVec = {
2521 x: vec.x / vecDist,
2522 y: vec.y / vecDist
2523 };
2524 t = t == null ? 0 : t;
2525 d = d != null ? d : t * vecDist;
2526 return {
2527 x: p0.x + normVec.x * d,
2528 y: p0.y + normVec.y * d
2529 };
2530};
2531var bound = function bound(min, val, max) {
2532 return Math.max(min, Math.min(max, val));
2533}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2534
2535var makeBoundingBox = function makeBoundingBox(bb) {
2536 if (bb == null) {
2537 return {
2538 x1: Infinity,
2539 y1: Infinity,
2540 x2: -Infinity,
2541 y2: -Infinity,
2542 w: 0,
2543 h: 0
2544 };
2545 } else if (bb.x1 != null && bb.y1 != null) {
2546 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2547 return {
2548 x1: bb.x1,
2549 y1: bb.y1,
2550 x2: bb.x2,
2551 y2: bb.y2,
2552 w: bb.x2 - bb.x1,
2553 h: bb.y2 - bb.y1
2554 };
2555 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2556 return {
2557 x1: bb.x1,
2558 y1: bb.y1,
2559 x2: bb.x1 + bb.w,
2560 y2: bb.y1 + bb.h,
2561 w: bb.w,
2562 h: bb.h
2563 };
2564 }
2565 }
2566};
2567var copyBoundingBox = function copyBoundingBox(bb) {
2568 return {
2569 x1: bb.x1,
2570 x2: bb.x2,
2571 w: bb.w,
2572 y1: bb.y1,
2573 y2: bb.y2,
2574 h: bb.h
2575 };
2576};
2577var clearBoundingBox = function clearBoundingBox(bb) {
2578 bb.x1 = Infinity;
2579 bb.y1 = Infinity;
2580 bb.x2 = -Infinity;
2581 bb.y2 = -Infinity;
2582 bb.w = 0;
2583 bb.h = 0;
2584};
2585var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2586 // update bb1 with bb2 bounds
2587 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2588 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2589 bb1.w = bb1.x2 - bb1.x1;
2590 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2591 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2592 bb1.h = bb1.y2 - bb1.y1;
2593};
2594var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2595 bb.x1 = Math.min(bb.x1, x);
2596 bb.x2 = Math.max(bb.x2, x);
2597 bb.w = bb.x2 - bb.x1;
2598 bb.y1 = Math.min(bb.y1, y);
2599 bb.y2 = Math.max(bb.y2, y);
2600 bb.h = bb.y2 - bb.y1;
2601};
2602var expandBoundingBox = function expandBoundingBox(bb) {
2603 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2604 bb.x1 -= padding;
2605 bb.x2 += padding;
2606 bb.y1 -= padding;
2607 bb.y2 += padding;
2608 bb.w = bb.x2 - bb.x1;
2609 bb.h = bb.y2 - bb.y1;
2610 return bb;
2611};
2612var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2613 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2614 var top, right, bottom, left;
2615
2616 if (padding.length === 1) {
2617 top = right = bottom = left = padding[0];
2618 } else if (padding.length === 2) {
2619 top = bottom = padding[0];
2620 left = right = padding[1];
2621 } else if (padding.length === 4) {
2622 var _padding = _slicedToArray(padding, 4);
2623
2624 top = _padding[0];
2625 right = _padding[1];
2626 bottom = _padding[2];
2627 left = _padding[3];
2628 }
2629
2630 bb.x1 -= left;
2631 bb.x2 += right;
2632 bb.y1 -= top;
2633 bb.y2 += bottom;
2634 bb.w = bb.x2 - bb.x1;
2635 bb.h = bb.y2 - bb.y1;
2636 return bb;
2637};
2638
2639var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2640 bb1.x1 = bb2.x1;
2641 bb1.y1 = bb2.y1;
2642 bb1.x2 = bb2.x2;
2643 bb1.y2 = bb2.y2;
2644 bb1.w = bb1.x2 - bb1.x1;
2645 bb1.h = bb1.y2 - bb1.y1;
2646};
2647var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2648 // case: one bb to right of other
2649 if (bb1.x1 > bb2.x2) {
2650 return false;
2651 }
2652
2653 if (bb2.x1 > bb1.x2) {
2654 return false;
2655 } // case: one bb to left of other
2656
2657
2658 if (bb1.x2 < bb2.x1) {
2659 return false;
2660 }
2661
2662 if (bb2.x2 < bb1.x1) {
2663 return false;
2664 } // case: one bb above other
2665
2666
2667 if (bb1.y2 < bb2.y1) {
2668 return false;
2669 }
2670
2671 if (bb2.y2 < bb1.y1) {
2672 return false;
2673 } // case: one bb below other
2674
2675
2676 if (bb1.y1 > bb2.y2) {
2677 return false;
2678 }
2679
2680 if (bb2.y1 > bb1.y2) {
2681 return false;
2682 } // otherwise, must have some overlap
2683
2684
2685 return true;
2686};
2687var inBoundingBox = function inBoundingBox(bb, x, y) {
2688 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2689};
2690var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2691 return inBoundingBox(bb, pt.x, pt.y);
2692};
2693var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2694 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2695};
2696var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2697 var cornerRadius = getRoundRectangleRadius(width, height);
2698 var halfWidth = width / 2;
2699 var halfHeight = height / 2; // Check intersections with straight line segments
2700
2701 var straightLineIntersections; // Top segment, left to right
2702
2703 {
2704 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2705 var topStartY = nodeY - halfHeight - padding;
2706 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2707 var topEndY = topStartY;
2708 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2709
2710 if (straightLineIntersections.length > 0) {
2711 return straightLineIntersections;
2712 }
2713 } // Right segment, top to bottom
2714
2715 {
2716 var rightStartX = nodeX + halfWidth + padding;
2717 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2718 var rightEndX = rightStartX;
2719 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2720 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2721
2722 if (straightLineIntersections.length > 0) {
2723 return straightLineIntersections;
2724 }
2725 } // Bottom segment, left to right
2726
2727 {
2728 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2729 var bottomStartY = nodeY + halfHeight + padding;
2730 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2731 var bottomEndY = bottomStartY;
2732 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2733
2734 if (straightLineIntersections.length > 0) {
2735 return straightLineIntersections;
2736 }
2737 } // Left segment, top to bottom
2738
2739 {
2740 var leftStartX = nodeX - halfWidth - padding;
2741 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2742 var leftEndX = leftStartX;
2743 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2744 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2745
2746 if (straightLineIntersections.length > 0) {
2747 return straightLineIntersections;
2748 }
2749 } // Check intersections with arc segments
2750
2751 var arcIntersections; // Top Left
2752
2753 {
2754 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2755 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2756 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2757
2758 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2759 return [arcIntersections[0], arcIntersections[1]];
2760 }
2761 } // Top Right
2762
2763 {
2764 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2765 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2766 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2767
2768 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2769 return [arcIntersections[0], arcIntersections[1]];
2770 }
2771 } // Bottom Right
2772
2773 {
2774 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2775 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2776 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2777
2778 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2779 return [arcIntersections[0], arcIntersections[1]];
2780 }
2781 } // Bottom Left
2782
2783 {
2784 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2785 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2786 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2787
2788 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2789 return [arcIntersections[0], arcIntersections[1]];
2790 }
2791 }
2792 return []; // if nothing
2793};
2794var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2795 var t = tolerance;
2796 var x1 = Math.min(lx1, lx2);
2797 var x2 = Math.max(lx1, lx2);
2798 var y1 = Math.min(ly1, ly2);
2799 var y2 = Math.max(ly1, ly2);
2800 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2801};
2802var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2803 var bb = {
2804 x1: Math.min(x1, x3, x2) - tolerance,
2805 x2: Math.max(x1, x3, x2) + tolerance,
2806 y1: Math.min(y1, y3, y2) - tolerance,
2807 y2: Math.max(y1, y3, y2) + tolerance
2808 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2809
2810 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2811 // console.log('bezier out of rough bb')
2812 return false;
2813 } else {
2814 // console.log('do more expensive check');
2815 return true;
2816 }
2817};
2818var solveQuadratic = function solveQuadratic(a, b, c, val) {
2819 c -= val;
2820 var r = b * b - 4 * a * c;
2821
2822 if (r < 0) {
2823 return [];
2824 }
2825
2826 var sqrtR = Math.sqrt(r);
2827 var denom = 2 * a;
2828 var root1 = (-b + sqrtR) / denom;
2829 var root2 = (-b - sqrtR) / denom;
2830 return [root1, root2];
2831};
2832var solveCubic = function solveCubic(a, b, c, d, result) {
2833 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2834 // r is the real component, i is the imaginary component
2835 // An implementation of the Cardano method from the year 1545
2836 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2837 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2838
2839 if (a === 0) {
2840 a = epsilon;
2841 }
2842
2843 b /= a;
2844 c /= a;
2845 d /= a;
2846 var discriminant, q, r, dum1, s, t, term1, r13;
2847 q = (3.0 * c - b * b) / 9.0;
2848 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2849 r /= 54.0;
2850 discriminant = q * q * q + r * r;
2851 result[1] = 0;
2852 term1 = b / 3.0;
2853
2854 if (discriminant > 0) {
2855 s = r + Math.sqrt(discriminant);
2856 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2857 t = r - Math.sqrt(discriminant);
2858 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2859 result[0] = -term1 + s + t;
2860 term1 += (s + t) / 2.0;
2861 result[4] = result[2] = -term1;
2862 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2863 result[3] = term1;
2864 result[5] = -term1;
2865 return;
2866 }
2867
2868 result[5] = result[3] = 0;
2869
2870 if (discriminant === 0) {
2871 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2872 result[0] = -term1 + 2.0 * r13;
2873 result[4] = result[2] = -(r13 + term1);
2874 return;
2875 }
2876
2877 q = -q;
2878 dum1 = q * q * q;
2879 dum1 = Math.acos(r / Math.sqrt(dum1));
2880 r13 = 2.0 * Math.sqrt(q);
2881 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2882 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2883 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2884 return;
2885};
2886var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2887 // Find minimum distance by using the minimum of the distance
2888 // function between the given point and the curve
2889 // This gives the coefficients of the resulting cubic equation
2890 // whose roots tell us where a possible minimum is
2891 // (Coefficients are divided by 4)
2892 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;
2893 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;
2894 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;
2895 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);
2896
2897 var roots = []; // Use the cubic solving algorithm
2898
2899 solveCubic(a, b, c, d, roots);
2900 var zeroThreshold = 0.0000001;
2901 var params = [];
2902
2903 for (var index = 0; index < 6; index += 2) {
2904 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2905 params.push(roots[index]);
2906 }
2907 }
2908
2909 params.push(1.0);
2910 params.push(0.0);
2911 var minDistanceSquared = -1;
2912 var curX, curY, distSquared;
2913
2914 for (var i = 0; i < params.length; i++) {
2915 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2916 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2917 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2918
2919 if (minDistanceSquared >= 0) {
2920 if (distSquared < minDistanceSquared) {
2921 minDistanceSquared = distSquared;
2922 }
2923 } else {
2924 minDistanceSquared = distSquared;
2925 }
2926 }
2927
2928 return minDistanceSquared;
2929};
2930var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2931 var offset = [x - x1, y - y1];
2932 var line = [x2 - x1, y2 - y1];
2933 var lineSq = line[0] * line[0] + line[1] * line[1];
2934 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2935 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2936 var adjSq = dotProduct * dotProduct / lineSq;
2937
2938 if (dotProduct < 0) {
2939 return hypSq;
2940 }
2941
2942 if (adjSq > lineSq) {
2943 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2944 }
2945
2946 return hypSq - adjSq;
2947};
2948var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2949 var x1, y1, x2, y2;
2950 var y3; // Intersect with vertical line through (x, y)
2951
2952 var up = 0; // let down = 0;
2953
2954 for (var i = 0; i < points.length / 2; i++) {
2955 x1 = points[i * 2];
2956 y1 = points[i * 2 + 1];
2957
2958 if (i + 1 < points.length / 2) {
2959 x2 = points[(i + 1) * 2];
2960 y2 = points[(i + 1) * 2 + 1];
2961 } else {
2962 x2 = points[(i + 1 - points.length / 2) * 2];
2963 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2964 }
2965
2966 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2967 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2968
2969 if (y3 > y) {
2970 up++;
2971 } // if( y3 < y ){
2972 // down++;
2973 // }
2974
2975 } else {
2976 continue;
2977 }
2978 }
2979
2980 if (up % 2 === 0) {
2981 return false;
2982 } else {
2983 return true;
2984 }
2985};
2986var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2987 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2988
2989 var angle;
2990
2991 if (direction[0] != null) {
2992 angle = Math.atan(direction[1] / direction[0]);
2993
2994 if (direction[0] < 0) {
2995 angle = angle + Math.PI / 2;
2996 } else {
2997 angle = -angle - Math.PI / 2;
2998 }
2999 } else {
3000 angle = direction;
3001 }
3002
3003 var cos = Math.cos(-angle);
3004 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
3005
3006 for (var i = 0; i < transformedPoints.length / 2; i++) {
3007 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
3008 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
3009 transformedPoints[i * 2] += centerX;
3010 transformedPoints[i * 2 + 1] += centerY;
3011 }
3012
3013 var points;
3014
3015 if (padding > 0) {
3016 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3017 points = joinLines(expandedLineSet);
3018 } else {
3019 points = transformedPoints;
3020 }
3021
3022 return pointInsidePolygonPoints(x, y, points);
3023};
3024var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
3025 var cutPolygonPoints = new Array(basePoints.length);
3026 var halfW = width / 2;
3027 var halfH = height / 2;
3028 var cornerRadius = getRoundPolygonRadius(width, height);
3029 var squaredCornerRadius = cornerRadius * cornerRadius;
3030
3031 for (var i = 0; i < basePoints.length / 4; i++) {
3032 var sourceUv = void 0,
3033 destUv = void 0;
3034
3035 if (i === 0) {
3036 sourceUv = basePoints.length - 2;
3037 } else {
3038 sourceUv = i * 4 - 2;
3039 }
3040
3041 destUv = i * 4 + 2;
3042 var px = centerX + halfW * basePoints[i * 4];
3043 var py = centerY + halfH * basePoints[i * 4 + 1];
3044 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3045 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3046 var cp0x = px - offset * basePoints[sourceUv];
3047 var cp0y = py - offset * basePoints[sourceUv + 1];
3048 var cp1x = px + offset * basePoints[destUv];
3049 var cp1y = py + offset * basePoints[destUv + 1];
3050 cutPolygonPoints[i * 4] = cp0x;
3051 cutPolygonPoints[i * 4 + 1] = cp0y;
3052 cutPolygonPoints[i * 4 + 2] = cp1x;
3053 cutPolygonPoints[i * 4 + 3] = cp1y;
3054 var orthx = basePoints[sourceUv + 1];
3055 var orthy = -basePoints[sourceUv];
3056 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3057
3058 if (cosAlpha < 0) {
3059 orthx *= -1;
3060 orthy *= -1;
3061 }
3062
3063 var cx = cp0x + orthx * cornerRadius;
3064 var cy = cp0y + orthy * cornerRadius;
3065 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
3066
3067 if (squaredDistance <= squaredCornerRadius) {
3068 return true;
3069 }
3070 }
3071
3072 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3073};
3074var joinLines = function joinLines(lineSet) {
3075 var vertices = new Array(lineSet.length / 2);
3076 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3077 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3078
3079 for (var i = 0; i < lineSet.length / 4; i++) {
3080 currentLineStartX = lineSet[i * 4];
3081 currentLineStartY = lineSet[i * 4 + 1];
3082 currentLineEndX = lineSet[i * 4 + 2];
3083 currentLineEndY = lineSet[i * 4 + 3];
3084
3085 if (i < lineSet.length / 4 - 1) {
3086 nextLineStartX = lineSet[(i + 1) * 4];
3087 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3088 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3089 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3090 } else {
3091 nextLineStartX = lineSet[0];
3092 nextLineStartY = lineSet[1];
3093 nextLineEndX = lineSet[2];
3094 nextLineEndY = lineSet[3];
3095 }
3096
3097 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3098 vertices[i * 2] = intersection[0];
3099 vertices[i * 2 + 1] = intersection[1];
3100 }
3101
3102 return vertices;
3103};
3104var expandPolygon = function expandPolygon(points, pad) {
3105 var expandedLineSet = new Array(points.length * 2);
3106 var currentPointX, currentPointY, nextPointX, nextPointY;
3107
3108 for (var i = 0; i < points.length / 2; i++) {
3109 currentPointX = points[i * 2];
3110 currentPointY = points[i * 2 + 1];
3111
3112 if (i < points.length / 2 - 1) {
3113 nextPointX = points[(i + 1) * 2];
3114 nextPointY = points[(i + 1) * 2 + 1];
3115 } else {
3116 nextPointX = points[0];
3117 nextPointY = points[1];
3118 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3119 // Assume CCW polygon winding
3120
3121
3122 var offsetX = nextPointY - currentPointY;
3123 var offsetY = -(nextPointX - currentPointX); // Normalize
3124
3125 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3126 var normalizedOffsetX = offsetX / offsetLength;
3127 var normalizedOffsetY = offsetY / offsetLength;
3128 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3129 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3130 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3131 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3132 }
3133
3134 return expandedLineSet;
3135};
3136var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3137 var dispX = centerX - x;
3138 var dispY = centerY - y;
3139 dispX /= ellipseWradius;
3140 dispY /= ellipseHradius;
3141 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3142 var newLength = len - 1;
3143
3144 if (newLength < 0) {
3145 return [];
3146 }
3147
3148 var lenProportion = newLength / len;
3149 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3150};
3151var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3152 x -= centerX;
3153 y -= centerY;
3154 x /= width / 2 + padding;
3155 y /= height / 2 + padding;
3156 return x * x + y * y <= 1;
3157}; // Returns intersections of increasing distance from line's start point
3158
3159var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3160 // Calculate d, direction vector of line
3161 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3162
3163 var f = [x1 - centerX, y1 - centerY];
3164 var a = d[0] * d[0] + d[1] * d[1];
3165 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3166 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3167 var discriminant = b * b - 4 * a * c;
3168
3169 if (discriminant < 0) {
3170 return [];
3171 }
3172
3173 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3174 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3175 var tMin = Math.min(t1, t2);
3176 var tMax = Math.max(t1, t2);
3177 var inRangeParams = [];
3178
3179 if (tMin >= 0 && tMin <= 1) {
3180 inRangeParams.push(tMin);
3181 }
3182
3183 if (tMax >= 0 && tMax <= 1) {
3184 inRangeParams.push(tMax);
3185 }
3186
3187 if (inRangeParams.length === 0) {
3188 return [];
3189 }
3190
3191 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3192 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3193
3194 if (inRangeParams.length > 1) {
3195 if (inRangeParams[0] == inRangeParams[1]) {
3196 return [nearIntersectionX, nearIntersectionY];
3197 } else {
3198 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3199 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3200 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3201 }
3202 } else {
3203 return [nearIntersectionX, nearIntersectionY];
3204 }
3205};
3206var midOfThree = function midOfThree(a, b, c) {
3207 if (b <= a && a <= c || c <= a && a <= b) {
3208 return a;
3209 } else if (a <= b && b <= c || c <= b && b <= a) {
3210 return b;
3211 } else {
3212 return c;
3213 }
3214}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3215
3216var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3217 var dx13 = x1 - x3;
3218 var dx21 = x2 - x1;
3219 var dx43 = x4 - x3;
3220 var dy13 = y1 - y3;
3221 var dy21 = y2 - y1;
3222 var dy43 = y4 - y3;
3223 var ua_t = dx43 * dy13 - dy43 * dx13;
3224 var ub_t = dx21 * dy13 - dy21 * dx13;
3225 var u_b = dy43 * dx21 - dx43 * dy21;
3226
3227 if (u_b !== 0) {
3228 var ua = ua_t / u_b;
3229 var ub = ub_t / u_b;
3230 var flptThreshold = 0.001;
3231
3232 var _min = 0 - flptThreshold;
3233
3234 var _max = 1 + flptThreshold;
3235
3236 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3237 return [x1 + ua * dx21, y1 + ua * dy21];
3238 } else {
3239 if (!infiniteLines) {
3240 return [];
3241 } else {
3242 return [x1 + ua * dx21, y1 + ua * dy21];
3243 }
3244 }
3245 } else {
3246 if (ua_t === 0 || ub_t === 0) {
3247 // Parallel, coincident lines. Check if overlap
3248 // Check endpoint of second line
3249 if (midOfThree(x1, x2, x4) === x4) {
3250 return [x4, y4];
3251 } // Check start point of second line
3252
3253
3254 if (midOfThree(x1, x2, x3) === x3) {
3255 return [x3, y3];
3256 } // Endpoint of first line
3257
3258
3259 if (midOfThree(x3, x4, x2) === x2) {
3260 return [x2, y2];
3261 }
3262
3263 return [];
3264 } else {
3265 // Parallel, non-coincident
3266 return [];
3267 }
3268 }
3269}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3270// intersect a node polygon (pts transformed)
3271//
3272// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3273// intersect the points (no transform)
3274
3275var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3276 var intersections = [];
3277 var intersection;
3278 var transformedPoints = new Array(basePoints.length);
3279 var doTransform = true;
3280
3281 if (width == null) {
3282 doTransform = false;
3283 }
3284
3285 var points;
3286
3287 if (doTransform) {
3288 for (var i = 0; i < transformedPoints.length / 2; i++) {
3289 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3290 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3291 }
3292
3293 if (padding > 0) {
3294 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3295 points = joinLines(expandedLineSet);
3296 } else {
3297 points = transformedPoints;
3298 }
3299 } else {
3300 points = basePoints;
3301 }
3302
3303 var currentX, currentY, nextX, nextY;
3304
3305 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3306 currentX = points[_i2 * 2];
3307 currentY = points[_i2 * 2 + 1];
3308
3309 if (_i2 < points.length / 2 - 1) {
3310 nextX = points[(_i2 + 1) * 2];
3311 nextY = points[(_i2 + 1) * 2 + 1];
3312 } else {
3313 nextX = points[0];
3314 nextY = points[1];
3315 }
3316
3317 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3318
3319 if (intersection.length !== 0) {
3320 intersections.push(intersection[0], intersection[1]);
3321 }
3322 }
3323
3324 return intersections;
3325};
3326var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3327 var intersections = [];
3328 var intersection;
3329 var lines = new Array(basePoints.length);
3330 var halfW = width / 2;
3331 var halfH = height / 2;
3332 var cornerRadius = getRoundPolygonRadius(width, height);
3333
3334 for (var i = 0; i < basePoints.length / 4; i++) {
3335 var sourceUv = void 0,
3336 destUv = void 0;
3337
3338 if (i === 0) {
3339 sourceUv = basePoints.length - 2;
3340 } else {
3341 sourceUv = i * 4 - 2;
3342 }
3343
3344 destUv = i * 4 + 2;
3345 var px = centerX + halfW * basePoints[i * 4];
3346 var py = centerY + halfH * basePoints[i * 4 + 1];
3347 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3348 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3349 var cp0x = px - offset * basePoints[sourceUv];
3350 var cp0y = py - offset * basePoints[sourceUv + 1];
3351 var cp1x = px + offset * basePoints[destUv];
3352 var cp1y = py + offset * basePoints[destUv + 1];
3353
3354 if (i === 0) {
3355 lines[basePoints.length - 2] = cp0x;
3356 lines[basePoints.length - 1] = cp0y;
3357 } else {
3358 lines[i * 4 - 2] = cp0x;
3359 lines[i * 4 - 1] = cp0y;
3360 }
3361
3362 lines[i * 4] = cp1x;
3363 lines[i * 4 + 1] = cp1y;
3364 var orthx = basePoints[sourceUv + 1];
3365 var orthy = -basePoints[sourceUv];
3366 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3367
3368 if (cosAlpha < 0) {
3369 orthx *= -1;
3370 orthy *= -1;
3371 }
3372
3373 var cx = cp0x + orthx * cornerRadius;
3374 var cy = cp0y + orthy * cornerRadius;
3375 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3376
3377 if (intersection.length !== 0) {
3378 intersections.push(intersection[0], intersection[1]);
3379 }
3380 }
3381
3382 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3383 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3384
3385 if (intersection.length !== 0) {
3386 intersections.push(intersection[0], intersection[1]);
3387 }
3388 }
3389
3390 if (intersections.length > 2) {
3391 var lowestIntersection = [intersections[0], intersections[1]];
3392 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3393
3394 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3395 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3396
3397 if (squaredDistance <= lowestSquaredDistance) {
3398 lowestIntersection[0] = intersections[_i4 * 2];
3399 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3400 lowestSquaredDistance = squaredDistance;
3401 }
3402 }
3403
3404 return lowestIntersection;
3405 }
3406
3407 return intersections;
3408};
3409var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3410 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3411 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3412 var lenRatio = (length - amount) / length;
3413
3414 if (lenRatio < 0) {
3415 lenRatio = 0.00001;
3416 }
3417
3418 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3419};
3420var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3421 var points = generateUnitNgonPoints(sides, rotationRadians);
3422 points = fitPolygonToSquare(points);
3423 return points;
3424};
3425var fitPolygonToSquare = function fitPolygonToSquare(points) {
3426 var x, y;
3427 var sides = points.length / 2;
3428 var minX = Infinity,
3429 minY = Infinity,
3430 maxX = -Infinity,
3431 maxY = -Infinity;
3432
3433 for (var i = 0; i < sides; i++) {
3434 x = points[2 * i];
3435 y = points[2 * i + 1];
3436 minX = Math.min(minX, x);
3437 maxX = Math.max(maxX, x);
3438 minY = Math.min(minY, y);
3439 maxY = Math.max(maxY, y);
3440 } // stretch factors
3441
3442
3443 var sx = 2 / (maxX - minX);
3444 var sy = 2 / (maxY - minY);
3445
3446 for (var _i5 = 0; _i5 < sides; _i5++) {
3447 x = points[2 * _i5] = points[2 * _i5] * sx;
3448 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3449 minX = Math.min(minX, x);
3450 maxX = Math.max(maxX, x);
3451 minY = Math.min(minY, y);
3452 maxY = Math.max(maxY, y);
3453 }
3454
3455 if (minY < -1) {
3456 for (var _i6 = 0; _i6 < sides; _i6++) {
3457 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3458 }
3459 }
3460
3461 return points;
3462};
3463var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3464 var increment = 1.0 / sides * 2 * Math.PI;
3465 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3466 startAngle += rotationRadians;
3467 var points = new Array(sides * 2);
3468 var currentAngle;
3469
3470 for (var i = 0; i < sides; i++) {
3471 currentAngle = i * increment + startAngle;
3472 points[2 * i] = Math.cos(currentAngle); // x
3473
3474 points[2 * i + 1] = Math.sin(-currentAngle); // y
3475 }
3476
3477 return points;
3478}; // Set the default radius, unless half of width or height is smaller than default
3479
3480var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3481 return Math.min(width / 4, height / 4, 8);
3482}; // Set the default radius
3483
3484var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3485 return Math.min(width / 10, height / 10, 8);
3486};
3487var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3488 return 8;
3489};
3490var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3491 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3492}; // get curve width, height, and control point position offsets as a percentage of node height / width
3493
3494var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3495 return {
3496 heightOffset: Math.min(15, 0.05 * height),
3497 widthOffset: Math.min(100, 0.25 * width),
3498 ctrlPtOffsetPct: 0.05
3499 };
3500};
3501
3502var pageRankDefaults = defaults$g({
3503 dampingFactor: 0.8,
3504 precision: 0.000001,
3505 iterations: 200,
3506 weight: function weight(edge) {
3507 return 1;
3508 }
3509});
3510var elesfn$o = {
3511 pageRank: function pageRank(options) {
3512 var _pageRankDefaults = pageRankDefaults(options),
3513 dampingFactor = _pageRankDefaults.dampingFactor,
3514 precision = _pageRankDefaults.precision,
3515 iterations = _pageRankDefaults.iterations,
3516 weight = _pageRankDefaults.weight;
3517
3518 var cy = this._private.cy;
3519
3520 var _this$byGroup = this.byGroup(),
3521 nodes = _this$byGroup.nodes,
3522 edges = _this$byGroup.edges;
3523
3524 var numNodes = nodes.length;
3525 var numNodesSqd = numNodes * numNodes;
3526 var numEdges = edges.length; // Construct transposed adjacency matrix
3527 // First lets have a zeroed matrix of the right size
3528 // We'll also keep track of the sum of each column
3529
3530 var matrix = new Array(numNodesSqd);
3531 var columnSum = new Array(numNodes);
3532 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3533
3534 for (var i = 0; i < numNodes; i++) {
3535 for (var j = 0; j < numNodes; j++) {
3536 var n = i * numNodes + j;
3537 matrix[n] = 0;
3538 }
3539
3540 columnSum[i] = 0;
3541 } // Now, process edges
3542
3543
3544 for (var _i = 0; _i < numEdges; _i++) {
3545 var edge = edges[_i];
3546 var srcId = edge.data('source');
3547 var tgtId = edge.data('target'); // Don't include loops in the matrix
3548
3549 if (srcId === tgtId) {
3550 continue;
3551 }
3552
3553 var s = nodes.indexOfId(srcId);
3554 var t = nodes.indexOfId(tgtId);
3555 var w = weight(edge);
3556
3557 var _n = t * numNodes + s; // Update matrix
3558
3559
3560 matrix[_n] += w; // Update column sum
3561
3562 columnSum[s] += w;
3563 } // Add additional probability based on damping factor
3564 // Also, take into account columns that have sum = 0
3565
3566
3567 var p = 1.0 / numNodes + additionalProb; // Shorthand
3568 // Traverse matrix, column by column
3569
3570 for (var _j = 0; _j < numNodes; _j++) {
3571 if (columnSum[_j] === 0) {
3572 // No 'links' out from node jth, assume equal probability for each possible node
3573 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3574 var _n2 = _i2 * numNodes + _j;
3575
3576 matrix[_n2] = p;
3577 }
3578 } else {
3579 // Node jth has outgoing link, compute normalized probabilities
3580 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3581 var _n3 = _i3 * numNodes + _j;
3582
3583 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3584 }
3585 }
3586 } // Compute dominant eigenvector using power method
3587
3588
3589 var eigenvector = new Array(numNodes);
3590 var temp = new Array(numNodes);
3591 var previous; // Start with a vector of all 1's
3592 // Also, initialize a null vector which will be used as shorthand
3593
3594 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3595 eigenvector[_i4] = 1;
3596 }
3597
3598 for (var iter = 0; iter < iterations; iter++) {
3599 // Temp array with all 0's
3600 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3601 temp[_i5] = 0;
3602 } // Multiply matrix with previous result
3603
3604
3605 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3606 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3607 var _n4 = _i6 * numNodes + _j2;
3608
3609 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3610 }
3611 }
3612
3613 inPlaceSumNormalize(temp);
3614 previous = eigenvector;
3615 eigenvector = temp;
3616 temp = previous;
3617 var diff = 0; // Compute difference (squared module) of both vectors
3618
3619 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3620 var delta = previous[_i7] - eigenvector[_i7];
3621 diff += delta * delta;
3622 } // If difference is less than the desired threshold, stop iterating
3623
3624
3625 if (diff < precision) {
3626 break;
3627 }
3628 } // Construct result
3629
3630
3631 var res = {
3632 rank: function rank(node) {
3633 node = cy.collection(node)[0];
3634 return eigenvector[nodes.indexOf(node)];
3635 }
3636 };
3637 return res;
3638 } // pageRank
3639
3640}; // elesfn
3641
3642var defaults$f = defaults$g({
3643 root: null,
3644 weight: function weight(edge) {
3645 return 1;
3646 },
3647 directed: false,
3648 alpha: 0
3649});
3650var elesfn$n = {
3651 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3652 options = defaults$f(options);
3653 var cy = this.cy();
3654 var nodes = this.nodes();
3655 var numNodes = nodes.length;
3656
3657 if (!options.directed) {
3658 var degrees = {};
3659 var maxDegree = 0;
3660
3661 for (var i = 0; i < numNodes; i++) {
3662 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3663
3664 options.root = node;
3665 var currDegree = this.degreeCentrality(options);
3666
3667 if (maxDegree < currDegree.degree) {
3668 maxDegree = currDegree.degree;
3669 }
3670
3671 degrees[node.id()] = currDegree.degree;
3672 }
3673
3674 return {
3675 degree: function degree(node) {
3676 if (maxDegree === 0) {
3677 return 0;
3678 }
3679
3680 if (string(node)) {
3681 // from is a selector string
3682 node = cy.filter(node);
3683 }
3684
3685 return degrees[node.id()] / maxDegree;
3686 }
3687 };
3688 } else {
3689 var indegrees = {};
3690 var outdegrees = {};
3691 var maxIndegree = 0;
3692 var maxOutdegree = 0;
3693
3694 for (var _i = 0; _i < numNodes; _i++) {
3695 var _node = nodes[_i];
3696
3697 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3698
3699
3700 options.root = _node;
3701
3702 var _currDegree = this.degreeCentrality(options);
3703
3704 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3705 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3706 indegrees[id] = _currDegree.indegree;
3707 outdegrees[id] = _currDegree.outdegree;
3708 }
3709
3710 return {
3711 indegree: function indegree(node) {
3712 if (maxIndegree == 0) {
3713 return 0;
3714 }
3715
3716 if (string(node)) {
3717 // from is a selector string
3718 node = cy.filter(node);
3719 }
3720
3721 return indegrees[node.id()] / maxIndegree;
3722 },
3723 outdegree: function outdegree(node) {
3724 if (maxOutdegree === 0) {
3725 return 0;
3726 }
3727
3728 if (string(node)) {
3729 // from is a selector string
3730 node = cy.filter(node);
3731 }
3732
3733 return outdegrees[node.id()] / maxOutdegree;
3734 }
3735 };
3736 }
3737 },
3738 // degreeCentralityNormalized
3739 // Implemented from the algorithm in Opsahl's paper
3740 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3741 // check the heading 2 "Degree"
3742 degreeCentrality: function degreeCentrality(options) {
3743 options = defaults$f(options);
3744 var cy = this.cy();
3745 var callingEles = this;
3746 var _options = options,
3747 root = _options.root,
3748 weight = _options.weight,
3749 directed = _options.directed,
3750 alpha = _options.alpha;
3751 root = cy.collection(root)[0];
3752
3753 if (!directed) {
3754 var connEdges = root.connectedEdges().intersection(callingEles);
3755 var k = connEdges.length;
3756 var s = 0; // Now, sum edge weights
3757
3758 for (var i = 0; i < connEdges.length; i++) {
3759 s += weight(connEdges[i]);
3760 }
3761
3762 return {
3763 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3764 };
3765 } else {
3766 var edges = root.connectedEdges();
3767 var incoming = edges.filter(function (edge) {
3768 return edge.target().same(root) && callingEles.has(edge);
3769 });
3770 var outgoing = edges.filter(function (edge) {
3771 return edge.source().same(root) && callingEles.has(edge);
3772 });
3773 var k_in = incoming.length;
3774 var k_out = outgoing.length;
3775 var s_in = 0;
3776 var s_out = 0; // Now, sum incoming edge weights
3777
3778 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3779 s_in += weight(incoming[_i2]);
3780 } // Now, sum outgoing edge weights
3781
3782
3783 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3784 s_out += weight(outgoing[_i3]);
3785 }
3786
3787 return {
3788 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3789 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3790 };
3791 }
3792 } // degreeCentrality
3793
3794}; // elesfn
3795// nice, short mathematical alias
3796
3797elesfn$n.dc = elesfn$n.degreeCentrality;
3798elesfn$n.dcn = elesfn$n.degreeCentralityNormalised = elesfn$n.degreeCentralityNormalized;
3799
3800var defaults$e = defaults$g({
3801 harmonic: true,
3802 weight: function weight() {
3803 return 1;
3804 },
3805 directed: false,
3806 root: null
3807});
3808var elesfn$m = {
3809 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3810 var _defaults = defaults$e(options),
3811 harmonic = _defaults.harmonic,
3812 weight = _defaults.weight,
3813 directed = _defaults.directed;
3814
3815 var cy = this.cy();
3816 var closenesses = {};
3817 var maxCloseness = 0;
3818 var nodes = this.nodes();
3819 var fw = this.floydWarshall({
3820 weight: weight,
3821 directed: directed
3822 }); // Compute closeness for every node and find the maximum closeness
3823
3824 for (var i = 0; i < nodes.length; i++) {
3825 var currCloseness = 0;
3826 var node_i = nodes[i];
3827
3828 for (var j = 0; j < nodes.length; j++) {
3829 if (i !== j) {
3830 var d = fw.distance(node_i, nodes[j]);
3831
3832 if (harmonic) {
3833 currCloseness += 1 / d;
3834 } else {
3835 currCloseness += d;
3836 }
3837 }
3838 }
3839
3840 if (!harmonic) {
3841 currCloseness = 1 / currCloseness;
3842 }
3843
3844 if (maxCloseness < currCloseness) {
3845 maxCloseness = currCloseness;
3846 }
3847
3848 closenesses[node_i.id()] = currCloseness;
3849 }
3850
3851 return {
3852 closeness: function closeness(node) {
3853 if (maxCloseness == 0) {
3854 return 0;
3855 }
3856
3857 if (string(node)) {
3858 // from is a selector string
3859 node = cy.filter(node)[0].id();
3860 } else {
3861 // from is a node
3862 node = node.id();
3863 }
3864
3865 return closenesses[node] / maxCloseness;
3866 }
3867 };
3868 },
3869 // Implemented from pseudocode from wikipedia
3870 closenessCentrality: function closenessCentrality(options) {
3871 var _defaults2 = defaults$e(options),
3872 root = _defaults2.root,
3873 weight = _defaults2.weight,
3874 directed = _defaults2.directed,
3875 harmonic = _defaults2.harmonic;
3876
3877 root = this.filter(root)[0]; // we need distance from this node to every other node
3878
3879 var dijkstra = this.dijkstra({
3880 root: root,
3881 weight: weight,
3882 directed: directed
3883 });
3884 var totalDistance = 0;
3885 var nodes = this.nodes();
3886
3887 for (var i = 0; i < nodes.length; i++) {
3888 var n = nodes[i];
3889
3890 if (!n.same(root)) {
3891 var d = dijkstra.distanceTo(n);
3892
3893 if (harmonic) {
3894 totalDistance += 1 / d;
3895 } else {
3896 totalDistance += d;
3897 }
3898 }
3899 }
3900
3901 return harmonic ? totalDistance : 1 / totalDistance;
3902 } // closenessCentrality
3903
3904}; // elesfn
3905// nice, short mathematical alias
3906
3907elesfn$m.cc = elesfn$m.closenessCentrality;
3908elesfn$m.ccn = elesfn$m.closenessCentralityNormalised = elesfn$m.closenessCentralityNormalized;
3909
3910var defaults$d = defaults$g({
3911 weight: null,
3912 directed: false
3913});
3914var elesfn$l = {
3915 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3916 betweennessCentrality: function betweennessCentrality(options) {
3917 var _defaults = defaults$d(options),
3918 directed = _defaults.directed,
3919 weight = _defaults.weight;
3920
3921 var weighted = weight != null;
3922 var cy = this.cy(); // starting
3923
3924 var V = this.nodes();
3925 var A = {};
3926 var _C = {};
3927 var max = 0;
3928 var C = {
3929 set: function set(key, val) {
3930 _C[key] = val;
3931
3932 if (val > max) {
3933 max = val;
3934 }
3935 },
3936 get: function get(key) {
3937 return _C[key];
3938 }
3939 }; // A contains the neighborhoods of every node
3940
3941 for (var i = 0; i < V.length; i++) {
3942 var v = V[i];
3943 var vid = v.id();
3944
3945 if (directed) {
3946 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3947 } else {
3948 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3949 }
3950
3951 C.set(vid, 0);
3952 }
3953
3954 var _loop = function _loop(s) {
3955 var sid = V[s].id();
3956 var S = []; // stack
3957
3958 var P = {};
3959 var g = {};
3960 var d = {};
3961 var Q = new Heap__default["default"](function (a, b) {
3962 return d[a] - d[b];
3963 }); // queue
3964 // init dictionaries
3965
3966 for (var _i = 0; _i < V.length; _i++) {
3967 var _vid = V[_i].id();
3968
3969 P[_vid] = [];
3970 g[_vid] = 0;
3971 d[_vid] = Infinity;
3972 }
3973
3974 g[sid] = 1; // sigma
3975
3976 d[sid] = 0; // distance to s
3977
3978 Q.push(sid);
3979
3980 while (!Q.empty()) {
3981 var _v = Q.pop();
3982
3983 S.push(_v);
3984
3985 if (weighted) {
3986 for (var j = 0; j < A[_v].length; j++) {
3987 var w = A[_v][j];
3988 var vEle = cy.getElementById(_v);
3989 var edge = void 0;
3990
3991 if (vEle.edgesTo(w).length > 0) {
3992 edge = vEle.edgesTo(w)[0];
3993 } else {
3994 edge = w.edgesTo(vEle)[0];
3995 }
3996
3997 var edgeWeight = weight(edge);
3998 w = w.id();
3999
4000 if (d[w] > d[_v] + edgeWeight) {
4001 d[w] = d[_v] + edgeWeight;
4002
4003 if (Q.nodes.indexOf(w) < 0) {
4004 //if w is not in Q
4005 Q.push(w);
4006 } else {
4007 // update position if w is in Q
4008 Q.updateItem(w);
4009 }
4010
4011 g[w] = 0;
4012 P[w] = [];
4013 }
4014
4015 if (d[w] == d[_v] + edgeWeight) {
4016 g[w] = g[w] + g[_v];
4017 P[w].push(_v);
4018 }
4019 }
4020 } else {
4021 for (var _j = 0; _j < A[_v].length; _j++) {
4022 var _w = A[_v][_j].id();
4023
4024 if (d[_w] == Infinity) {
4025 Q.push(_w);
4026 d[_w] = d[_v] + 1;
4027 }
4028
4029 if (d[_w] == d[_v] + 1) {
4030 g[_w] = g[_w] + g[_v];
4031
4032 P[_w].push(_v);
4033 }
4034 }
4035 }
4036 }
4037
4038 var e = {};
4039
4040 for (var _i2 = 0; _i2 < V.length; _i2++) {
4041 e[V[_i2].id()] = 0;
4042 }
4043
4044 while (S.length > 0) {
4045 var _w2 = S.pop();
4046
4047 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4048 var _v2 = P[_w2][_j2];
4049 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4050 }
4051
4052 if (_w2 != V[s].id()) {
4053 C.set(_w2, C.get(_w2) + e[_w2]);
4054 }
4055 }
4056 };
4057
4058 for (var s = 0; s < V.length; s++) {
4059 _loop(s);
4060 }
4061
4062 var ret = {
4063 betweenness: function betweenness(node) {
4064 var id = cy.collection(node).id();
4065 return C.get(id);
4066 },
4067 betweennessNormalized: function betweennessNormalized(node) {
4068 if (max == 0) {
4069 return 0;
4070 }
4071
4072 var id = cy.collection(node).id();
4073 return C.get(id) / max;
4074 }
4075 }; // alias
4076
4077 ret.betweennessNormalised = ret.betweennessNormalized;
4078 return ret;
4079 } // betweennessCentrality
4080
4081}; // elesfn
4082// nice, short mathematical alias
4083
4084elesfn$l.bc = elesfn$l.betweennessCentrality;
4085
4086// Implemented by Zoe Xi @zoexi for GSOC 2016
4087/* eslint-disable no-unused-vars */
4088
4089var defaults$c = defaults$g({
4090 expandFactor: 2,
4091 // affects time of computation and cluster granularity to some extent: M * M
4092 inflateFactor: 2,
4093 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4094 multFactor: 1,
4095 // optional self loops for each node. Use a neutral value to improve cluster computations.
4096 maxIterations: 20,
4097 // maximum number of iterations of the MCL algorithm in a single run
4098 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4099 function (edge) {
4100 return 1;
4101 }]
4102});
4103/* eslint-enable */
4104
4105var setOptions$3 = function setOptions(options) {
4106 return defaults$c(options);
4107};
4108/* eslint-enable */
4109
4110
4111var getSimilarity$1 = function getSimilarity(edge, attributes) {
4112 var total = 0;
4113
4114 for (var i = 0; i < attributes.length; i++) {
4115 total += attributes[i](edge);
4116 }
4117
4118 return total;
4119};
4120
4121var addLoops = function addLoops(M, n, val) {
4122 for (var i = 0; i < n; i++) {
4123 M[i * n + i] = val;
4124 }
4125};
4126
4127var normalize = function normalize(M, n) {
4128 var sum;
4129
4130 for (var col = 0; col < n; col++) {
4131 sum = 0;
4132
4133 for (var row = 0; row < n; row++) {
4134 sum += M[row * n + col];
4135 }
4136
4137 for (var _row = 0; _row < n; _row++) {
4138 M[_row * n + col] = M[_row * n + col] / sum;
4139 }
4140 }
4141}; // TODO: blocked matrix multiplication?
4142
4143
4144var mmult = function mmult(A, B, n) {
4145 var C = new Array(n * n);
4146
4147 for (var i = 0; i < n; i++) {
4148 for (var j = 0; j < n; j++) {
4149 C[i * n + j] = 0;
4150 }
4151
4152 for (var k = 0; k < n; k++) {
4153 for (var _j = 0; _j < n; _j++) {
4154 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4155 }
4156 }
4157 }
4158
4159 return C;
4160};
4161
4162var expand = function expand(M, n, expandFactor
4163/** power **/
4164) {
4165 var _M = M.slice(0);
4166
4167 for (var p = 1; p < expandFactor; p++) {
4168 M = mmult(M, _M, n);
4169 }
4170
4171 return M;
4172};
4173
4174var inflate = function inflate(M, n, inflateFactor
4175/** r **/
4176) {
4177 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4178
4179
4180 for (var i = 0; i < n * n; i++) {
4181 _M[i] = Math.pow(M[i], inflateFactor);
4182 }
4183
4184 normalize(_M, n);
4185 return _M;
4186};
4187
4188var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4189 // Check that both matrices have the same elements (i,j)
4190 for (var i = 0; i < n2; i++) {
4191 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4192
4193 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4194
4195 if (v1 !== v2) {
4196 return false;
4197 }
4198 }
4199
4200 return true;
4201};
4202
4203var assign$2 = function assign(M, n, nodes, cy) {
4204 var clusters = [];
4205
4206 for (var i = 0; i < n; i++) {
4207 var cluster = [];
4208
4209 for (var j = 0; j < n; j++) {
4210 // Row-wise attractors and elements that they attract belong in same cluster
4211 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4212 cluster.push(nodes[j]);
4213 }
4214 }
4215
4216 if (cluster.length !== 0) {
4217 clusters.push(cy.collection(cluster));
4218 }
4219 }
4220
4221 return clusters;
4222};
4223
4224var isDuplicate = function isDuplicate(c1, c2) {
4225 for (var i = 0; i < c1.length; i++) {
4226 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4227 return false;
4228 }
4229 }
4230
4231 return true;
4232};
4233
4234var removeDuplicates = function removeDuplicates(clusters) {
4235 for (var i = 0; i < clusters.length; i++) {
4236 for (var j = 0; j < clusters.length; j++) {
4237 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4238 clusters.splice(j, 1);
4239 }
4240 }
4241 }
4242
4243 return clusters;
4244};
4245
4246var markovClustering = function markovClustering(options) {
4247 var nodes = this.nodes();
4248 var edges = this.edges();
4249 var cy = this.cy(); // Set parameters of algorithm:
4250
4251 var opts = setOptions$3(options); // Map each node to its position in node array
4252
4253 var id2position = {};
4254
4255 for (var i = 0; i < nodes.length; i++) {
4256 id2position[nodes[i].id()] = i;
4257 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4258
4259
4260 var n = nodes.length,
4261 n2 = n * n;
4262
4263 var M = new Array(n2),
4264 _M;
4265
4266 for (var _i = 0; _i < n2; _i++) {
4267 M[_i] = 0;
4268 }
4269
4270 for (var e = 0; e < edges.length; e++) {
4271 var edge = edges[e];
4272 var _i2 = id2position[edge.source().id()];
4273 var j = id2position[edge.target().id()];
4274 var sim = getSimilarity$1(edge, opts.attributes);
4275 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4276
4277 M[j * n + _i2] += sim;
4278 } // Begin Markov cluster algorithm
4279 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4280
4281
4282 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4283
4284 normalize(M, n);
4285 var isStillMoving = true;
4286 var iterations = 0;
4287
4288 while (isStillMoving && iterations < opts.maxIterations) {
4289 isStillMoving = false; // Step 3:
4290
4291 _M = expand(M, n, opts.expandFactor); // Step 4:
4292
4293 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4294
4295 if (!hasConverged(M, _M, n2, 4)) {
4296 isStillMoving = true;
4297 }
4298
4299 iterations++;
4300 } // Build clusters from matrix
4301
4302
4303 var clusters = assign$2(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4304
4305 clusters = removeDuplicates(clusters);
4306 return clusters;
4307};
4308
4309var markovClustering$1 = {
4310 markovClustering: markovClustering,
4311 mcl: markovClustering
4312};
4313
4314// Common distance metrics for clustering algorithms
4315
4316var identity = function identity(x) {
4317 return x;
4318};
4319
4320var absDiff = function absDiff(p, q) {
4321 return Math.abs(q - p);
4322};
4323
4324var addAbsDiff = function addAbsDiff(total, p, q) {
4325 return total + absDiff(p, q);
4326};
4327
4328var addSquaredDiff = function addSquaredDiff(total, p, q) {
4329 return total + Math.pow(q - p, 2);
4330};
4331
4332var sqrt = function sqrt(x) {
4333 return Math.sqrt(x);
4334};
4335
4336var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4337 return Math.max(currentMax, absDiff(p, q));
4338};
4339
4340var getDistance = function getDistance(length, getP, getQ, init, visit) {
4341 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4342 var ret = init;
4343 var p, q;
4344
4345 for (var dim = 0; dim < length; dim++) {
4346 p = getP(dim);
4347 q = getQ(dim);
4348 ret = visit(ret, p, q);
4349 }
4350
4351 return post(ret);
4352};
4353
4354var distances = {
4355 euclidean: function euclidean(length, getP, getQ) {
4356 if (length >= 2) {
4357 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4358 } else {
4359 // for single attr case, more efficient to avoid sqrt
4360 return getDistance(length, getP, getQ, 0, addAbsDiff);
4361 }
4362 },
4363 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4364 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4365 },
4366 manhattan: function manhattan(length, getP, getQ) {
4367 return getDistance(length, getP, getQ, 0, addAbsDiff);
4368 },
4369 max: function max(length, getP, getQ) {
4370 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4371 }
4372}; // in case the user accidentally doesn't use camel case
4373
4374distances['squared-euclidean'] = distances['squaredEuclidean'];
4375distances['squaredeuclidean'] = distances['squaredEuclidean'];
4376function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4377 var impl;
4378
4379 if (fn$6(method)) {
4380 impl = method;
4381 } else {
4382 impl = distances[method] || distances.euclidean;
4383 }
4384
4385 if (length === 0 && fn$6(method)) {
4386 return impl(nodeP, nodeQ);
4387 } else {
4388 return impl(length, getP, getQ, nodeP, nodeQ);
4389 }
4390}
4391
4392var defaults$b = defaults$g({
4393 k: 2,
4394 m: 2,
4395 sensitivityThreshold: 0.0001,
4396 distance: 'euclidean',
4397 maxIterations: 10,
4398 attributes: [],
4399 testMode: false,
4400 testCentroids: null
4401});
4402
4403var setOptions$2 = function setOptions(options) {
4404 return defaults$b(options);
4405};
4406/* eslint-enable */
4407
4408
4409var getDist = function getDist(type, node, centroid, attributes, mode) {
4410 var noNodeP = mode !== 'kMedoids';
4411 var getP = noNodeP ? function (i) {
4412 return centroid[i];
4413 } : function (i) {
4414 return attributes[i](centroid);
4415 };
4416
4417 var getQ = function getQ(i) {
4418 return attributes[i](node);
4419 };
4420
4421 var nodeP = centroid;
4422 var nodeQ = node;
4423 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4424};
4425
4426var randomCentroids = function randomCentroids(nodes, k, attributes) {
4427 var ndim = attributes.length;
4428 var min = new Array(ndim);
4429 var max = new Array(ndim);
4430 var centroids = new Array(k);
4431 var centroid = null; // Find min, max values for each attribute dimension
4432
4433 for (var i = 0; i < ndim; i++) {
4434 min[i] = nodes.min(attributes[i]).value;
4435 max[i] = nodes.max(attributes[i]).value;
4436 } // Build k centroids, each represented as an n-dim feature vector
4437
4438
4439 for (var c = 0; c < k; c++) {
4440 centroid = [];
4441
4442 for (var _i = 0; _i < ndim; _i++) {
4443 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4444 }
4445
4446 centroids[c] = centroid;
4447 }
4448
4449 return centroids;
4450};
4451
4452var classify = function classify(node, centroids, distance, attributes, type) {
4453 var min = Infinity;
4454 var index = 0;
4455
4456 for (var i = 0; i < centroids.length; i++) {
4457 var dist = getDist(distance, node, centroids[i], attributes, type);
4458
4459 if (dist < min) {
4460 min = dist;
4461 index = i;
4462 }
4463 }
4464
4465 return index;
4466};
4467
4468var buildCluster = function buildCluster(centroid, nodes, assignment) {
4469 var cluster = [];
4470 var node = null;
4471
4472 for (var n = 0; n < nodes.length; n++) {
4473 node = nodes[n];
4474
4475 if (assignment[node.id()] === centroid) {
4476 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4477 cluster.push(node);
4478 }
4479 }
4480
4481 return cluster;
4482};
4483
4484var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4485 return Math.abs(v2 - v1) <= sensitivityThreshold;
4486};
4487
4488var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4489 for (var i = 0; i < v1.length; i++) {
4490 for (var j = 0; j < v1[i].length; j++) {
4491 var diff = Math.abs(v1[i][j] - v2[i][j]);
4492
4493 if (diff > sensitivityThreshold) {
4494 return false;
4495 }
4496 }
4497 }
4498
4499 return true;
4500};
4501
4502var seenBefore = function seenBefore(node, medoids, n) {
4503 for (var i = 0; i < n; i++) {
4504 if (node === medoids[i]) return true;
4505 }
4506
4507 return false;
4508};
4509
4510var randomMedoids = function randomMedoids(nodes, k) {
4511 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4512 // so we need to check to see if we've already seen or chose this node before.
4513
4514 if (nodes.length < 50) {
4515 // Randomly select k medoids from the n nodes
4516 for (var i = 0; i < k; i++) {
4517 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).
4518 // Instead choose a different random node.
4519
4520 while (seenBefore(node, medoids, i)) {
4521 node = nodes[Math.floor(Math.random() * nodes.length)];
4522 }
4523
4524 medoids[i] = node;
4525 }
4526 } else {
4527 // Relatively large data set, so pretty safe to not check and just select random nodes
4528 for (var _i2 = 0; _i2 < k; _i2++) {
4529 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4530 }
4531 }
4532
4533 return medoids;
4534};
4535
4536var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4537 var cost = 0;
4538
4539 for (var n = 0; n < cluster.length; n++) {
4540 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4541 }
4542
4543 return cost;
4544};
4545
4546var kMeans = function kMeans(options) {
4547 var cy = this.cy();
4548 var nodes = this.nodes();
4549 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4550
4551 var opts = setOptions$2(options); // Begin k-means algorithm
4552
4553 var clusters = new Array(opts.k);
4554 var assignment = {};
4555 var centroids; // Step 1: Initialize centroid positions
4556
4557 if (opts.testMode) {
4558 if (typeof opts.testCentroids === 'number') {
4559 // TODO: implement a seeded random number generator.
4560 opts.testCentroids;
4561 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4562 } else if (_typeof(opts.testCentroids) === 'object') {
4563 centroids = opts.testCentroids;
4564 } else {
4565 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4566 }
4567 } else {
4568 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4569 }
4570
4571 var isStillMoving = true;
4572 var iterations = 0;
4573
4574 while (isStillMoving && iterations < opts.maxIterations) {
4575 // Step 2: Assign nodes to the nearest centroid
4576 for (var n = 0; n < nodes.length; n++) {
4577 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4578
4579 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4580 } // Step 3: For each of the k clusters, update its centroid
4581
4582
4583 isStillMoving = false;
4584
4585 for (var c = 0; c < opts.k; c++) {
4586 // Get all nodes that belong to this cluster
4587 var cluster = buildCluster(c, nodes, assignment);
4588
4589 if (cluster.length === 0) {
4590 // If cluster is empty, break out early & move to next cluster
4591 continue;
4592 } // Update centroids by calculating avg of all nodes within the cluster.
4593
4594
4595 var ndim = opts.attributes.length;
4596 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4597
4598 var newCentroid = new Array(ndim);
4599 var sum = new Array(ndim);
4600
4601 for (var d = 0; d < ndim; d++) {
4602 sum[d] = 0.0;
4603
4604 for (var i = 0; i < cluster.length; i++) {
4605 node = cluster[i];
4606 sum[d] += opts.attributes[d](node);
4607 }
4608
4609 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4610
4611 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4612 isStillMoving = true;
4613 }
4614 }
4615
4616 centroids[c] = newCentroid;
4617 clusters[c] = cy.collection(cluster);
4618 }
4619
4620 iterations++;
4621 }
4622
4623 return clusters;
4624};
4625
4626var kMedoids = function kMedoids(options) {
4627 var cy = this.cy();
4628 var nodes = this.nodes();
4629 var node = null;
4630 var opts = setOptions$2(options); // Begin k-medoids algorithm
4631
4632 var clusters = new Array(opts.k);
4633 var medoids;
4634 var assignment = {};
4635 var curCost;
4636 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4637 // Step 1: Initialize k medoids
4638
4639 if (opts.testMode) {
4640 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4641 medoids = opts.testCentroids;
4642 } else {
4643 medoids = randomMedoids(nodes, opts.k);
4644 }
4645 } else {
4646 medoids = randomMedoids(nodes, opts.k);
4647 }
4648
4649 var isStillMoving = true;
4650 var iterations = 0;
4651
4652 while (isStillMoving && iterations < opts.maxIterations) {
4653 // Step 2: Assign nodes to the nearest medoid
4654 for (var n = 0; n < nodes.length; n++) {
4655 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4656
4657 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4658 }
4659
4660 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4661 // select the node with the lowest configuration cost as new medoid.
4662
4663 for (var m = 0; m < medoids.length; m++) {
4664 // Get all nodes that belong to this medoid
4665 var cluster = buildCluster(m, nodes, assignment);
4666
4667 if (cluster.length === 0) {
4668 // If cluster is empty, break out early & move to next cluster
4669 continue;
4670 }
4671
4672 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4673 // Select different medoid if its configuration has the lowest cost
4674
4675 for (var _n = 0; _n < cluster.length; _n++) {
4676 curCost = findCost(cluster[_n], cluster, opts.attributes);
4677
4678 if (curCost < minCosts[m]) {
4679 minCosts[m] = curCost;
4680 medoids[m] = cluster[_n];
4681 isStillMoving = true;
4682 }
4683 }
4684
4685 clusters[m] = cy.collection(cluster);
4686 }
4687
4688 iterations++;
4689 }
4690
4691 return clusters;
4692};
4693
4694var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4695 var numerator, denominator;
4696
4697 for (var n = 0; n < nodes.length; n++) {
4698 for (var c = 0; c < centroids.length; c++) {
4699 weight[n][c] = Math.pow(U[n][c], opts.m);
4700 }
4701 }
4702
4703 for (var _c = 0; _c < centroids.length; _c++) {
4704 for (var dim = 0; dim < opts.attributes.length; dim++) {
4705 numerator = 0;
4706 denominator = 0;
4707
4708 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4709 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4710 denominator += weight[_n2][_c];
4711 }
4712
4713 centroids[_c][dim] = numerator / denominator;
4714 }
4715 }
4716};
4717
4718var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4719 // Save previous step
4720 for (var i = 0; i < U.length; i++) {
4721 _U[i] = U[i].slice();
4722 }
4723
4724 var sum, numerator, denominator;
4725 var pow = 2 / (opts.m - 1);
4726
4727 for (var c = 0; c < centroids.length; c++) {
4728 for (var n = 0; n < nodes.length; n++) {
4729 sum = 0;
4730
4731 for (var k = 0; k < centroids.length; k++) {
4732 // against all other centroids
4733 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4734 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4735 sum += Math.pow(numerator / denominator, pow);
4736 }
4737
4738 U[n][c] = 1 / sum;
4739 }
4740 }
4741};
4742
4743var assign$1 = function assign(nodes, U, opts, cy) {
4744 var clusters = new Array(opts.k);
4745
4746 for (var c = 0; c < clusters.length; c++) {
4747 clusters[c] = [];
4748 }
4749
4750 var max;
4751 var index;
4752
4753 for (var n = 0; n < U.length; n++) {
4754 // for each node (U is N x C matrix)
4755 max = -Infinity;
4756 index = -1; // Determine which cluster the node is most likely to belong in
4757
4758 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4759 if (U[n][_c2] > max) {
4760 max = U[n][_c2];
4761 index = _c2;
4762 }
4763 }
4764
4765 clusters[index].push(nodes[n]);
4766 } // Turn every array into a collection of nodes
4767
4768
4769 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4770 clusters[_c3] = cy.collection(clusters[_c3]);
4771 }
4772
4773 return clusters;
4774};
4775
4776var fuzzyCMeans = function fuzzyCMeans(options) {
4777 var cy = this.cy();
4778 var nodes = this.nodes();
4779 var opts = setOptions$2(options); // Begin fuzzy c-means algorithm
4780
4781 var clusters;
4782 var centroids;
4783 var U;
4784
4785 var _U;
4786
4787 var weight; // Step 1: Initialize letiables.
4788
4789 _U = new Array(nodes.length);
4790
4791 for (var i = 0; i < nodes.length; i++) {
4792 // N x C matrix
4793 _U[i] = new Array(opts.k);
4794 }
4795
4796 U = new Array(nodes.length);
4797
4798 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4799 // N x C matrix
4800 U[_i3] = new Array(opts.k);
4801 }
4802
4803 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4804 var total = 0;
4805
4806 for (var j = 0; j < opts.k; j++) {
4807 U[_i4][j] = Math.random();
4808 total += U[_i4][j];
4809 }
4810
4811 for (var _j = 0; _j < opts.k; _j++) {
4812 U[_i4][_j] = U[_i4][_j] / total;
4813 }
4814 }
4815
4816 centroids = new Array(opts.k);
4817
4818 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4819 centroids[_i5] = new Array(opts.attributes.length);
4820 }
4821
4822 weight = new Array(nodes.length);
4823
4824 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4825 // N x C matrix
4826 weight[_i6] = new Array(opts.k);
4827 } // end init FCM
4828
4829
4830 var isStillMoving = true;
4831 var iterations = 0;
4832
4833 while (isStillMoving && iterations < opts.maxIterations) {
4834 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4835
4836 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4837
4838 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4839
4840 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4841 isStillMoving = true;
4842 }
4843
4844 iterations++;
4845 } // Assign nodes to clusters with highest probability.
4846
4847
4848 clusters = assign$1(nodes, U, opts, cy);
4849 return {
4850 clusters: clusters,
4851 degreeOfMembership: U
4852 };
4853};
4854
4855var kClustering = {
4856 kMeans: kMeans,
4857 kMedoids: kMedoids,
4858 fuzzyCMeans: fuzzyCMeans,
4859 fcm: fuzzyCMeans
4860};
4861
4862// Implemented by Zoe Xi @zoexi for GSOC 2016
4863var defaults$a = defaults$g({
4864 distance: 'euclidean',
4865 // distance metric to compare nodes
4866 linkage: 'min',
4867 // linkage criterion : how to determine the distance between clusters of nodes
4868 mode: 'threshold',
4869 // mode:'threshold' => clusters must be threshold distance apart
4870 threshold: Infinity,
4871 // the distance threshold
4872 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4873 addDendrogram: false,
4874 // whether to add the dendrogram to the graph for viz
4875 dendrogramDepth: 0,
4876 // depth at which dendrogram branches are merged into the returned clusters
4877 attributes: [] // array of attr functions
4878
4879});
4880var linkageAliases = {
4881 'single': 'min',
4882 'complete': 'max'
4883};
4884
4885var setOptions$1 = function setOptions(options) {
4886 var opts = defaults$a(options);
4887 var preferredAlias = linkageAliases[opts.linkage];
4888
4889 if (preferredAlias != null) {
4890 opts.linkage = preferredAlias;
4891 }
4892
4893 return opts;
4894};
4895
4896var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4897 // Find two closest clusters from cached mins
4898 var minKey = 0;
4899 var min = Infinity;
4900 var dist;
4901 var attrs = opts.attributes;
4902
4903 var getDist = function getDist(n1, n2) {
4904 return clusteringDistance(opts.distance, attrs.length, function (i) {
4905 return attrs[i](n1);
4906 }, function (i) {
4907 return attrs[i](n2);
4908 }, n1, n2);
4909 };
4910
4911 for (var i = 0; i < clusters.length; i++) {
4912 var key = clusters[i].key;
4913 var _dist = dists[key][mins[key]];
4914
4915 if (_dist < min) {
4916 minKey = key;
4917 min = _dist;
4918 }
4919 }
4920
4921 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4922 return false;
4923 }
4924
4925 var c1 = index[minKey];
4926 var c2 = index[mins[minKey]];
4927 var merged; // Merge two closest clusters
4928
4929 if (opts.mode === 'dendrogram') {
4930 merged = {
4931 left: c1,
4932 right: c2,
4933 key: c1.key
4934 };
4935 } else {
4936 merged = {
4937 value: c1.value.concat(c2.value),
4938 key: c1.key
4939 };
4940 }
4941
4942 clusters[c1.index] = merged;
4943 clusters.splice(c2.index, 1);
4944 index[c1.key] = merged; // Update distances with new merged cluster
4945
4946 for (var _i = 0; _i < clusters.length; _i++) {
4947 var cur = clusters[_i];
4948
4949 if (c1.key === cur.key) {
4950 dist = Infinity;
4951 } else if (opts.linkage === 'min') {
4952 dist = dists[c1.key][cur.key];
4953
4954 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4955 dist = dists[c2.key][cur.key];
4956 }
4957 } else if (opts.linkage === 'max') {
4958 dist = dists[c1.key][cur.key];
4959
4960 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4961 dist = dists[c2.key][cur.key];
4962 }
4963 } else if (opts.linkage === 'mean') {
4964 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4965 } else {
4966 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4967 }
4968
4969 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4970 } // Update cached mins
4971
4972
4973 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4974 var key1 = clusters[_i2].key;
4975
4976 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4977 var _min = key1;
4978
4979 for (var j = 0; j < clusters.length; j++) {
4980 var key2 = clusters[j].key;
4981
4982 if (dists[key1][key2] < dists[key1][_min]) {
4983 _min = key2;
4984 }
4985 }
4986
4987 mins[key1] = _min;
4988 }
4989
4990 clusters[_i2].index = _i2;
4991 } // Clean up meta data used for clustering
4992
4993
4994 c1.key = c2.key = c1.index = c2.index = null;
4995 return true;
4996};
4997
4998var getAllChildren = function getAllChildren(root, arr, cy) {
4999 if (!root) return;
5000
5001 if (root.value) {
5002 arr.push(root.value);
5003 } else {
5004 if (root.left) getAllChildren(root.left, arr);
5005 if (root.right) getAllChildren(root.right, arr);
5006 }
5007};
5008
5009var buildDendrogram = function buildDendrogram(root, cy) {
5010 if (!root) return '';
5011
5012 if (root.left && root.right) {
5013 var leftStr = buildDendrogram(root.left, cy);
5014 var rightStr = buildDendrogram(root.right, cy);
5015 var node = cy.add({
5016 group: 'nodes',
5017 data: {
5018 id: leftStr + ',' + rightStr
5019 }
5020 });
5021 cy.add({
5022 group: 'edges',
5023 data: {
5024 source: leftStr,
5025 target: node.id()
5026 }
5027 });
5028 cy.add({
5029 group: 'edges',
5030 data: {
5031 source: rightStr,
5032 target: node.id()
5033 }
5034 });
5035 return node.id();
5036 } else if (root.value) {
5037 return root.value.id();
5038 }
5039};
5040
5041var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5042 if (!root) return [];
5043 var left = [],
5044 right = [],
5045 leaves = [];
5046
5047 if (k === 0) {
5048 // don't cut tree, simply return all nodes as 1 single cluster
5049 if (root.left) getAllChildren(root.left, left);
5050 if (root.right) getAllChildren(root.right, right);
5051 leaves = left.concat(right);
5052 return [cy.collection(leaves)];
5053 } else if (k === 1) {
5054 // cut at root
5055 if (root.value) {
5056 // leaf node
5057 return [cy.collection(root.value)];
5058 } else {
5059 if (root.left) getAllChildren(root.left, left);
5060 if (root.right) getAllChildren(root.right, right);
5061 return [cy.collection(left), cy.collection(right)];
5062 }
5063 } else {
5064 if (root.value) {
5065 return [cy.collection(root.value)];
5066 } else {
5067 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5068 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5069 return left.concat(right);
5070 }
5071 }
5072};
5073/* eslint-enable */
5074
5075
5076var hierarchicalClustering = function hierarchicalClustering(options) {
5077 var cy = this.cy();
5078 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5079
5080 var opts = setOptions$1(options);
5081 var attrs = opts.attributes;
5082
5083 var getDist = function getDist(n1, n2) {
5084 return clusteringDistance(opts.distance, attrs.length, function (i) {
5085 return attrs[i](n1);
5086 }, function (i) {
5087 return attrs[i](n2);
5088 }, n1, n2);
5089 }; // Begin hierarchical algorithm
5090
5091
5092 var clusters = [];
5093 var dists = []; // distances between each pair of clusters
5094
5095 var mins = []; // closest cluster for each cluster
5096
5097 var index = []; // hash of all clusters by key
5098 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5099
5100 for (var n = 0; n < nodes.length; n++) {
5101 var cluster = {
5102 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5103 key: n,
5104 index: n
5105 };
5106 clusters[n] = cluster;
5107 index[n] = cluster;
5108 dists[n] = [];
5109 mins[n] = 0;
5110 } // Calculate the distance between each pair of clusters
5111
5112
5113 for (var i = 0; i < clusters.length; i++) {
5114 for (var j = 0; j <= i; j++) {
5115 var dist = void 0;
5116
5117 if (opts.mode === 'dendrogram') {
5118 // modes store cluster values differently
5119 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5120 } else {
5121 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5122 }
5123
5124 dists[i][j] = dist;
5125 dists[j][i] = dist;
5126
5127 if (dist < dists[i][mins[i]]) {
5128 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5129 }
5130 }
5131 } // Find the closest pair of clusters and merge them into a single cluster.
5132 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5133
5134
5135 var merged = mergeClosest(clusters, index, dists, mins, opts);
5136
5137 while (merged) {
5138 merged = mergeClosest(clusters, index, dists, mins, opts);
5139 }
5140
5141 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5142 // in addition to returning the clusters.
5143
5144 if (opts.mode === 'dendrogram') {
5145 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5146 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5147 } else {
5148 // Regular mode simply returns the clusters
5149 retClusters = new Array(clusters.length);
5150 clusters.forEach(function (cluster, i) {
5151 // Clean up meta data used for clustering
5152 cluster.key = cluster.index = null;
5153 retClusters[i] = cy.collection(cluster.value);
5154 });
5155 }
5156
5157 return retClusters;
5158};
5159
5160var hierarchicalClustering$1 = {
5161 hierarchicalClustering: hierarchicalClustering,
5162 hca: hierarchicalClustering
5163};
5164
5165// Implemented by Zoe Xi @zoexi for GSOC 2016
5166var defaults$9 = defaults$g({
5167 distance: 'euclidean',
5168 // distance metric to compare attributes between two nodes
5169 preference: 'median',
5170 // suitability of a data point to serve as an exemplar
5171 damping: 0.8,
5172 // damping factor between [0.5, 1)
5173 maxIterations: 1000,
5174 // max number of iterations to run
5175 minIterations: 100,
5176 // min number of iterations to run in order for clustering to stop
5177 attributes: [// functions to quantify the similarity between any two points
5178 // e.g. node => node.data('weight')
5179 ]
5180});
5181
5182var setOptions = function setOptions(options) {
5183 var dmp = options.damping;
5184 var pref = options.preference;
5185
5186 if (!(0.5 <= dmp && dmp < 1)) {
5187 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5188 }
5189
5190 var validPrefs = ['median', 'mean', 'min', 'max'];
5191
5192 if (!(validPrefs.some(function (v) {
5193 return v === pref;
5194 }) || number$1(pref))) {
5195 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5196 return "'".concat(p, "'");
5197 }).join(', '), "] or a number. Got: ").concat(pref));
5198 }
5199
5200 return defaults$9(options);
5201};
5202/* eslint-enable */
5203
5204
5205var getSimilarity = function getSimilarity(type, n1, n2, attributes) {
5206 var attr = function attr(n, i) {
5207 return attributes[i](n);
5208 }; // nb negative because similarity should have an inverse relationship to distance
5209
5210
5211 return -clusteringDistance(type, attributes.length, function (i) {
5212 return attr(n1, i);
5213 }, function (i) {
5214 return attr(n2, i);
5215 }, n1, n2);
5216};
5217
5218var getPreference = function getPreference(S, preference) {
5219 // larger preference = greater # of clusters
5220 var p = null;
5221
5222 if (preference === 'median') {
5223 p = median(S);
5224 } else if (preference === 'mean') {
5225 p = mean(S);
5226 } else if (preference === 'min') {
5227 p = min(S);
5228 } else if (preference === 'max') {
5229 p = max(S);
5230 } else {
5231 // Custom preference number, as set by user
5232 p = preference;
5233 }
5234
5235 return p;
5236};
5237
5238var findExemplars = function findExemplars(n, R, A) {
5239 var indices = [];
5240
5241 for (var i = 0; i < n; i++) {
5242 if (R[i * n + i] + A[i * n + i] > 0) {
5243 indices.push(i);
5244 }
5245 }
5246
5247 return indices;
5248};
5249
5250var assignClusters = function assignClusters(n, S, exemplars) {
5251 var clusters = [];
5252
5253 for (var i = 0; i < n; i++) {
5254 var index = -1;
5255 var max = -Infinity;
5256
5257 for (var ei = 0; ei < exemplars.length; ei++) {
5258 var e = exemplars[ei];
5259
5260 if (S[i * n + e] > max) {
5261 index = e;
5262 max = S[i * n + e];
5263 }
5264 }
5265
5266 if (index > 0) {
5267 clusters.push(index);
5268 }
5269 }
5270
5271 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5272 clusters[exemplars[_ei]] = exemplars[_ei];
5273 }
5274
5275 return clusters;
5276};
5277
5278var assign = function assign(n, S, exemplars) {
5279 var clusters = assignClusters(n, S, exemplars);
5280
5281 for (var ei = 0; ei < exemplars.length; ei++) {
5282 var ii = [];
5283
5284 for (var c = 0; c < clusters.length; c++) {
5285 if (clusters[c] === exemplars[ei]) {
5286 ii.push(c);
5287 }
5288 }
5289
5290 var maxI = -1;
5291 var maxSum = -Infinity;
5292
5293 for (var i = 0; i < ii.length; i++) {
5294 var sum = 0;
5295
5296 for (var j = 0; j < ii.length; j++) {
5297 sum += S[ii[j] * n + ii[i]];
5298 }
5299
5300 if (sum > maxSum) {
5301 maxI = i;
5302 maxSum = sum;
5303 }
5304 }
5305
5306 exemplars[ei] = ii[maxI];
5307 }
5308
5309 clusters = assignClusters(n, S, exemplars);
5310 return clusters;
5311};
5312
5313var affinityPropagation = function affinityPropagation(options) {
5314 var cy = this.cy();
5315 var nodes = this.nodes();
5316 var opts = setOptions(options); // Map each node to its position in node array
5317
5318 var id2position = {};
5319
5320 for (var i = 0; i < nodes.length; i++) {
5321 id2position[nodes[i].id()] = i;
5322 } // Begin affinity propagation algorithm
5323
5324
5325 var n; // number of data points
5326
5327 var n2; // size of matrices
5328
5329 var S; // similarity matrix (1D array)
5330
5331 var p; // preference/suitability of a data point to serve as an exemplar
5332
5333 var R; // responsibility matrix (1D array)
5334
5335 var A; // availability matrix (1D array)
5336
5337 n = nodes.length;
5338 n2 = n * n; // Initialize and build S similarity matrix
5339
5340 S = new Array(n2);
5341
5342 for (var _i = 0; _i < n2; _i++) {
5343 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5344 }
5345
5346 for (var _i2 = 0; _i2 < n; _i2++) {
5347 for (var j = 0; j < n; j++) {
5348 if (_i2 !== j) {
5349 S[_i2 * n + j] = getSimilarity(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5350 }
5351 }
5352 } // Place preferences on the diagonal of S
5353
5354
5355 p = getPreference(S, opts.preference);
5356
5357 for (var _i3 = 0; _i3 < n; _i3++) {
5358 S[_i3 * n + _i3] = p;
5359 } // Initialize R responsibility matrix
5360
5361
5362 R = new Array(n2);
5363
5364 for (var _i4 = 0; _i4 < n2; _i4++) {
5365 R[_i4] = 0.0;
5366 } // Initialize A availability matrix
5367
5368
5369 A = new Array(n2);
5370
5371 for (var _i5 = 0; _i5 < n2; _i5++) {
5372 A[_i5] = 0.0;
5373 }
5374
5375 var old = new Array(n);
5376 var Rp = new Array(n);
5377 var se = new Array(n);
5378
5379 for (var _i6 = 0; _i6 < n; _i6++) {
5380 old[_i6] = 0.0;
5381 Rp[_i6] = 0.0;
5382 se[_i6] = 0;
5383 }
5384
5385 var e = new Array(n * opts.minIterations);
5386
5387 for (var _i7 = 0; _i7 < e.length; _i7++) {
5388 e[_i7] = 0;
5389 }
5390
5391 var iter;
5392
5393 for (iter = 0; iter < opts.maxIterations; iter++) {
5394 // main algorithmic loop
5395 // Update R responsibility matrix
5396 for (var _i8 = 0; _i8 < n; _i8++) {
5397 var max = -Infinity,
5398 max2 = -Infinity,
5399 maxI = -1,
5400 AS = 0.0;
5401
5402 for (var _j = 0; _j < n; _j++) {
5403 old[_j] = R[_i8 * n + _j];
5404 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5405
5406 if (AS >= max) {
5407 max2 = max;
5408 max = AS;
5409 maxI = _j;
5410 } else if (AS > max2) {
5411 max2 = AS;
5412 }
5413 }
5414
5415 for (var _j2 = 0; _j2 < n; _j2++) {
5416 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5417 }
5418
5419 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5420 } // Update A availability matrix
5421
5422
5423 for (var _i9 = 0; _i9 < n; _i9++) {
5424 var sum = 0;
5425
5426 for (var _j3 = 0; _j3 < n; _j3++) {
5427 old[_j3] = A[_j3 * n + _i9];
5428 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5429 sum += Rp[_j3];
5430 }
5431
5432 sum -= Rp[_i9];
5433 Rp[_i9] = R[_i9 * n + _i9];
5434 sum += Rp[_i9];
5435
5436 for (var _j4 = 0; _j4 < n; _j4++) {
5437 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5438 }
5439
5440 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5441 } // Check for convergence
5442
5443
5444 var K = 0;
5445
5446 for (var _i10 = 0; _i10 < n; _i10++) {
5447 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5448 e[iter % opts.minIterations * n + _i10] = E;
5449 K += E;
5450 }
5451
5452 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5453 var _sum = 0;
5454
5455 for (var _i11 = 0; _i11 < n; _i11++) {
5456 se[_i11] = 0;
5457
5458 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5459 se[_i11] += e[_j5 * n + _i11];
5460 }
5461
5462 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5463 _sum++;
5464 }
5465 }
5466
5467 if (_sum === n) {
5468 // then we have convergence
5469 break;
5470 }
5471 }
5472 } // Identify exemplars (cluster centers)
5473
5474
5475 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5476
5477 var clusterIndices = assign(n, S, exemplarsIndices);
5478 var clusters = {};
5479
5480 for (var c = 0; c < exemplarsIndices.length; c++) {
5481 clusters[exemplarsIndices[c]] = [];
5482 }
5483
5484 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5485 var pos = id2position[nodes[_i12].id()];
5486
5487 var clusterIndex = clusterIndices[pos];
5488
5489 if (clusterIndex != null) {
5490 // the node may have not been assigned a cluster if no valid attributes were specified
5491 clusters[clusterIndex].push(nodes[_i12]);
5492 }
5493 }
5494
5495 var retClusters = new Array(exemplarsIndices.length);
5496
5497 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5498 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5499 }
5500
5501 return retClusters;
5502};
5503
5504var affinityPropagation$1 = {
5505 affinityPropagation: affinityPropagation,
5506 ap: affinityPropagation
5507};
5508
5509var hierholzerDefaults = defaults$g({
5510 root: undefined,
5511 directed: false
5512});
5513var elesfn$k = {
5514 hierholzer: function hierholzer(options) {
5515 if (!plainObject(options)) {
5516 var args = arguments;
5517 options = {
5518 root: args[0],
5519 directed: args[1]
5520 };
5521 }
5522
5523 var _hierholzerDefaults = hierholzerDefaults(options),
5524 root = _hierholzerDefaults.root,
5525 directed = _hierholzerDefaults.directed;
5526
5527 var eles = this;
5528 var dflag = false;
5529 var oddIn;
5530 var oddOut;
5531 var startVertex;
5532 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5533 var nodes = {};
5534 var edges = {};
5535
5536 if (directed) {
5537 eles.forEach(function (ele) {
5538 var id = ele.id();
5539
5540 if (ele.isNode()) {
5541 var ind = ele.indegree(true);
5542 var outd = ele.outdegree(true);
5543 var d1 = ind - outd;
5544 var d2 = outd - ind;
5545
5546 if (d1 == 1) {
5547 if (oddIn) dflag = true;else oddIn = id;
5548 } else if (d2 == 1) {
5549 if (oddOut) dflag = true;else oddOut = id;
5550 } else if (d2 > 1 || d1 > 1) {
5551 dflag = true;
5552 }
5553
5554 nodes[id] = [];
5555 ele.outgoers().forEach(function (e) {
5556 if (e.isEdge()) nodes[id].push(e.id());
5557 });
5558 } else {
5559 edges[id] = [undefined, ele.target().id()];
5560 }
5561 });
5562 } else {
5563 eles.forEach(function (ele) {
5564 var id = ele.id();
5565
5566 if (ele.isNode()) {
5567 var d = ele.degree(true);
5568
5569 if (d % 2) {
5570 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5571 }
5572
5573 nodes[id] = [];
5574 ele.connectedEdges().forEach(function (e) {
5575 return nodes[id].push(e.id());
5576 });
5577 } else {
5578 edges[id] = [ele.source().id(), ele.target().id()];
5579 }
5580 });
5581 }
5582
5583 var result = {
5584 found: false,
5585 trail: undefined
5586 };
5587 if (dflag) return result;else if (oddOut && oddIn) {
5588 if (directed) {
5589 if (startVertex && oddOut != startVertex) {
5590 return result;
5591 }
5592
5593 startVertex = oddOut;
5594 } else {
5595 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5596 return result;
5597 } else if (!startVertex) {
5598 startVertex = oddOut;
5599 }
5600 }
5601 } else {
5602 if (!startVertex) startVertex = eles[0].id();
5603 }
5604
5605 var walk = function walk(v) {
5606 var currentNode = v;
5607 var subtour = [v];
5608 var adj, adjTail, adjHead;
5609
5610 while (nodes[currentNode].length) {
5611 adj = nodes[currentNode].shift();
5612 adjTail = edges[adj][0];
5613 adjHead = edges[adj][1];
5614
5615 if (currentNode != adjHead) {
5616 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5617 return e != adj;
5618 });
5619 currentNode = adjHead;
5620 } else if (!directed && currentNode != adjTail) {
5621 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5622 return e != adj;
5623 });
5624 currentNode = adjTail;
5625 }
5626
5627 subtour.unshift(adj);
5628 subtour.unshift(currentNode);
5629 }
5630
5631 return subtour;
5632 };
5633
5634 var trail = [];
5635 var subtour = [];
5636 subtour = walk(startVertex);
5637
5638 while (subtour.length != 1) {
5639 if (nodes[subtour[0]].length == 0) {
5640 trail.unshift(eles.getElementById(subtour.shift()));
5641 trail.unshift(eles.getElementById(subtour.shift()));
5642 } else {
5643 subtour = walk(subtour.shift()).concat(subtour);
5644 }
5645 }
5646
5647 trail.unshift(eles.getElementById(subtour.shift())); // final node
5648
5649 for (var d in nodes) {
5650 if (nodes[d].length) {
5651 return result;
5652 }
5653 }
5654
5655 result.found = true;
5656 result.trail = this.spawn(trail, true);
5657 return result;
5658 }
5659};
5660
5661var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5662 var eles = this;
5663 var nodes = {};
5664 var id = 0;
5665 var edgeCount = 0;
5666 var components = [];
5667 var stack = [];
5668 var visitedEdges = {};
5669
5670 var buildComponent = function buildComponent(x, y) {
5671 var i = stack.length - 1;
5672 var cutset = [];
5673 var component = eles.spawn();
5674
5675 while (stack[i].x != x || stack[i].y != y) {
5676 cutset.push(stack.pop().edge);
5677 i--;
5678 }
5679
5680 cutset.push(stack.pop().edge);
5681 cutset.forEach(function (edge) {
5682 var connectedNodes = edge.connectedNodes().intersection(eles);
5683 component.merge(edge);
5684 connectedNodes.forEach(function (node) {
5685 var nodeId = node.id();
5686 var connectedEdges = node.connectedEdges().intersection(eles);
5687 component.merge(node);
5688
5689 if (!nodes[nodeId].cutVertex) {
5690 component.merge(connectedEdges);
5691 } else {
5692 component.merge(connectedEdges.filter(function (edge) {
5693 return edge.isLoop();
5694 }));
5695 }
5696 });
5697 });
5698 components.push(component);
5699 };
5700
5701 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5702 if (root === parent) edgeCount += 1;
5703 nodes[currentNode] = {
5704 id: id,
5705 low: id++,
5706 cutVertex: false
5707 };
5708 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5709
5710 if (edges.size() === 0) {
5711 components.push(eles.spawn(eles.getElementById(currentNode)));
5712 } else {
5713 var sourceId, targetId, otherNodeId, edgeId;
5714 edges.forEach(function (edge) {
5715 sourceId = edge.source().id();
5716 targetId = edge.target().id();
5717 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5718
5719 if (otherNodeId !== parent) {
5720 edgeId = edge.id();
5721
5722 if (!visitedEdges[edgeId]) {
5723 visitedEdges[edgeId] = true;
5724 stack.push({
5725 x: currentNode,
5726 y: otherNodeId,
5727 edge: edge
5728 });
5729 }
5730
5731 if (!(otherNodeId in nodes)) {
5732 biconnectedSearch(root, otherNodeId, currentNode);
5733 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5734
5735 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5736 nodes[currentNode].cutVertex = true;
5737 buildComponent(currentNode, otherNodeId);
5738 }
5739 } else {
5740 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5741 }
5742 }
5743 });
5744 }
5745 };
5746
5747 eles.forEach(function (ele) {
5748 if (ele.isNode()) {
5749 var nodeId = ele.id();
5750
5751 if (!(nodeId in nodes)) {
5752 edgeCount = 0;
5753 biconnectedSearch(nodeId, nodeId);
5754 nodes[nodeId].cutVertex = edgeCount > 1;
5755 }
5756 }
5757 });
5758 var cutVertices = Object.keys(nodes).filter(function (id) {
5759 return nodes[id].cutVertex;
5760 }).map(function (id) {
5761 return eles.getElementById(id);
5762 });
5763 return {
5764 cut: eles.spawn(cutVertices),
5765 components: components
5766 };
5767};
5768
5769var hopcroftTarjanBiconnected$1 = {
5770 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5771 htbc: hopcroftTarjanBiconnected,
5772 htb: hopcroftTarjanBiconnected,
5773 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5774};
5775
5776var tarjanStronglyConnected = function tarjanStronglyConnected() {
5777 var eles = this;
5778 var nodes = {};
5779 var index = 0;
5780 var components = [];
5781 var stack = [];
5782 var cut = eles.spawn(eles);
5783
5784 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5785 stack.push(sourceNodeId);
5786 nodes[sourceNodeId] = {
5787 index: index,
5788 low: index++,
5789 explored: false
5790 };
5791 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5792 connectedEdges.forEach(function (edge) {
5793 var targetNodeId = edge.target().id();
5794
5795 if (targetNodeId !== sourceNodeId) {
5796 if (!(targetNodeId in nodes)) {
5797 stronglyConnectedSearch(targetNodeId);
5798 }
5799
5800 if (!nodes[targetNodeId].explored) {
5801 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5802 }
5803 }
5804 });
5805
5806 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5807 var componentNodes = eles.spawn();
5808
5809 for (;;) {
5810 var nodeId = stack.pop();
5811 componentNodes.merge(eles.getElementById(nodeId));
5812 nodes[nodeId].low = nodes[sourceNodeId].index;
5813 nodes[nodeId].explored = true;
5814
5815 if (nodeId === sourceNodeId) {
5816 break;
5817 }
5818 }
5819
5820 var componentEdges = componentNodes.edgesWith(componentNodes);
5821 var component = componentNodes.merge(componentEdges);
5822 components.push(component);
5823 cut = cut.difference(component);
5824 }
5825 };
5826
5827 eles.forEach(function (ele) {
5828 if (ele.isNode()) {
5829 var nodeId = ele.id();
5830
5831 if (!(nodeId in nodes)) {
5832 stronglyConnectedSearch(nodeId);
5833 }
5834 }
5835 });
5836 return {
5837 cut: cut,
5838 components: components
5839 };
5840};
5841
5842var tarjanStronglyConnected$1 = {
5843 tarjanStronglyConnected: tarjanStronglyConnected,
5844 tsc: tarjanStronglyConnected,
5845 tscc: tarjanStronglyConnected,
5846 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5847};
5848
5849var elesfn$j = {};
5850[elesfn$v, elesfn$u, elesfn$t, elesfn$s, elesfn$r, elesfn$q, elesfn$p, elesfn$o, elesfn$n, elesfn$m, elesfn$l, markovClustering$1, kClustering, hierarchicalClustering$1, affinityPropagation$1, elesfn$k, hopcroftTarjanBiconnected$1, tarjanStronglyConnected$1].forEach(function (props) {
5851 extend(elesfn$j, props);
5852});
5853
5854/*!
5855Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5856Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5857Licensed under The MIT License (http://opensource.org/licenses/MIT)
5858*/
5859
5860/* promise states [Promises/A+ 2.1] */
5861var STATE_PENDING = 0;
5862/* [Promises/A+ 2.1.1] */
5863
5864var STATE_FULFILLED = 1;
5865/* [Promises/A+ 2.1.2] */
5866
5867var STATE_REJECTED = 2;
5868/* [Promises/A+ 2.1.3] */
5869
5870/* promise object constructor */
5871
5872var api = function api(executor) {
5873 /* optionally support non-constructor/plain-function call */
5874 if (!(this instanceof api)) return new api(executor);
5875 /* initialize object */
5876
5877 this.id = 'Thenable/1.0.7';
5878 this.state = STATE_PENDING;
5879 /* initial state */
5880
5881 this.fulfillValue = undefined;
5882 /* initial value */
5883
5884 /* [Promises/A+ 1.3, 2.1.2.2] */
5885
5886 this.rejectReason = undefined;
5887 /* initial reason */
5888
5889 /* [Promises/A+ 1.5, 2.1.3.2] */
5890
5891 this.onFulfilled = [];
5892 /* initial handlers */
5893
5894 this.onRejected = [];
5895 /* initial handlers */
5896
5897 /* provide optional information-hiding proxy */
5898
5899 this.proxy = {
5900 then: this.then.bind(this)
5901 };
5902 /* support optional executor function */
5903
5904 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5905};
5906/* promise API methods */
5907
5908
5909api.prototype = {
5910 /* promise resolving methods */
5911 fulfill: function fulfill(value) {
5912 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5913 },
5914 reject: function reject(value) {
5915 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5916 },
5917
5918 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5919 then: function then(onFulfilled, onRejected) {
5920 var curr = this;
5921 var next = new api();
5922 /* [Promises/A+ 2.2.7] */
5923
5924 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5925 /* [Promises/A+ 2.2.2/2.2.6] */
5926
5927 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5928 /* [Promises/A+ 2.2.3/2.2.6] */
5929
5930 execute(curr);
5931 return next.proxy;
5932 /* [Promises/A+ 2.2.7, 3.3] */
5933 }
5934};
5935/* deliver an action */
5936
5937var deliver = function deliver(curr, state, name, value) {
5938 if (curr.state === STATE_PENDING) {
5939 curr.state = state;
5940 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5941
5942 curr[name] = value;
5943 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5944
5945 execute(curr);
5946 }
5947
5948 return curr;
5949};
5950/* execute all handlers */
5951
5952
5953var execute = function execute(curr) {
5954 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5955};
5956/* execute particular set of handlers */
5957
5958
5959var execute_handlers = function execute_handlers(curr, name, value) {
5960 /* global setImmediate: true */
5961
5962 /* global setTimeout: true */
5963
5964 /* short-circuit processing */
5965 if (curr[name].length === 0) return;
5966 /* iterate over all handlers, exactly once */
5967
5968 var handlers = curr[name];
5969 curr[name] = [];
5970 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5971
5972 var func = function func() {
5973 for (var i = 0; i < handlers.length; i++) {
5974 handlers[i](value);
5975 }
5976 /* [Promises/A+ 2.2.5] */
5977
5978 };
5979 /* execute procedure asynchronously */
5980
5981 /* [Promises/A+ 2.2.4, 3.1] */
5982
5983
5984 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5985};
5986/* generate a resolver function */
5987
5988
5989var resolver = function resolver(cb, next, method) {
5990 return function (value) {
5991 if (typeof cb !== 'function')
5992 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5993 next[method].call(next, value);
5994 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5995 else {
5996 var result;
5997
5998 try {
5999 result = cb(value);
6000 }
6001 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
6002 catch (e) {
6003 next.reject(e);
6004 /* [Promises/A+ 2.2.7.2] */
6005
6006 return;
6007 }
6008
6009 resolve(next, result);
6010 /* [Promises/A+ 2.2.7.1] */
6011 }
6012 };
6013};
6014/* "Promise Resolution Procedure" */
6015
6016/* [Promises/A+ 2.3] */
6017
6018
6019var resolve = function resolve(promise, x) {
6020 /* sanity check arguments */
6021
6022 /* [Promises/A+ 2.3.1] */
6023 if (promise === x || promise.proxy === x) {
6024 promise.reject(new TypeError('cannot resolve promise with itself'));
6025 return;
6026 }
6027 /* surgically check for a "then" method
6028 (mainly to just call the "getter" of "then" only once) */
6029
6030
6031 var then;
6032
6033 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6034 try {
6035 then = x.then;
6036 }
6037 /* [Promises/A+ 2.3.3.1, 3.5] */
6038 catch (e) {
6039 promise.reject(e);
6040 /* [Promises/A+ 2.3.3.2] */
6041
6042 return;
6043 }
6044 }
6045 /* handle own Thenables [Promises/A+ 2.3.2]
6046 and similar "thenables" [Promises/A+ 2.3.3] */
6047
6048
6049 if (typeof then === 'function') {
6050 var resolved = false;
6051
6052 try {
6053 /* call retrieved "then" method */
6054
6055 /* [Promises/A+ 2.3.3.3] */
6056 then.call(x,
6057 /* resolvePromise */
6058
6059 /* [Promises/A+ 2.3.3.3.1] */
6060 function (y) {
6061 if (resolved) return;
6062 resolved = true;
6063 /* [Promises/A+ 2.3.3.3.3] */
6064
6065 if (y === x)
6066 /* [Promises/A+ 3.6] */
6067 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6068 },
6069 /* rejectPromise */
6070
6071 /* [Promises/A+ 2.3.3.3.2] */
6072 function (r) {
6073 if (resolved) return;
6074 resolved = true;
6075 /* [Promises/A+ 2.3.3.3.3] */
6076
6077 promise.reject(r);
6078 });
6079 } catch (e) {
6080 if (!resolved)
6081 /* [Promises/A+ 2.3.3.3.3] */
6082 promise.reject(e);
6083 /* [Promises/A+ 2.3.3.3.4] */
6084 }
6085
6086 return;
6087 }
6088 /* handle other values */
6089
6090
6091 promise.fulfill(x);
6092 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6093}; // so we always have Promise.all()
6094
6095
6096api.all = function (ps) {
6097 return new api(function (resolveAll, rejectAll) {
6098 var vals = new Array(ps.length);
6099 var doneCount = 0;
6100
6101 var fulfill = function fulfill(i, val) {
6102 vals[i] = val;
6103 doneCount++;
6104
6105 if (doneCount === ps.length) {
6106 resolveAll(vals);
6107 }
6108 };
6109
6110 for (var i = 0; i < ps.length; i++) {
6111 (function (i) {
6112 var p = ps[i];
6113 var isPromise = p != null && p.then != null;
6114
6115 if (isPromise) {
6116 p.then(function (val) {
6117 fulfill(i, val);
6118 }, function (err) {
6119 rejectAll(err);
6120 });
6121 } else {
6122 var val = p;
6123 fulfill(i, val);
6124 }
6125 })(i);
6126 }
6127 });
6128};
6129
6130api.resolve = function (val) {
6131 return new api(function (resolve, reject) {
6132 resolve(val);
6133 });
6134};
6135
6136api.reject = function (val) {
6137 return new api(function (resolve, reject) {
6138 reject(val);
6139 });
6140};
6141
6142var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6143
6144var Animation = function Animation(target, opts, opts2) {
6145 var isCore = core(target);
6146 var isEle = !isCore;
6147
6148 var _p = this._private = extend({
6149 duration: 1000
6150 }, opts, opts2);
6151
6152 _p.target = target;
6153 _p.style = _p.style || _p.css;
6154 _p.started = false;
6155 _p.playing = false;
6156 _p.hooked = false;
6157 _p.applying = false;
6158 _p.progress = 0;
6159 _p.completes = [];
6160 _p.frames = [];
6161
6162 if (_p.complete && fn$6(_p.complete)) {
6163 _p.completes.push(_p.complete);
6164 }
6165
6166 if (isEle) {
6167 var pos = target.position();
6168 _p.startPosition = _p.startPosition || {
6169 x: pos.x,
6170 y: pos.y
6171 };
6172 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6173 }
6174
6175 if (isCore) {
6176 var pan = target.pan();
6177 _p.startPan = {
6178 x: pan.x,
6179 y: pan.y
6180 };
6181 _p.startZoom = target.zoom();
6182 } // for future timeline/animations impl
6183
6184
6185 this.length = 1;
6186 this[0] = this;
6187};
6188
6189var anifn = Animation.prototype;
6190extend(anifn, {
6191 instanceString: function instanceString() {
6192 return 'animation';
6193 },
6194 hook: function hook() {
6195 var _p = this._private;
6196
6197 if (!_p.hooked) {
6198 // add to target's animation queue
6199 var q;
6200 var tAni = _p.target._private.animation;
6201
6202 if (_p.queue) {
6203 q = tAni.queue;
6204 } else {
6205 q = tAni.current;
6206 }
6207
6208 q.push(this); // add to the animation loop pool
6209
6210 if (elementOrCollection(_p.target)) {
6211 _p.target.cy().addToAnimationPool(_p.target);
6212 }
6213
6214 _p.hooked = true;
6215 }
6216
6217 return this;
6218 },
6219 play: function play() {
6220 var _p = this._private; // autorewind
6221
6222 if (_p.progress === 1) {
6223 _p.progress = 0;
6224 }
6225
6226 _p.playing = true;
6227 _p.started = false; // needs to be started by animation loop
6228
6229 _p.stopped = false;
6230 this.hook(); // the animation loop will start the animation...
6231
6232 return this;
6233 },
6234 playing: function playing() {
6235 return this._private.playing;
6236 },
6237 apply: function apply() {
6238 var _p = this._private;
6239 _p.applying = true;
6240 _p.started = false; // needs to be started by animation loop
6241
6242 _p.stopped = false;
6243 this.hook(); // the animation loop will apply the animation at this progress
6244
6245 return this;
6246 },
6247 applying: function applying() {
6248 return this._private.applying;
6249 },
6250 pause: function pause() {
6251 var _p = this._private;
6252 _p.playing = false;
6253 _p.started = false;
6254 return this;
6255 },
6256 stop: function stop() {
6257 var _p = this._private;
6258 _p.playing = false;
6259 _p.started = false;
6260 _p.stopped = true; // to be removed from animation queues
6261
6262 return this;
6263 },
6264 rewind: function rewind() {
6265 return this.progress(0);
6266 },
6267 fastforward: function fastforward() {
6268 return this.progress(1);
6269 },
6270 time: function time(t) {
6271 var _p = this._private;
6272
6273 if (t === undefined) {
6274 return _p.progress * _p.duration;
6275 } else {
6276 return this.progress(t / _p.duration);
6277 }
6278 },
6279 progress: function progress(p) {
6280 var _p = this._private;
6281 var wasPlaying = _p.playing;
6282
6283 if (p === undefined) {
6284 return _p.progress;
6285 } else {
6286 if (wasPlaying) {
6287 this.pause();
6288 }
6289
6290 _p.progress = p;
6291 _p.started = false;
6292
6293 if (wasPlaying) {
6294 this.play();
6295 }
6296 }
6297
6298 return this;
6299 },
6300 completed: function completed() {
6301 return this._private.progress === 1;
6302 },
6303 reverse: function reverse() {
6304 var _p = this._private;
6305 var wasPlaying = _p.playing;
6306
6307 if (wasPlaying) {
6308 this.pause();
6309 }
6310
6311 _p.progress = 1 - _p.progress;
6312 _p.started = false;
6313
6314 var swap = function swap(a, b) {
6315 var _pa = _p[a];
6316
6317 if (_pa == null) {
6318 return;
6319 }
6320
6321 _p[a] = _p[b];
6322 _p[b] = _pa;
6323 };
6324
6325 swap('zoom', 'startZoom');
6326 swap('pan', 'startPan');
6327 swap('position', 'startPosition'); // swap styles
6328
6329 if (_p.style) {
6330 for (var i = 0; i < _p.style.length; i++) {
6331 var prop = _p.style[i];
6332 var name = prop.name;
6333 var startStyleProp = _p.startStyle[name];
6334 _p.startStyle[name] = prop;
6335 _p.style[i] = startStyleProp;
6336 }
6337 }
6338
6339 if (wasPlaying) {
6340 this.play();
6341 }
6342
6343 return this;
6344 },
6345 promise: function promise(type) {
6346 var _p = this._private;
6347 var arr;
6348
6349 switch (type) {
6350 case 'frame':
6351 arr = _p.frames;
6352 break;
6353
6354 default:
6355 case 'complete':
6356 case 'completed':
6357 arr = _p.completes;
6358 }
6359
6360 return new Promise$1(function (resolve, reject) {
6361 arr.push(function () {
6362 resolve();
6363 });
6364 });
6365 }
6366});
6367anifn.complete = anifn.completed;
6368anifn.run = anifn.play;
6369anifn.running = anifn.playing;
6370
6371var define$3 = {
6372 animated: function animated() {
6373 return function animatedImpl() {
6374 var self = this;
6375 var selfIsArrayLike = self.length !== undefined;
6376 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6377
6378 var cy = this._private.cy || this;
6379
6380 if (!cy.styleEnabled()) {
6381 return false;
6382 }
6383
6384 var ele = all[0];
6385
6386 if (ele) {
6387 return ele._private.animation.current.length > 0;
6388 }
6389 };
6390 },
6391 // animated
6392 clearQueue: function clearQueue() {
6393 return function clearQueueImpl() {
6394 var self = this;
6395 var selfIsArrayLike = self.length !== undefined;
6396 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6397
6398 var cy = this._private.cy || this;
6399
6400 if (!cy.styleEnabled()) {
6401 return this;
6402 }
6403
6404 for (var i = 0; i < all.length; i++) {
6405 var ele = all[i];
6406 ele._private.animation.queue = [];
6407 }
6408
6409 return this;
6410 };
6411 },
6412 // clearQueue
6413 delay: function delay() {
6414 return function delayImpl(time, complete) {
6415 var cy = this._private.cy || this;
6416
6417 if (!cy.styleEnabled()) {
6418 return this;
6419 }
6420
6421 return this.animate({
6422 delay: time,
6423 duration: time,
6424 complete: complete
6425 });
6426 };
6427 },
6428 // delay
6429 delayAnimation: function delayAnimation() {
6430 return function delayAnimationImpl(time, complete) {
6431 var cy = this._private.cy || this;
6432
6433 if (!cy.styleEnabled()) {
6434 return this;
6435 }
6436
6437 return this.animation({
6438 delay: time,
6439 duration: time,
6440 complete: complete
6441 });
6442 };
6443 },
6444 // delay
6445 animation: function animation() {
6446 return function animationImpl(properties, params) {
6447 var self = this;
6448 var selfIsArrayLike = self.length !== undefined;
6449 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6450
6451 var cy = this._private.cy || this;
6452 var isCore = !selfIsArrayLike;
6453 var isEles = !isCore;
6454
6455 if (!cy.styleEnabled()) {
6456 return this;
6457 }
6458
6459 var style = cy.style();
6460 properties = extend({}, properties, params);
6461 var propertiesEmpty = Object.keys(properties).length === 0;
6462
6463 if (propertiesEmpty) {
6464 return new Animation(all[0], properties); // nothing to animate
6465 }
6466
6467 if (properties.duration === undefined) {
6468 properties.duration = 400;
6469 }
6470
6471 switch (properties.duration) {
6472 case 'slow':
6473 properties.duration = 600;
6474 break;
6475
6476 case 'fast':
6477 properties.duration = 200;
6478 break;
6479 }
6480
6481 if (isEles) {
6482 properties.style = style.getPropsList(properties.style || properties.css);
6483 properties.css = undefined;
6484 }
6485
6486 if (isEles && properties.renderedPosition != null) {
6487 var rpos = properties.renderedPosition;
6488 var pan = cy.pan();
6489 var zoom = cy.zoom();
6490 properties.position = renderedToModelPosition(rpos, zoom, pan);
6491 } // override pan w/ panBy if set
6492
6493
6494 if (isCore && properties.panBy != null) {
6495 var panBy = properties.panBy;
6496 var cyPan = cy.pan();
6497 properties.pan = {
6498 x: cyPan.x + panBy.x,
6499 y: cyPan.y + panBy.y
6500 };
6501 } // override pan w/ center if set
6502
6503
6504 var center = properties.center || properties.centre;
6505
6506 if (isCore && center != null) {
6507 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6508
6509 if (centerPan != null) {
6510 properties.pan = centerPan;
6511 }
6512 } // override pan & zoom w/ fit if set
6513
6514
6515 if (isCore && properties.fit != null) {
6516 var fit = properties.fit;
6517 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6518
6519 if (fitVp != null) {
6520 properties.pan = fitVp.pan;
6521 properties.zoom = fitVp.zoom;
6522 }
6523 } // override zoom (& potentially pan) w/ zoom obj if set
6524
6525
6526 if (isCore && plainObject(properties.zoom)) {
6527 var vp = cy.getZoomedViewport(properties.zoom);
6528
6529 if (vp != null) {
6530 if (vp.zoomed) {
6531 properties.zoom = vp.zoom;
6532 }
6533
6534 if (vp.panned) {
6535 properties.pan = vp.pan;
6536 }
6537 } else {
6538 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6539 }
6540 }
6541
6542 return new Animation(all[0], properties);
6543 };
6544 },
6545 // animate
6546 animate: function animate() {
6547 return function animateImpl(properties, params) {
6548 var self = this;
6549 var selfIsArrayLike = self.length !== undefined;
6550 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6551
6552 var cy = this._private.cy || this;
6553
6554 if (!cy.styleEnabled()) {
6555 return this;
6556 }
6557
6558 if (params) {
6559 properties = extend({}, properties, params);
6560 } // manually hook and run the animation
6561
6562
6563 for (var i = 0; i < all.length; i++) {
6564 var ele = all[i];
6565 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6566 var ani = ele.animation(properties, queue ? {
6567 queue: true
6568 } : undefined);
6569 ani.play();
6570 }
6571
6572 return this; // chaining
6573 };
6574 },
6575 // animate
6576 stop: function stop() {
6577 return function stopImpl(clearQueue, jumpToEnd) {
6578 var self = this;
6579 var selfIsArrayLike = self.length !== undefined;
6580 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6581
6582 var cy = this._private.cy || this;
6583
6584 if (!cy.styleEnabled()) {
6585 return this;
6586 }
6587
6588 for (var i = 0; i < all.length; i++) {
6589 var ele = all[i];
6590 var _p = ele._private;
6591 var anis = _p.animation.current;
6592
6593 for (var j = 0; j < anis.length; j++) {
6594 var ani = anis[j];
6595 var ani_p = ani._private;
6596
6597 if (jumpToEnd) {
6598 // next iteration of the animation loop, the animation
6599 // will go straight to the end and be removed
6600 ani_p.duration = 0;
6601 }
6602 } // clear the queue of future animations
6603
6604
6605 if (clearQueue) {
6606 _p.animation.queue = [];
6607 }
6608
6609 if (!jumpToEnd) {
6610 _p.animation.current = [];
6611 }
6612 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6613
6614
6615 cy.notify('draw');
6616 return this;
6617 };
6618 } // stop
6619
6620}; // define
6621
6622var define$2 = {
6623 // access data field
6624 data: function data(params) {
6625 var defaults = {
6626 field: 'data',
6627 bindingEvent: 'data',
6628 allowBinding: false,
6629 allowSetting: false,
6630 allowGetting: false,
6631 settingEvent: 'data',
6632 settingTriggersEvent: false,
6633 triggerFnName: 'trigger',
6634 immutableKeys: {},
6635 // key => true if immutable
6636 updateStyle: false,
6637 beforeGet: function beforeGet(self) {},
6638 beforeSet: function beforeSet(self, obj) {},
6639 onSet: function onSet(self) {},
6640 canSet: function canSet(self) {
6641 return true;
6642 }
6643 };
6644 params = extend({}, defaults, params);
6645 return function dataImpl(name, value) {
6646 var p = params;
6647 var self = this;
6648 var selfIsArrayLike = self.length !== undefined;
6649 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6650
6651 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6652
6653 if (string(name)) {
6654 // set or get property
6655 var isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
6656
6657 var path = isPathLike && toPath__default["default"](name); // .data('foo')
6658
6659 if (p.allowGetting && value === undefined) {
6660 // get
6661 var ret;
6662
6663 if (single) {
6664 p.beforeGet(single); // check if it's path and a field with the same name doesn't exist
6665
6666 if (path && single._private[p.field][name] === undefined) {
6667 ret = get__default["default"](single._private[p.field], path);
6668 } else {
6669 ret = single._private[p.field][name];
6670 }
6671 }
6672
6673 return ret; // .data('foo', 'bar')
6674 } else if (p.allowSetting && value !== undefined) {
6675 // set
6676 var valid = !p.immutableKeys[name];
6677
6678 if (valid) {
6679 var change = _defineProperty({}, name, value);
6680
6681 p.beforeSet(self, change);
6682
6683 for (var i = 0, l = all.length; i < l; i++) {
6684 var ele = all[i];
6685
6686 if (p.canSet(ele)) {
6687 if (path && single._private[p.field][name] === undefined) {
6688 set__default["default"](ele._private[p.field], path, value);
6689 } else {
6690 ele._private[p.field][name] = value;
6691 }
6692 }
6693 } // update mappers if asked
6694
6695
6696 if (p.updateStyle) {
6697 self.updateStyle();
6698 } // call onSet callback
6699
6700
6701 p.onSet(self);
6702
6703 if (p.settingTriggersEvent) {
6704 self[p.triggerFnName](p.settingEvent);
6705 }
6706 }
6707 } // .data({ 'foo': 'bar' })
6708
6709 } else if (p.allowSetting && plainObject(name)) {
6710 // extend
6711 var obj = name;
6712 var k, v;
6713 var keys = Object.keys(obj);
6714 p.beforeSet(self, obj);
6715
6716 for (var _i = 0; _i < keys.length; _i++) {
6717 k = keys[_i];
6718 v = obj[k];
6719
6720 var _valid = !p.immutableKeys[k];
6721
6722 if (_valid) {
6723 for (var j = 0; j < all.length; j++) {
6724 var _ele = all[j];
6725
6726 if (p.canSet(_ele)) {
6727 _ele._private[p.field][k] = v;
6728 }
6729 }
6730 }
6731 } // update mappers if asked
6732
6733
6734 if (p.updateStyle) {
6735 self.updateStyle();
6736 } // call onSet callback
6737
6738
6739 p.onSet(self);
6740
6741 if (p.settingTriggersEvent) {
6742 self[p.triggerFnName](p.settingEvent);
6743 } // .data(function(){ ... })
6744
6745 } else if (p.allowBinding && fn$6(name)) {
6746 // bind to event
6747 var fn = name;
6748 self.on(p.bindingEvent, fn); // .data()
6749 } else if (p.allowGetting && name === undefined) {
6750 // get whole object
6751 var _ret;
6752
6753 if (single) {
6754 p.beforeGet(single);
6755 _ret = single._private[p.field];
6756 }
6757
6758 return _ret;
6759 }
6760
6761 return self; // maintain chainability
6762 }; // function
6763 },
6764 // data
6765 // remove data field
6766 removeData: function removeData(params) {
6767 var defaults = {
6768 field: 'data',
6769 event: 'data',
6770 triggerFnName: 'trigger',
6771 triggerEvent: false,
6772 immutableKeys: {} // key => true if immutable
6773
6774 };
6775 params = extend({}, defaults, params);
6776 return function removeDataImpl(names) {
6777 var p = params;
6778 var self = this;
6779 var selfIsArrayLike = self.length !== undefined;
6780 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6781 // .removeData('foo bar')
6782
6783 if (string(names)) {
6784 // then get the list of keys, and delete them
6785 var keys = names.split(/\s+/);
6786 var l = keys.length;
6787
6788 for (var i = 0; i < l; i++) {
6789 // delete each non-empty key
6790 var key = keys[i];
6791
6792 if (emptyString(key)) {
6793 continue;
6794 }
6795
6796 var valid = !p.immutableKeys[key]; // not valid if immutable
6797
6798 if (valid) {
6799 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6800 all[i_a]._private[p.field][key] = undefined;
6801 }
6802 }
6803 }
6804
6805 if (p.triggerEvent) {
6806 self[p.triggerFnName](p.event);
6807 } // .removeData()
6808
6809 } else if (names === undefined) {
6810 // then delete all keys
6811 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6812 var _privateFields = all[_i_a]._private[p.field];
6813
6814 var _keys = Object.keys(_privateFields);
6815
6816 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6817 var _key = _keys[_i2];
6818 var validKeyToDelete = !p.immutableKeys[_key];
6819
6820 if (validKeyToDelete) {
6821 _privateFields[_key] = undefined;
6822 }
6823 }
6824 }
6825
6826 if (p.triggerEvent) {
6827 self[p.triggerFnName](p.event);
6828 }
6829 }
6830
6831 return self; // maintain chaining
6832 }; // function
6833 } // removeData
6834
6835}; // define
6836
6837var define$1 = {
6838 eventAliasesOn: function eventAliasesOn(proto) {
6839 var p = proto;
6840 p.addListener = p.listen = p.bind = p.on;
6841 p.unlisten = p.unbind = p.off = p.removeListener;
6842 p.trigger = p.emit; // this is just a wrapper alias of .on()
6843
6844 p.pon = p.promiseOn = function (events, selector) {
6845 var self = this;
6846 var args = Array.prototype.slice.call(arguments, 0);
6847 return new Promise$1(function (resolve, reject) {
6848 var callback = function callback(e) {
6849 self.off.apply(self, offArgs);
6850 resolve(e);
6851 };
6852
6853 var onArgs = args.concat([callback]);
6854 var offArgs = onArgs.concat([]);
6855 self.on.apply(self, onArgs);
6856 });
6857 };
6858 }
6859}; // define
6860
6861// use this module to cherry pick functions into your prototype
6862var define = {};
6863[define$3, define$2, define$1].forEach(function (m) {
6864 extend(define, m);
6865});
6866
6867var elesfn$i = {
6868 animate: define.animate(),
6869 animation: define.animation(),
6870 animated: define.animated(),
6871 clearQueue: define.clearQueue(),
6872 delay: define.delay(),
6873 delayAnimation: define.delayAnimation(),
6874 stop: define.stop()
6875};
6876
6877var elesfn$h = {
6878 classes: function classes(_classes) {
6879 var self = this;
6880
6881 if (_classes === undefined) {
6882 var ret = [];
6883
6884 self[0]._private.classes.forEach(function (cls) {
6885 return ret.push(cls);
6886 });
6887
6888 return ret;
6889 } else if (!array(_classes)) {
6890 // extract classes from string
6891 _classes = (_classes || '').match(/\S+/g) || [];
6892 }
6893
6894 var changed = [];
6895 var classesSet = new Set$1(_classes); // check and update each ele
6896
6897 for (var j = 0; j < self.length; j++) {
6898 var ele = self[j];
6899 var _p = ele._private;
6900 var eleClasses = _p.classes;
6901 var changedEle = false; // check if ele has all of the passed classes
6902
6903 for (var i = 0; i < _classes.length; i++) {
6904 var cls = _classes[i];
6905 var eleHasClass = eleClasses.has(cls);
6906
6907 if (!eleHasClass) {
6908 changedEle = true;
6909 break;
6910 }
6911 } // check if ele has classes outside of those passed
6912
6913
6914 if (!changedEle) {
6915 changedEle = eleClasses.size !== _classes.length;
6916 }
6917
6918 if (changedEle) {
6919 _p.classes = classesSet;
6920 changed.push(ele);
6921 }
6922 } // trigger update style on those eles that had class changes
6923
6924
6925 if (changed.length > 0) {
6926 this.spawn(changed).updateStyle().emit('class');
6927 }
6928
6929 return self;
6930 },
6931 addClass: function addClass(classes) {
6932 return this.toggleClass(classes, true);
6933 },
6934 hasClass: function hasClass(className) {
6935 var ele = this[0];
6936 return ele != null && ele._private.classes.has(className);
6937 },
6938 toggleClass: function toggleClass(classes, toggle) {
6939 if (!array(classes)) {
6940 // extract classes from string
6941 classes = classes.match(/\S+/g) || [];
6942 }
6943
6944 var self = this;
6945 var toggleUndefd = toggle === undefined;
6946 var changed = []; // eles who had classes changed
6947
6948 for (var i = 0, il = self.length; i < il; i++) {
6949 var ele = self[i];
6950 var eleClasses = ele._private.classes;
6951 var changedEle = false;
6952
6953 for (var j = 0; j < classes.length; j++) {
6954 var cls = classes[j];
6955 var hasClass = eleClasses.has(cls);
6956 var changedNow = false;
6957
6958 if (toggle || toggleUndefd && !hasClass) {
6959 eleClasses.add(cls);
6960 changedNow = true;
6961 } else if (!toggle || toggleUndefd && hasClass) {
6962 eleClasses["delete"](cls);
6963 changedNow = true;
6964 }
6965
6966 if (!changedEle && changedNow) {
6967 changed.push(ele);
6968 changedEle = true;
6969 }
6970 } // for j classes
6971
6972 } // for i eles
6973 // trigger update style on those eles that had class changes
6974
6975
6976 if (changed.length > 0) {
6977 this.spawn(changed).updateStyle().emit('class');
6978 }
6979
6980 return self;
6981 },
6982 removeClass: function removeClass(classes) {
6983 return this.toggleClass(classes, false);
6984 },
6985 flashClass: function flashClass(classes, duration) {
6986 var self = this;
6987
6988 if (duration == null) {
6989 duration = 250;
6990 } else if (duration === 0) {
6991 return self; // nothing to do really
6992 }
6993
6994 self.addClass(classes);
6995 setTimeout(function () {
6996 self.removeClass(classes);
6997 }, duration);
6998 return self;
6999 }
7000};
7001elesfn$h.className = elesfn$h.classNames = elesfn$h.classes;
7002
7003var tokens = {
7004 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
7005 // chars we need to escape in let names, etc
7006 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
7007 // binary comparison op (used in data selectors)
7008 boolOp: '\\?|\\!|\\^',
7009 // boolean (unary) operators (used in data selectors)
7010 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
7011 // string literals (used in data selectors) -- doublequotes | singlequotes
7012 number: number,
7013 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
7014 meta: 'degree|indegree|outdegree',
7015 // allowed metadata fields (i.e. allowed functions to use from Collection)
7016 separator: '\\s*,\\s*',
7017 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
7018 descendant: '\\s+',
7019 child: '\\s+>\\s+',
7020 subject: '\\$',
7021 group: 'node|edge|\\*',
7022 directedEdge: '\\s+->\\s+',
7023 undirectedEdge: '\\s+<->\\s+'
7024};
7025tokens.variable = '(?:[\\w-.]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name can have letters, numbers, dashes, and periods
7026
7027tokens.className = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a class name has the same rules as a variable except it can't have a '.' in the name
7028
7029tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
7030
7031tokens.id = tokens.variable; // an element id (follows variable conventions)
7032
7033(function () {
7034 var ops, op, i; // add @ variants to comparatorOp
7035
7036 ops = tokens.comparatorOp.split('|');
7037
7038 for (i = 0; i < ops.length; i++) {
7039 op = ops[i];
7040 tokens.comparatorOp += '|@' + op;
7041 } // add ! variants to comparatorOp
7042
7043
7044 ops = tokens.comparatorOp.split('|');
7045
7046 for (i = 0; i < ops.length; i++) {
7047 op = ops[i];
7048
7049 if (op.indexOf('!') >= 0) {
7050 continue;
7051 } // skip ops that explicitly contain !
7052
7053
7054 if (op === '=') {
7055 continue;
7056 } // skip = b/c != is explicitly defined
7057
7058
7059 tokens.comparatorOp += '|\\!' + op;
7060 }
7061})();
7062
7063/**
7064 * Make a new query object
7065 *
7066 * @prop type {Type} The type enum (int) of the query
7067 * @prop checks List of checks to make against an ele to test for a match
7068 */
7069var newQuery = function newQuery() {
7070 return {
7071 checks: []
7072 };
7073};
7074
7075/**
7076 * A check type enum-like object. Uses integer values for fast match() lookup.
7077 * The ordering does not matter as long as the ints are unique.
7078 */
7079var Type = {
7080 /** E.g. node */
7081 GROUP: 0,
7082
7083 /** A collection of elements */
7084 COLLECTION: 1,
7085
7086 /** A filter(ele) function */
7087 FILTER: 2,
7088
7089 /** E.g. [foo > 1] */
7090 DATA_COMPARE: 3,
7091
7092 /** E.g. [foo] */
7093 DATA_EXIST: 4,
7094
7095 /** E.g. [?foo] */
7096 DATA_BOOL: 5,
7097
7098 /** E.g. [[degree > 2]] */
7099 META_COMPARE: 6,
7100
7101 /** E.g. :selected */
7102 STATE: 7,
7103
7104 /** E.g. #foo */
7105 ID: 8,
7106
7107 /** E.g. .foo */
7108 CLASS: 9,
7109
7110 /** E.g. #foo <-> #bar */
7111 UNDIRECTED_EDGE: 10,
7112
7113 /** E.g. #foo -> #bar */
7114 DIRECTED_EDGE: 11,
7115
7116 /** E.g. $#foo -> #bar */
7117 NODE_SOURCE: 12,
7118
7119 /** E.g. #foo -> $#bar */
7120 NODE_TARGET: 13,
7121
7122 /** E.g. $#foo <-> #bar */
7123 NODE_NEIGHBOR: 14,
7124
7125 /** E.g. #foo > #bar */
7126 CHILD: 15,
7127
7128 /** E.g. #foo #bar */
7129 DESCENDANT: 16,
7130
7131 /** E.g. $#foo > #bar */
7132 PARENT: 17,
7133
7134 /** E.g. $#foo #bar */
7135 ANCESTOR: 18,
7136
7137 /** E.g. #foo > $bar > #baz */
7138 COMPOUND_SPLIT: 19,
7139
7140 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7141 TRUE: 20
7142};
7143
7144var stateSelectors = [{
7145 selector: ':selected',
7146 matches: function matches(ele) {
7147 return ele.selected();
7148 }
7149}, {
7150 selector: ':unselected',
7151 matches: function matches(ele) {
7152 return !ele.selected();
7153 }
7154}, {
7155 selector: ':selectable',
7156 matches: function matches(ele) {
7157 return ele.selectable();
7158 }
7159}, {
7160 selector: ':unselectable',
7161 matches: function matches(ele) {
7162 return !ele.selectable();
7163 }
7164}, {
7165 selector: ':locked',
7166 matches: function matches(ele) {
7167 return ele.locked();
7168 }
7169}, {
7170 selector: ':unlocked',
7171 matches: function matches(ele) {
7172 return !ele.locked();
7173 }
7174}, {
7175 selector: ':visible',
7176 matches: function matches(ele) {
7177 return ele.visible();
7178 }
7179}, {
7180 selector: ':hidden',
7181 matches: function matches(ele) {
7182 return !ele.visible();
7183 }
7184}, {
7185 selector: ':transparent',
7186 matches: function matches(ele) {
7187 return ele.transparent();
7188 }
7189}, {
7190 selector: ':grabbed',
7191 matches: function matches(ele) {
7192 return ele.grabbed();
7193 }
7194}, {
7195 selector: ':free',
7196 matches: function matches(ele) {
7197 return !ele.grabbed();
7198 }
7199}, {
7200 selector: ':removed',
7201 matches: function matches(ele) {
7202 return ele.removed();
7203 }
7204}, {
7205 selector: ':inside',
7206 matches: function matches(ele) {
7207 return !ele.removed();
7208 }
7209}, {
7210 selector: ':grabbable',
7211 matches: function matches(ele) {
7212 return ele.grabbable();
7213 }
7214}, {
7215 selector: ':ungrabbable',
7216 matches: function matches(ele) {
7217 return !ele.grabbable();
7218 }
7219}, {
7220 selector: ':animated',
7221 matches: function matches(ele) {
7222 return ele.animated();
7223 }
7224}, {
7225 selector: ':unanimated',
7226 matches: function matches(ele) {
7227 return !ele.animated();
7228 }
7229}, {
7230 selector: ':parent',
7231 matches: function matches(ele) {
7232 return ele.isParent();
7233 }
7234}, {
7235 selector: ':childless',
7236 matches: function matches(ele) {
7237 return ele.isChildless();
7238 }
7239}, {
7240 selector: ':child',
7241 matches: function matches(ele) {
7242 return ele.isChild();
7243 }
7244}, {
7245 selector: ':orphan',
7246 matches: function matches(ele) {
7247 return ele.isOrphan();
7248 }
7249}, {
7250 selector: ':nonorphan',
7251 matches: function matches(ele) {
7252 return ele.isChild();
7253 }
7254}, {
7255 selector: ':compound',
7256 matches: function matches(ele) {
7257 if (ele.isNode()) {
7258 return ele.isParent();
7259 } else {
7260 return ele.source().isParent() || ele.target().isParent();
7261 }
7262 }
7263}, {
7264 selector: ':loop',
7265 matches: function matches(ele) {
7266 return ele.isLoop();
7267 }
7268}, {
7269 selector: ':simple',
7270 matches: function matches(ele) {
7271 return ele.isSimple();
7272 }
7273}, {
7274 selector: ':active',
7275 matches: function matches(ele) {
7276 return ele.active();
7277 }
7278}, {
7279 selector: ':inactive',
7280 matches: function matches(ele) {
7281 return !ele.active();
7282 }
7283}, {
7284 selector: ':backgrounding',
7285 matches: function matches(ele) {
7286 return ele.backgrounding();
7287 }
7288}, {
7289 selector: ':nonbackgrounding',
7290 matches: function matches(ele) {
7291 return !ele.backgrounding();
7292 }
7293}].sort(function (a, b) {
7294 // n.b. selectors that are starting substrings of others must have the longer ones first
7295 return descending(a.selector, b.selector);
7296});
7297
7298var lookup = function () {
7299 var selToFn = {};
7300 var s;
7301
7302 for (var i = 0; i < stateSelectors.length; i++) {
7303 s = stateSelectors[i];
7304 selToFn[s.selector] = s.matches;
7305 }
7306
7307 return selToFn;
7308}();
7309
7310var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7311 return lookup[sel](ele);
7312};
7313var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7314 return s.selector;
7315}).join('|') + ')';
7316
7317// so that values get compared properly in Selector.filter()
7318
7319var cleanMetaChars = function cleanMetaChars(str) {
7320 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7321 return $1;
7322 });
7323};
7324
7325var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7326 selector[selector.length - 1] = replacementQuery;
7327}; // NOTE: add new expression syntax here to have it recognised by the parser;
7328// - a query contains all adjacent (i.e. no separator in between) expressions;
7329// - the current query is stored in selector[i]
7330// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7331
7332
7333var exprs = [{
7334 name: 'group',
7335 // just used for identifying when debugging
7336 query: true,
7337 regex: '(' + tokens.group + ')',
7338 populate: function populate(selector, query, _ref) {
7339 var _ref2 = _slicedToArray(_ref, 1),
7340 group = _ref2[0];
7341
7342 query.checks.push({
7343 type: Type.GROUP,
7344 value: group === '*' ? group : group + 's'
7345 });
7346 }
7347}, {
7348 name: 'state',
7349 query: true,
7350 regex: stateSelectorRegex,
7351 populate: function populate(selector, query, _ref3) {
7352 var _ref4 = _slicedToArray(_ref3, 1),
7353 state = _ref4[0];
7354
7355 query.checks.push({
7356 type: Type.STATE,
7357 value: state
7358 });
7359 }
7360}, {
7361 name: 'id',
7362 query: true,
7363 regex: '\\#(' + tokens.id + ')',
7364 populate: function populate(selector, query, _ref5) {
7365 var _ref6 = _slicedToArray(_ref5, 1),
7366 id = _ref6[0];
7367
7368 query.checks.push({
7369 type: Type.ID,
7370 value: cleanMetaChars(id)
7371 });
7372 }
7373}, {
7374 name: 'className',
7375 query: true,
7376 regex: '\\.(' + tokens.className + ')',
7377 populate: function populate(selector, query, _ref7) {
7378 var _ref8 = _slicedToArray(_ref7, 1),
7379 className = _ref8[0];
7380
7381 query.checks.push({
7382 type: Type.CLASS,
7383 value: cleanMetaChars(className)
7384 });
7385 }
7386}, {
7387 name: 'dataExists',
7388 query: true,
7389 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7390 populate: function populate(selector, query, _ref9) {
7391 var _ref10 = _slicedToArray(_ref9, 1),
7392 variable = _ref10[0];
7393
7394 query.checks.push({
7395 type: Type.DATA_EXIST,
7396 field: cleanMetaChars(variable)
7397 });
7398 }
7399}, {
7400 name: 'dataCompare',
7401 query: true,
7402 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7403 populate: function populate(selector, query, _ref11) {
7404 var _ref12 = _slicedToArray(_ref11, 3),
7405 variable = _ref12[0],
7406 comparatorOp = _ref12[1],
7407 value = _ref12[2];
7408
7409 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7410
7411 if (valueIsString) {
7412 value = value.substring(1, value.length - 1);
7413 } else {
7414 value = parseFloat(value);
7415 }
7416
7417 query.checks.push({
7418 type: Type.DATA_COMPARE,
7419 field: cleanMetaChars(variable),
7420 operator: comparatorOp,
7421 value: value
7422 });
7423 }
7424}, {
7425 name: 'dataBool',
7426 query: true,
7427 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7428 populate: function populate(selector, query, _ref13) {
7429 var _ref14 = _slicedToArray(_ref13, 2),
7430 boolOp = _ref14[0],
7431 variable = _ref14[1];
7432
7433 query.checks.push({
7434 type: Type.DATA_BOOL,
7435 field: cleanMetaChars(variable),
7436 operator: boolOp
7437 });
7438 }
7439}, {
7440 name: 'metaCompare',
7441 query: true,
7442 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7443 populate: function populate(selector, query, _ref15) {
7444 var _ref16 = _slicedToArray(_ref15, 3),
7445 meta = _ref16[0],
7446 comparatorOp = _ref16[1],
7447 number = _ref16[2];
7448
7449 query.checks.push({
7450 type: Type.META_COMPARE,
7451 field: cleanMetaChars(meta),
7452 operator: comparatorOp,
7453 value: parseFloat(number)
7454 });
7455 }
7456}, {
7457 name: 'nextQuery',
7458 separator: true,
7459 regex: tokens.separator,
7460 populate: function populate(selector, query) {
7461 var currentSubject = selector.currentSubject;
7462 var edgeCount = selector.edgeCount;
7463 var compoundCount = selector.compoundCount;
7464 var lastQ = selector[selector.length - 1];
7465
7466 if (currentSubject != null) {
7467 lastQ.subject = currentSubject;
7468 selector.currentSubject = null;
7469 }
7470
7471 lastQ.edgeCount = edgeCount;
7472 lastQ.compoundCount = compoundCount;
7473 selector.edgeCount = 0;
7474 selector.compoundCount = 0; // go on to next query
7475
7476 var nextQuery = selector[selector.length++] = newQuery();
7477 return nextQuery; // this is the new query to be filled by the following exprs
7478 }
7479}, {
7480 name: 'directedEdge',
7481 separator: true,
7482 regex: tokens.directedEdge,
7483 populate: function populate(selector, query) {
7484 if (selector.currentSubject == null) {
7485 // undirected edge
7486 var edgeQuery = newQuery();
7487 var source = query;
7488 var target = newQuery();
7489 edgeQuery.checks.push({
7490 type: Type.DIRECTED_EDGE,
7491 source: source,
7492 target: target
7493 }); // the query in the selector should be the edge rather than the source
7494
7495 replaceLastQuery(selector, query, edgeQuery);
7496 selector.edgeCount++; // we're now populating the target query with expressions that follow
7497
7498 return target;
7499 } else {
7500 // source/target
7501 var srcTgtQ = newQuery();
7502 var _source = query;
7503
7504 var _target = newQuery();
7505
7506 srcTgtQ.checks.push({
7507 type: Type.NODE_SOURCE,
7508 source: _source,
7509 target: _target
7510 }); // the query in the selector should be the neighbourhood rather than the node
7511
7512 replaceLastQuery(selector, query, srcTgtQ);
7513 selector.edgeCount++;
7514 return _target; // now populating the target with the following expressions
7515 }
7516 }
7517}, {
7518 name: 'undirectedEdge',
7519 separator: true,
7520 regex: tokens.undirectedEdge,
7521 populate: function populate(selector, query) {
7522 if (selector.currentSubject == null) {
7523 // undirected edge
7524 var edgeQuery = newQuery();
7525 var source = query;
7526 var target = newQuery();
7527 edgeQuery.checks.push({
7528 type: Type.UNDIRECTED_EDGE,
7529 nodes: [source, target]
7530 }); // the query in the selector should be the edge rather than the source
7531
7532 replaceLastQuery(selector, query, edgeQuery);
7533 selector.edgeCount++; // we're now populating the target query with expressions that follow
7534
7535 return target;
7536 } else {
7537 // neighbourhood
7538 var nhoodQ = newQuery();
7539 var node = query;
7540 var neighbor = newQuery();
7541 nhoodQ.checks.push({
7542 type: Type.NODE_NEIGHBOR,
7543 node: node,
7544 neighbor: neighbor
7545 }); // the query in the selector should be the neighbourhood rather than the node
7546
7547 replaceLastQuery(selector, query, nhoodQ);
7548 return neighbor; // now populating the neighbor with following expressions
7549 }
7550 }
7551}, {
7552 name: 'child',
7553 separator: true,
7554 regex: tokens.child,
7555 populate: function populate(selector, query) {
7556 if (selector.currentSubject == null) {
7557 // default: child query
7558 var parentChildQuery = newQuery();
7559 var child = newQuery();
7560 var parent = selector[selector.length - 1];
7561 parentChildQuery.checks.push({
7562 type: Type.CHILD,
7563 parent: parent,
7564 child: child
7565 }); // the query in the selector should be the '>' itself
7566
7567 replaceLastQuery(selector, query, parentChildQuery);
7568 selector.compoundCount++; // we're now populating the child query with expressions that follow
7569
7570 return child;
7571 } else if (selector.currentSubject === query) {
7572 // compound split query
7573 var compound = newQuery();
7574 var left = selector[selector.length - 1];
7575 var right = newQuery();
7576 var subject = newQuery();
7577
7578 var _child = newQuery();
7579
7580 var _parent = newQuery(); // set up the root compound q
7581
7582
7583 compound.checks.push({
7584 type: Type.COMPOUND_SPLIT,
7585 left: left,
7586 right: right,
7587 subject: subject
7588 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7589
7590 subject.checks = query.checks; // take the checks from the left
7591
7592 query.checks = [{
7593 type: Type.TRUE
7594 }]; // checks under left refs the subject implicitly
7595 // set up the right q
7596
7597 _parent.checks.push({
7598 type: Type.TRUE
7599 }); // parent implicitly refs the subject
7600
7601
7602 right.checks.push({
7603 type: Type.PARENT,
7604 // type is swapped on right side queries
7605 parent: _parent,
7606 child: _child // empty for now
7607
7608 });
7609 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7610
7611 selector.currentSubject = subject;
7612 selector.compoundCount++;
7613 return _child; // now populating the right side's child
7614 } else {
7615 // parent query
7616 // info for parent query
7617 var _parent2 = newQuery();
7618
7619 var _child2 = newQuery();
7620
7621 var pcQChecks = [{
7622 type: Type.PARENT,
7623 parent: _parent2,
7624 child: _child2
7625 }]; // the parent-child query takes the place of the query previously being populated
7626
7627 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7628
7629 query.checks = pcQChecks; // pc query takes over
7630
7631 selector.compoundCount++;
7632 return _child2; // we're now populating the child
7633 }
7634 }
7635}, {
7636 name: 'descendant',
7637 separator: true,
7638 regex: tokens.descendant,
7639 populate: function populate(selector, query) {
7640 if (selector.currentSubject == null) {
7641 // default: descendant query
7642 var ancChQuery = newQuery();
7643 var descendant = newQuery();
7644 var ancestor = selector[selector.length - 1];
7645 ancChQuery.checks.push({
7646 type: Type.DESCENDANT,
7647 ancestor: ancestor,
7648 descendant: descendant
7649 }); // the query in the selector should be the '>' itself
7650
7651 replaceLastQuery(selector, query, ancChQuery);
7652 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7653
7654 return descendant;
7655 } else if (selector.currentSubject === query) {
7656 // compound split query
7657 var compound = newQuery();
7658 var left = selector[selector.length - 1];
7659 var right = newQuery();
7660 var subject = newQuery();
7661
7662 var _descendant = newQuery();
7663
7664 var _ancestor = newQuery(); // set up the root compound q
7665
7666
7667 compound.checks.push({
7668 type: Type.COMPOUND_SPLIT,
7669 left: left,
7670 right: right,
7671 subject: subject
7672 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7673
7674 subject.checks = query.checks; // take the checks from the left
7675
7676 query.checks = [{
7677 type: Type.TRUE
7678 }]; // checks under left refs the subject implicitly
7679 // set up the right q
7680
7681 _ancestor.checks.push({
7682 type: Type.TRUE
7683 }); // ancestor implicitly refs the subject
7684
7685
7686 right.checks.push({
7687 type: Type.ANCESTOR,
7688 // type is swapped on right side queries
7689 ancestor: _ancestor,
7690 descendant: _descendant // empty for now
7691
7692 });
7693 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7694
7695 selector.currentSubject = subject;
7696 selector.compoundCount++;
7697 return _descendant; // now populating the right side's descendant
7698 } else {
7699 // ancestor query
7700 // info for parent query
7701 var _ancestor2 = newQuery();
7702
7703 var _descendant2 = newQuery();
7704
7705 var adQChecks = [{
7706 type: Type.ANCESTOR,
7707 ancestor: _ancestor2,
7708 descendant: _descendant2
7709 }]; // the parent-child query takes the place of the query previously being populated
7710
7711 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7712
7713 query.checks = adQChecks; // pc query takes over
7714
7715 selector.compoundCount++;
7716 return _descendant2; // we're now populating the child
7717 }
7718 }
7719}, {
7720 name: 'subject',
7721 modifier: true,
7722 regex: tokens.subject,
7723 populate: function populate(selector, query) {
7724 if (selector.currentSubject != null && selector.currentSubject !== query) {
7725 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7726 return false;
7727 }
7728
7729 selector.currentSubject = query;
7730 var topQ = selector[selector.length - 1];
7731 var topChk = topQ.checks[0];
7732 var topType = topChk == null ? null : topChk.type;
7733
7734 if (topType === Type.DIRECTED_EDGE) {
7735 // directed edge with subject on the target
7736 // change to target node check
7737 topChk.type = Type.NODE_TARGET;
7738 } else if (topType === Type.UNDIRECTED_EDGE) {
7739 // undirected edge with subject on the second node
7740 // change to neighbor check
7741 topChk.type = Type.NODE_NEIGHBOR;
7742 topChk.node = topChk.nodes[1]; // second node is subject
7743
7744 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7745
7746 topChk.nodes = null;
7747 }
7748 }
7749}];
7750exprs.forEach(function (e) {
7751 return e.regexObj = new RegExp('^' + e.regex);
7752});
7753
7754/**
7755 * Of all the expressions, find the first match in the remaining text.
7756 * @param {string} remaining The remaining text to parse
7757 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7758 */
7759
7760var consumeExpr = function consumeExpr(remaining) {
7761 var expr;
7762 var match;
7763 var name;
7764
7765 for (var j = 0; j < exprs.length; j++) {
7766 var e = exprs[j];
7767 var n = e.name;
7768 var m = remaining.match(e.regexObj);
7769
7770 if (m != null) {
7771 match = m;
7772 expr = e;
7773 name = n;
7774 var consumed = m[0];
7775 remaining = remaining.substring(consumed.length);
7776 break; // we've consumed one expr, so we can return now
7777 }
7778 }
7779
7780 return {
7781 expr: expr,
7782 match: match,
7783 name: name,
7784 remaining: remaining
7785 };
7786};
7787/**
7788 * Consume all the leading whitespace
7789 * @param {string} remaining The text to consume
7790 * @returns The text with the leading whitespace removed
7791 */
7792
7793
7794var consumeWhitespace = function consumeWhitespace(remaining) {
7795 var match = remaining.match(/^\s+/);
7796
7797 if (match) {
7798 var consumed = match[0];
7799 remaining = remaining.substring(consumed.length);
7800 }
7801
7802 return remaining;
7803};
7804/**
7805 * Parse the string and store the parsed representation in the Selector.
7806 * @param {string} selector The selector string
7807 * @returns `true` if the selector was successfully parsed, `false` otherwise
7808 */
7809
7810
7811var parse = function parse(selector) {
7812 var self = this;
7813 var remaining = self.inputText = selector;
7814 var currentQuery = self[0] = newQuery();
7815 self.length = 1;
7816 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7817
7818 for (;;) {
7819 var exprInfo = consumeExpr(remaining);
7820
7821 if (exprInfo.expr == null) {
7822 warn('The selector `' + selector + '`is invalid');
7823 return false;
7824 } else {
7825 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7826
7827 var ret = exprInfo.expr.populate(self, currentQuery, args);
7828
7829 if (ret === false) {
7830 return false; // exit if population failed
7831 } else if (ret != null) {
7832 currentQuery = ret; // change the current query to be filled if the expr specifies
7833 }
7834 }
7835
7836 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7837
7838 if (remaining.match(/^\s*$/)) {
7839 break;
7840 }
7841 }
7842
7843 var lastQ = self[self.length - 1];
7844
7845 if (self.currentSubject != null) {
7846 lastQ.subject = self.currentSubject;
7847 }
7848
7849 lastQ.edgeCount = self.edgeCount;
7850 lastQ.compoundCount = self.compoundCount;
7851
7852 for (var i = 0; i < self.length; i++) {
7853 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7854
7855 if (q.compoundCount > 0 && q.edgeCount > 0) {
7856 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7857 return false;
7858 }
7859
7860 if (q.edgeCount > 1) {
7861 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7862 return false;
7863 } else if (q.edgeCount === 1) {
7864 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.');
7865 }
7866 }
7867
7868 return true; // success
7869};
7870/**
7871 * Get the selector represented as a string. This value uses default formatting,
7872 * so things like spacing may differ from the input text passed to the constructor.
7873 * @returns {string} The selector string
7874 */
7875
7876
7877var toString = function toString() {
7878 if (this.toStringCache != null) {
7879 return this.toStringCache;
7880 }
7881
7882 var clean = function clean(obj) {
7883 if (obj == null) {
7884 return '';
7885 } else {
7886 return obj;
7887 }
7888 };
7889
7890 var cleanVal = function cleanVal(val) {
7891 if (string(val)) {
7892 return '"' + val + '"';
7893 } else {
7894 return clean(val);
7895 }
7896 };
7897
7898 var space = function space(val) {
7899 return ' ' + val + ' ';
7900 };
7901
7902 var checkToString = function checkToString(check, subject) {
7903 var type = check.type,
7904 value = check.value;
7905
7906 switch (type) {
7907 case Type.GROUP:
7908 {
7909 var group = clean(value);
7910 return group.substring(0, group.length - 1);
7911 }
7912
7913 case Type.DATA_COMPARE:
7914 {
7915 var field = check.field,
7916 operator = check.operator;
7917 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7918 }
7919
7920 case Type.DATA_BOOL:
7921 {
7922 var _operator = check.operator,
7923 _field = check.field;
7924 return '[' + clean(_operator) + _field + ']';
7925 }
7926
7927 case Type.DATA_EXIST:
7928 {
7929 var _field2 = check.field;
7930 return '[' + _field2 + ']';
7931 }
7932
7933 case Type.META_COMPARE:
7934 {
7935 var _operator2 = check.operator,
7936 _field3 = check.field;
7937 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7938 }
7939
7940 case Type.STATE:
7941 {
7942 return value;
7943 }
7944
7945 case Type.ID:
7946 {
7947 return '#' + value;
7948 }
7949
7950 case Type.CLASS:
7951 {
7952 return '.' + value;
7953 }
7954
7955 case Type.PARENT:
7956 case Type.CHILD:
7957 {
7958 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7959 }
7960
7961 case Type.ANCESTOR:
7962 case Type.DESCENDANT:
7963 {
7964 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7965 }
7966
7967 case Type.COMPOUND_SPLIT:
7968 {
7969 var lhs = queryToString(check.left, subject);
7970 var sub = queryToString(check.subject, subject);
7971 var rhs = queryToString(check.right, subject);
7972 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7973 }
7974
7975 case Type.TRUE:
7976 {
7977 return '';
7978 }
7979 }
7980 };
7981
7982 var queryToString = function queryToString(query, subject) {
7983 return query.checks.reduce(function (str, chk, i) {
7984 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7985 }, '');
7986 };
7987
7988 var str = '';
7989
7990 for (var i = 0; i < this.length; i++) {
7991 var query = this[i];
7992 str += queryToString(query, query.subject);
7993
7994 if (this.length > 1 && i < this.length - 1) {
7995 str += ', ';
7996 }
7997 }
7998
7999 this.toStringCache = str;
8000 return str;
8001};
8002var parse$1 = {
8003 parse: parse,
8004 toString: toString
8005};
8006
8007var valCmp = function valCmp(fieldVal, operator, value) {
8008 var matches;
8009 var isFieldStr = string(fieldVal);
8010 var isFieldNum = number$1(fieldVal);
8011 var isValStr = string(value);
8012 var fieldStr, valStr;
8013 var caseInsensitive = false;
8014 var notExpr = false;
8015 var isIneqCmp = false;
8016
8017 if (operator.indexOf('!') >= 0) {
8018 operator = operator.replace('!', '');
8019 notExpr = true;
8020 }
8021
8022 if (operator.indexOf('@') >= 0) {
8023 operator = operator.replace('@', '');
8024 caseInsensitive = true;
8025 }
8026
8027 if (isFieldStr || isValStr || caseInsensitive) {
8028 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
8029 valStr = '' + value;
8030 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
8031 // even if we're comparing numbers
8032
8033
8034 if (caseInsensitive) {
8035 fieldVal = fieldStr = fieldStr.toLowerCase();
8036 value = valStr = valStr.toLowerCase();
8037 }
8038
8039 switch (operator) {
8040 case '*=':
8041 matches = fieldStr.indexOf(valStr) >= 0;
8042 break;
8043
8044 case '$=':
8045 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
8046 break;
8047
8048 case '^=':
8049 matches = fieldStr.indexOf(valStr) === 0;
8050 break;
8051
8052 case '=':
8053 matches = fieldVal === value;
8054 break;
8055
8056 case '>':
8057 isIneqCmp = true;
8058 matches = fieldVal > value;
8059 break;
8060
8061 case '>=':
8062 isIneqCmp = true;
8063 matches = fieldVal >= value;
8064 break;
8065
8066 case '<':
8067 isIneqCmp = true;
8068 matches = fieldVal < value;
8069 break;
8070
8071 case '<=':
8072 isIneqCmp = true;
8073 matches = fieldVal <= value;
8074 break;
8075
8076 default:
8077 matches = false;
8078 break;
8079 } // apply the not op, but null vals for inequalities should always stay non-matching
8080
8081
8082 if (notExpr && (fieldVal != null || !isIneqCmp)) {
8083 matches = !matches;
8084 }
8085
8086 return matches;
8087};
8088var boolCmp = function boolCmp(fieldVal, operator) {
8089 switch (operator) {
8090 case '?':
8091 return fieldVal ? true : false;
8092
8093 case '!':
8094 return fieldVal ? false : true;
8095
8096 case '^':
8097 return fieldVal === undefined;
8098 }
8099};
8100var existCmp = function existCmp(fieldVal) {
8101 return fieldVal !== undefined;
8102};
8103var data$1 = function data(ele, field) {
8104 return ele.data(field);
8105};
8106var meta = function meta(ele, field) {
8107 return ele[field]();
8108};
8109
8110/** A lookup of `match(check, ele)` functions by `Type` int */
8111
8112var match = [];
8113/**
8114 * Returns whether the query matches for the element
8115 * @param query The `{ type, value, ... }` query object
8116 * @param ele The element to compare against
8117*/
8118
8119var matches$1 = function matches(query, ele) {
8120 return query.checks.every(function (chk) {
8121 return match[chk.type](chk, ele);
8122 });
8123};
8124
8125match[Type.GROUP] = function (check, ele) {
8126 var group = check.value;
8127 return group === '*' || group === ele.group();
8128};
8129
8130match[Type.STATE] = function (check, ele) {
8131 var stateSelector = check.value;
8132 return stateSelectorMatches(stateSelector, ele);
8133};
8134
8135match[Type.ID] = function (check, ele) {
8136 var id = check.value;
8137 return ele.id() === id;
8138};
8139
8140match[Type.CLASS] = function (check, ele) {
8141 var cls = check.value;
8142 return ele.hasClass(cls);
8143};
8144
8145match[Type.META_COMPARE] = function (check, ele) {
8146 var field = check.field,
8147 operator = check.operator,
8148 value = check.value;
8149 return valCmp(meta(ele, field), operator, value);
8150};
8151
8152match[Type.DATA_COMPARE] = function (check, ele) {
8153 var field = check.field,
8154 operator = check.operator,
8155 value = check.value;
8156 return valCmp(data$1(ele, field), operator, value);
8157};
8158
8159match[Type.DATA_BOOL] = function (check, ele) {
8160 var field = check.field,
8161 operator = check.operator;
8162 return boolCmp(data$1(ele, field), operator);
8163};
8164
8165match[Type.DATA_EXIST] = function (check, ele) {
8166 var field = check.field;
8167 check.operator;
8168 return existCmp(data$1(ele, field));
8169};
8170
8171match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8172 var qA = check.nodes[0];
8173 var qB = check.nodes[1];
8174 var src = ele.source();
8175 var tgt = ele.target();
8176 return matches$1(qA, src) && matches$1(qB, tgt) || matches$1(qB, src) && matches$1(qA, tgt);
8177};
8178
8179match[Type.NODE_NEIGHBOR] = function (check, ele) {
8180 return matches$1(check.node, ele) && ele.neighborhood().some(function (n) {
8181 return n.isNode() && matches$1(check.neighbor, n);
8182 });
8183};
8184
8185match[Type.DIRECTED_EDGE] = function (check, ele) {
8186 return matches$1(check.source, ele.source()) && matches$1(check.target, ele.target());
8187};
8188
8189match[Type.NODE_SOURCE] = function (check, ele) {
8190 return matches$1(check.source, ele) && ele.outgoers().some(function (n) {
8191 return n.isNode() && matches$1(check.target, n);
8192 });
8193};
8194
8195match[Type.NODE_TARGET] = function (check, ele) {
8196 return matches$1(check.target, ele) && ele.incomers().some(function (n) {
8197 return n.isNode() && matches$1(check.source, n);
8198 });
8199};
8200
8201match[Type.CHILD] = function (check, ele) {
8202 return matches$1(check.child, ele) && matches$1(check.parent, ele.parent());
8203};
8204
8205match[Type.PARENT] = function (check, ele) {
8206 return matches$1(check.parent, ele) && ele.children().some(function (c) {
8207 return matches$1(check.child, c);
8208 });
8209};
8210
8211match[Type.DESCENDANT] = function (check, ele) {
8212 return matches$1(check.descendant, ele) && ele.ancestors().some(function (a) {
8213 return matches$1(check.ancestor, a);
8214 });
8215};
8216
8217match[Type.ANCESTOR] = function (check, ele) {
8218 return matches$1(check.ancestor, ele) && ele.descendants().some(function (d) {
8219 return matches$1(check.descendant, d);
8220 });
8221};
8222
8223match[Type.COMPOUND_SPLIT] = function (check, ele) {
8224 return matches$1(check.subject, ele) && matches$1(check.left, ele) && matches$1(check.right, ele);
8225};
8226
8227match[Type.TRUE] = function () {
8228 return true;
8229};
8230
8231match[Type.COLLECTION] = function (check, ele) {
8232 var collection = check.value;
8233 return collection.has(ele);
8234};
8235
8236match[Type.FILTER] = function (check, ele) {
8237 var filter = check.value;
8238 return filter(ele);
8239};
8240
8241var filter = function filter(collection) {
8242 var self = this; // for 1 id #foo queries, just get the element
8243
8244 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8245 return collection.getElementById(self[0].checks[0].value).collection();
8246 }
8247
8248 var selectorFunction = function selectorFunction(element) {
8249 for (var j = 0; j < self.length; j++) {
8250 var query = self[j];
8251
8252 if (matches$1(query, element)) {
8253 return true;
8254 }
8255 }
8256
8257 return false;
8258 };
8259
8260 if (self.text() == null) {
8261 selectorFunction = function selectorFunction() {
8262 return true;
8263 };
8264 }
8265
8266 return collection.filter(selectorFunction);
8267}; // filter
8268// does selector match a single element?
8269
8270
8271var matches = function matches(ele) {
8272 var self = this;
8273
8274 for (var j = 0; j < self.length; j++) {
8275 var query = self[j];
8276
8277 if (matches$1(query, ele)) {
8278 return true;
8279 }
8280 }
8281
8282 return false;
8283}; // matches
8284
8285
8286var matching = {
8287 matches: matches,
8288 filter: filter
8289};
8290
8291var Selector = function Selector(selector) {
8292 this.inputText = selector;
8293 this.currentSubject = null;
8294 this.compoundCount = 0;
8295 this.edgeCount = 0;
8296 this.length = 0;
8297
8298 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8299 this.addQuery({
8300 checks: [{
8301 type: Type.COLLECTION,
8302 value: selector.collection()
8303 }]
8304 });
8305 } else if (fn$6(selector)) {
8306 this.addQuery({
8307 checks: [{
8308 type: Type.FILTER,
8309 value: selector
8310 }]
8311 });
8312 } else if (string(selector)) {
8313 if (!this.parse(selector)) {
8314 this.invalid = true;
8315 }
8316 } else {
8317 error('A selector must be created from a string; found ');
8318 }
8319};
8320
8321var selfn = Selector.prototype;
8322[parse$1, matching].forEach(function (p) {
8323 return extend(selfn, p);
8324});
8325
8326selfn.text = function () {
8327 return this.inputText;
8328};
8329
8330selfn.size = function () {
8331 return this.length;
8332};
8333
8334selfn.eq = function (i) {
8335 return this[i];
8336};
8337
8338selfn.sameText = function (otherSel) {
8339 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8340};
8341
8342selfn.addQuery = function (q) {
8343 this[this.length++] = q;
8344};
8345
8346selfn.selector = selfn.toString;
8347
8348var elesfn$g = {
8349 allAre: function allAre(selector) {
8350 var selObj = new Selector(selector);
8351 return this.every(function (ele) {
8352 return selObj.matches(ele);
8353 });
8354 },
8355 is: function is(selector) {
8356 var selObj = new Selector(selector);
8357 return this.some(function (ele) {
8358 return selObj.matches(ele);
8359 });
8360 },
8361 some: function some(fn, thisArg) {
8362 for (var i = 0; i < this.length; i++) {
8363 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8364
8365 if (ret) {
8366 return true;
8367 }
8368 }
8369
8370 return false;
8371 },
8372 every: function every(fn, thisArg) {
8373 for (var i = 0; i < this.length; i++) {
8374 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8375
8376 if (!ret) {
8377 return false;
8378 }
8379 }
8380
8381 return true;
8382 },
8383 same: function same(collection) {
8384 // cheap collection ref check
8385 if (this === collection) {
8386 return true;
8387 }
8388
8389 collection = this.cy().collection(collection);
8390 var thisLength = this.length;
8391 var collectionLength = collection.length; // cheap length check
8392
8393 if (thisLength !== collectionLength) {
8394 return false;
8395 } // cheap element ref check
8396
8397
8398 if (thisLength === 1) {
8399 return this[0] === collection[0];
8400 }
8401
8402 return this.every(function (ele) {
8403 return collection.hasElementWithId(ele.id());
8404 });
8405 },
8406 anySame: function anySame(collection) {
8407 collection = this.cy().collection(collection);
8408 return this.some(function (ele) {
8409 return collection.hasElementWithId(ele.id());
8410 });
8411 },
8412 allAreNeighbors: function allAreNeighbors(collection) {
8413 collection = this.cy().collection(collection);
8414 var nhood = this.neighborhood();
8415 return collection.every(function (ele) {
8416 return nhood.hasElementWithId(ele.id());
8417 });
8418 },
8419 contains: function contains(collection) {
8420 collection = this.cy().collection(collection);
8421 var self = this;
8422 return collection.every(function (ele) {
8423 return self.hasElementWithId(ele.id());
8424 });
8425 }
8426};
8427elesfn$g.allAreNeighbours = elesfn$g.allAreNeighbors;
8428elesfn$g.has = elesfn$g.contains;
8429elesfn$g.equal = elesfn$g.equals = elesfn$g.same;
8430
8431var cache = function cache(fn, name) {
8432 return function traversalCache(arg1, arg2, arg3, arg4) {
8433 var selectorOrEles = arg1;
8434 var eles = this;
8435 var key;
8436
8437 if (selectorOrEles == null) {
8438 key = '';
8439 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8440 key = selectorOrEles.id();
8441 }
8442
8443 if (eles.length === 1 && key) {
8444 var _p = eles[0]._private;
8445 var tch = _p.traversalCache = _p.traversalCache || {};
8446 var ch = tch[name] = tch[name] || [];
8447 var hash = hashString(key);
8448 var cacheHit = ch[hash];
8449
8450 if (cacheHit) {
8451 return cacheHit;
8452 } else {
8453 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8454 }
8455 } else {
8456 return fn.call(eles, arg1, arg2, arg3, arg4);
8457 }
8458 };
8459};
8460
8461var elesfn$f = {
8462 parent: function parent(selector) {
8463 var parents = []; // optimisation for single ele call
8464
8465 if (this.length === 1) {
8466 var parent = this[0]._private.parent;
8467
8468 if (parent) {
8469 return parent;
8470 }
8471 }
8472
8473 for (var i = 0; i < this.length; i++) {
8474 var ele = this[i];
8475 var _parent = ele._private.parent;
8476
8477 if (_parent) {
8478 parents.push(_parent);
8479 }
8480 }
8481
8482 return this.spawn(parents, true).filter(selector);
8483 },
8484 parents: function parents(selector) {
8485 var parents = [];
8486 var eles = this.parent();
8487
8488 while (eles.nonempty()) {
8489 for (var i = 0; i < eles.length; i++) {
8490 var ele = eles[i];
8491 parents.push(ele);
8492 }
8493
8494 eles = eles.parent();
8495 }
8496
8497 return this.spawn(parents, true).filter(selector);
8498 },
8499 commonAncestors: function commonAncestors(selector) {
8500 var ancestors;
8501
8502 for (var i = 0; i < this.length; i++) {
8503 var ele = this[i];
8504 var parents = ele.parents();
8505 ancestors = ancestors || parents;
8506 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8507 }
8508
8509 return ancestors.filter(selector);
8510 },
8511 orphans: function orphans(selector) {
8512 return this.stdFilter(function (ele) {
8513 return ele.isOrphan();
8514 }).filter(selector);
8515 },
8516 nonorphans: function nonorphans(selector) {
8517 return this.stdFilter(function (ele) {
8518 return ele.isChild();
8519 }).filter(selector);
8520 },
8521 children: cache(function (selector) {
8522 var children = [];
8523
8524 for (var i = 0; i < this.length; i++) {
8525 var ele = this[i];
8526 var eleChildren = ele._private.children;
8527
8528 for (var j = 0; j < eleChildren.length; j++) {
8529 children.push(eleChildren[j]);
8530 }
8531 }
8532
8533 return this.spawn(children, true).filter(selector);
8534 }, 'children'),
8535 siblings: function siblings(selector) {
8536 return this.parent().children().not(this).filter(selector);
8537 },
8538 isParent: function isParent() {
8539 var ele = this[0];
8540
8541 if (ele) {
8542 return ele.isNode() && ele._private.children.length !== 0;
8543 }
8544 },
8545 isChildless: function isChildless() {
8546 var ele = this[0];
8547
8548 if (ele) {
8549 return ele.isNode() && ele._private.children.length === 0;
8550 }
8551 },
8552 isChild: function isChild() {
8553 var ele = this[0];
8554
8555 if (ele) {
8556 return ele.isNode() && ele._private.parent != null;
8557 }
8558 },
8559 isOrphan: function isOrphan() {
8560 var ele = this[0];
8561
8562 if (ele) {
8563 return ele.isNode() && ele._private.parent == null;
8564 }
8565 },
8566 descendants: function descendants(selector) {
8567 var elements = [];
8568
8569 function add(eles) {
8570 for (var i = 0; i < eles.length; i++) {
8571 var ele = eles[i];
8572 elements.push(ele);
8573
8574 if (ele.children().nonempty()) {
8575 add(ele.children());
8576 }
8577 }
8578 }
8579
8580 add(this.children());
8581 return this.spawn(elements, true).filter(selector);
8582 }
8583};
8584
8585function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8586 var q = [];
8587 var did = new Set$1();
8588 var cy = eles.cy();
8589 var hasCompounds = cy.hasCompoundNodes();
8590
8591 for (var i = 0; i < eles.length; i++) {
8592 var ele = eles[i];
8593
8594 if (includeSelf) {
8595 q.push(ele);
8596 } else if (hasCompounds) {
8597 recursiveStep(q, did, ele);
8598 }
8599 }
8600
8601 while (q.length > 0) {
8602 var _ele = q.shift();
8603
8604 fn(_ele);
8605 did.add(_ele.id());
8606
8607 if (hasCompounds) {
8608 recursiveStep(q, did, _ele);
8609 }
8610 }
8611
8612 return eles;
8613}
8614
8615function addChildren(q, did, ele) {
8616 if (ele.isParent()) {
8617 var children = ele._private.children;
8618
8619 for (var i = 0; i < children.length; i++) {
8620 var child = children[i];
8621
8622 if (!did.has(child.id())) {
8623 q.push(child);
8624 }
8625 }
8626 }
8627} // very efficient version of eles.add( eles.descendants() ).forEach()
8628// for internal use
8629
8630
8631elesfn$f.forEachDown = function (fn) {
8632 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8633 return forEachCompound(this, fn, includeSelf, addChildren);
8634};
8635
8636function addParent(q, did, ele) {
8637 if (ele.isChild()) {
8638 var parent = ele._private.parent;
8639
8640 if (!did.has(parent.id())) {
8641 q.push(parent);
8642 }
8643 }
8644}
8645
8646elesfn$f.forEachUp = function (fn) {
8647 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8648 return forEachCompound(this, fn, includeSelf, addParent);
8649};
8650
8651function addParentAndChildren(q, did, ele) {
8652 addParent(q, did, ele);
8653 addChildren(q, did, ele);
8654}
8655
8656elesfn$f.forEachUpAndDown = function (fn) {
8657 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8658 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8659}; // aliases
8660
8661
8662elesfn$f.ancestors = elesfn$f.parents;
8663
8664var fn$5, elesfn$e;
8665fn$5 = elesfn$e = {
8666 data: define.data({
8667 field: 'data',
8668 bindingEvent: 'data',
8669 allowBinding: true,
8670 allowSetting: true,
8671 settingEvent: 'data',
8672 settingTriggersEvent: true,
8673 triggerFnName: 'trigger',
8674 allowGetting: true,
8675 immutableKeys: {
8676 'id': true,
8677 'source': true,
8678 'target': true,
8679 'parent': true
8680 },
8681 updateStyle: true
8682 }),
8683 removeData: define.removeData({
8684 field: 'data',
8685 event: 'data',
8686 triggerFnName: 'trigger',
8687 triggerEvent: true,
8688 immutableKeys: {
8689 'id': true,
8690 'source': true,
8691 'target': true,
8692 'parent': true
8693 },
8694 updateStyle: true
8695 }),
8696 scratch: define.data({
8697 field: 'scratch',
8698 bindingEvent: 'scratch',
8699 allowBinding: true,
8700 allowSetting: true,
8701 settingEvent: 'scratch',
8702 settingTriggersEvent: true,
8703 triggerFnName: 'trigger',
8704 allowGetting: true,
8705 updateStyle: true
8706 }),
8707 removeScratch: define.removeData({
8708 field: 'scratch',
8709 event: 'scratch',
8710 triggerFnName: 'trigger',
8711 triggerEvent: true,
8712 updateStyle: true
8713 }),
8714 rscratch: define.data({
8715 field: 'rscratch',
8716 allowBinding: false,
8717 allowSetting: true,
8718 settingTriggersEvent: false,
8719 allowGetting: true
8720 }),
8721 removeRscratch: define.removeData({
8722 field: 'rscratch',
8723 triggerEvent: false
8724 }),
8725 id: function id() {
8726 var ele = this[0];
8727
8728 if (ele) {
8729 return ele._private.data.id;
8730 }
8731 }
8732}; // aliases
8733
8734fn$5.attr = fn$5.data;
8735fn$5.removeAttr = fn$5.removeData;
8736var data = elesfn$e;
8737
8738var elesfn$d = {};
8739
8740function defineDegreeFunction(callback) {
8741 return function (includeLoops) {
8742 var self = this;
8743
8744 if (includeLoops === undefined) {
8745 includeLoops = true;
8746 }
8747
8748 if (self.length === 0) {
8749 return;
8750 }
8751
8752 if (self.isNode() && !self.removed()) {
8753 var degree = 0;
8754 var node = self[0];
8755 var connectedEdges = node._private.edges;
8756
8757 for (var i = 0; i < connectedEdges.length; i++) {
8758 var edge = connectedEdges[i];
8759
8760 if (!includeLoops && edge.isLoop()) {
8761 continue;
8762 }
8763
8764 degree += callback(node, edge);
8765 }
8766
8767 return degree;
8768 } else {
8769 return;
8770 }
8771 };
8772}
8773
8774extend(elesfn$d, {
8775 degree: defineDegreeFunction(function (node, edge) {
8776 if (edge.source().same(edge.target())) {
8777 return 2;
8778 } else {
8779 return 1;
8780 }
8781 }),
8782 indegree: defineDegreeFunction(function (node, edge) {
8783 if (edge.target().same(node)) {
8784 return 1;
8785 } else {
8786 return 0;
8787 }
8788 }),
8789 outdegree: defineDegreeFunction(function (node, edge) {
8790 if (edge.source().same(node)) {
8791 return 1;
8792 } else {
8793 return 0;
8794 }
8795 })
8796});
8797
8798function defineDegreeBoundsFunction(degreeFn, callback) {
8799 return function (includeLoops) {
8800 var ret;
8801 var nodes = this.nodes();
8802
8803 for (var i = 0; i < nodes.length; i++) {
8804 var ele = nodes[i];
8805 var degree = ele[degreeFn](includeLoops);
8806
8807 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8808 ret = degree;
8809 }
8810 }
8811
8812 return ret;
8813 };
8814}
8815
8816extend(elesfn$d, {
8817 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8818 return degree < min;
8819 }),
8820 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8821 return degree > max;
8822 }),
8823 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8824 return degree < min;
8825 }),
8826 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8827 return degree > max;
8828 }),
8829 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8830 return degree < min;
8831 }),
8832 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8833 return degree > max;
8834 })
8835});
8836extend(elesfn$d, {
8837 totalDegree: function totalDegree(includeLoops) {
8838 var total = 0;
8839 var nodes = this.nodes();
8840
8841 for (var i = 0; i < nodes.length; i++) {
8842 total += nodes[i].degree(includeLoops);
8843 }
8844
8845 return total;
8846 }
8847});
8848
8849var fn$4, elesfn$c;
8850
8851var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8852 for (var i = 0; i < eles.length; i++) {
8853 var ele = eles[i];
8854
8855 if (!ele.locked()) {
8856 var oldPos = ele._private.position;
8857 var delta = {
8858 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8859 y: newPos.y != null ? newPos.y - oldPos.y : 0
8860 };
8861
8862 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8863 ele.children().shift(delta, silent);
8864 }
8865
8866 ele.dirtyBoundingBoxCache();
8867 }
8868 }
8869};
8870
8871var positionDef = {
8872 field: 'position',
8873 bindingEvent: 'position',
8874 allowBinding: true,
8875 allowSetting: true,
8876 settingEvent: 'position',
8877 settingTriggersEvent: true,
8878 triggerFnName: 'emitAndNotify',
8879 allowGetting: true,
8880 validKeys: ['x', 'y'],
8881 beforeGet: function beforeGet(ele) {
8882 ele.updateCompoundBounds();
8883 },
8884 beforeSet: function beforeSet(eles, newPos) {
8885 beforePositionSet(eles, newPos, false);
8886 },
8887 onSet: function onSet(eles) {
8888 eles.dirtyCompoundBoundsCache();
8889 },
8890 canSet: function canSet(ele) {
8891 return !ele.locked();
8892 }
8893};
8894fn$4 = elesfn$c = {
8895 position: define.data(positionDef),
8896 // position but no notification to renderer
8897 silentPosition: define.data(extend({}, positionDef, {
8898 allowBinding: false,
8899 allowSetting: true,
8900 settingTriggersEvent: false,
8901 allowGetting: false,
8902 beforeSet: function beforeSet(eles, newPos) {
8903 beforePositionSet(eles, newPos, true);
8904 },
8905 onSet: function onSet(eles) {
8906 eles.dirtyCompoundBoundsCache();
8907 }
8908 })),
8909 positions: function positions(pos, silent) {
8910 if (plainObject(pos)) {
8911 if (silent) {
8912 this.silentPosition(pos);
8913 } else {
8914 this.position(pos);
8915 }
8916 } else if (fn$6(pos)) {
8917 var _fn = pos;
8918 var cy = this.cy();
8919 cy.startBatch();
8920
8921 for (var i = 0; i < this.length; i++) {
8922 var ele = this[i];
8923
8924 var _pos = void 0;
8925
8926 if (_pos = _fn(ele, i)) {
8927 if (silent) {
8928 ele.silentPosition(_pos);
8929 } else {
8930 ele.position(_pos);
8931 }
8932 }
8933 }
8934
8935 cy.endBatch();
8936 }
8937
8938 return this; // chaining
8939 },
8940 silentPositions: function silentPositions(pos) {
8941 return this.positions(pos, true);
8942 },
8943 shift: function shift(dim, val, silent) {
8944 var delta;
8945
8946 if (plainObject(dim)) {
8947 delta = {
8948 x: number$1(dim.x) ? dim.x : 0,
8949 y: number$1(dim.y) ? dim.y : 0
8950 };
8951 silent = val;
8952 } else if (string(dim) && number$1(val)) {
8953 delta = {
8954 x: 0,
8955 y: 0
8956 };
8957 delta[dim] = val;
8958 }
8959
8960 if (delta != null) {
8961 var cy = this.cy();
8962 cy.startBatch();
8963
8964 for (var i = 0; i < this.length; i++) {
8965 var ele = this[i]; // exclude any node that is a descendant of the calling collection
8966
8967 if (cy.hasCompoundNodes() && ele.isChild() && ele.ancestors().anySame(this)) {
8968 continue;
8969 }
8970
8971 var pos = ele.position();
8972 var newPos = {
8973 x: pos.x + delta.x,
8974 y: pos.y + delta.y
8975 };
8976
8977 if (silent) {
8978 ele.silentPosition(newPos);
8979 } else {
8980 ele.position(newPos);
8981 }
8982 }
8983
8984 cy.endBatch();
8985 }
8986
8987 return this;
8988 },
8989 silentShift: function silentShift(dim, val) {
8990 if (plainObject(dim)) {
8991 this.shift(dim, true);
8992 } else if (string(dim) && number$1(val)) {
8993 this.shift(dim, val, true);
8994 }
8995
8996 return this;
8997 },
8998 // get/set the rendered (i.e. on screen) positon of the element
8999 renderedPosition: function renderedPosition(dim, val) {
9000 var ele = this[0];
9001 var cy = this.cy();
9002 var zoom = cy.zoom();
9003 var pan = cy.pan();
9004 var rpos = plainObject(dim) ? dim : undefined;
9005 var setting = rpos !== undefined || val !== undefined && string(dim);
9006
9007 if (ele && ele.isNode()) {
9008 // must have an element and must be a node to return position
9009 if (setting) {
9010 for (var i = 0; i < this.length; i++) {
9011 var _ele = this[i];
9012
9013 if (val !== undefined) {
9014 // set one dimension
9015 _ele.position(dim, (val - pan[dim]) / zoom);
9016 } else if (rpos !== undefined) {
9017 // set whole position
9018 _ele.position(renderedToModelPosition(rpos, zoom, pan));
9019 }
9020 }
9021 } else {
9022 // getting
9023 var pos = ele.position();
9024 rpos = modelToRenderedPosition(pos, zoom, pan);
9025
9026 if (dim === undefined) {
9027 // then return the whole rendered position
9028 return rpos;
9029 } else {
9030 // then return the specified dimension
9031 return rpos[dim];
9032 }
9033 }
9034 } else if (!setting) {
9035 return undefined; // for empty collection case
9036 }
9037
9038 return this; // chaining
9039 },
9040 // get/set the position relative to the parent
9041 relativePosition: function relativePosition(dim, val) {
9042 var ele = this[0];
9043 var cy = this.cy();
9044 var ppos = plainObject(dim) ? dim : undefined;
9045 var setting = ppos !== undefined || val !== undefined && string(dim);
9046 var hasCompoundNodes = cy.hasCompoundNodes();
9047
9048 if (ele && ele.isNode()) {
9049 // must have an element and must be a node to return position
9050 if (setting) {
9051 for (var i = 0; i < this.length; i++) {
9052 var _ele2 = this[i];
9053 var parent = hasCompoundNodes ? _ele2.parent() : null;
9054 var hasParent = parent && parent.length > 0;
9055 var relativeToParent = hasParent;
9056
9057 if (hasParent) {
9058 parent = parent[0];
9059 }
9060
9061 var origin = relativeToParent ? parent.position() : {
9062 x: 0,
9063 y: 0
9064 };
9065
9066 if (val !== undefined) {
9067 // set one dimension
9068 _ele2.position(dim, val + origin[dim]);
9069 } else if (ppos !== undefined) {
9070 // set whole position
9071 _ele2.position({
9072 x: ppos.x + origin.x,
9073 y: ppos.y + origin.y
9074 });
9075 }
9076 }
9077 } else {
9078 // getting
9079 var pos = ele.position();
9080
9081 var _parent = hasCompoundNodes ? ele.parent() : null;
9082
9083 var _hasParent = _parent && _parent.length > 0;
9084
9085 var _relativeToParent = _hasParent;
9086
9087 if (_hasParent) {
9088 _parent = _parent[0];
9089 }
9090
9091 var _origin = _relativeToParent ? _parent.position() : {
9092 x: 0,
9093 y: 0
9094 };
9095
9096 ppos = {
9097 x: pos.x - _origin.x,
9098 y: pos.y - _origin.y
9099 };
9100
9101 if (dim === undefined) {
9102 // then return the whole rendered position
9103 return ppos;
9104 } else {
9105 // then return the specified dimension
9106 return ppos[dim];
9107 }
9108 }
9109 } else if (!setting) {
9110 return undefined; // for empty collection case
9111 }
9112
9113 return this; // chaining
9114 }
9115}; // aliases
9116
9117fn$4.modelPosition = fn$4.point = fn$4.position;
9118fn$4.modelPositions = fn$4.points = fn$4.positions;
9119fn$4.renderedPoint = fn$4.renderedPosition;
9120fn$4.relativePoint = fn$4.relativePosition;
9121var position = elesfn$c;
9122
9123var fn$3, elesfn$b;
9124fn$3 = elesfn$b = {};
9125
9126elesfn$b.renderedBoundingBox = function (options) {
9127 var bb = this.boundingBox(options);
9128 var cy = this.cy();
9129 var zoom = cy.zoom();
9130 var pan = cy.pan();
9131 var x1 = bb.x1 * zoom + pan.x;
9132 var x2 = bb.x2 * zoom + pan.x;
9133 var y1 = bb.y1 * zoom + pan.y;
9134 var y2 = bb.y2 * zoom + pan.y;
9135 return {
9136 x1: x1,
9137 x2: x2,
9138 y1: y1,
9139 y2: y2,
9140 w: x2 - x1,
9141 h: y2 - y1
9142 };
9143};
9144
9145elesfn$b.dirtyCompoundBoundsCache = function () {
9146 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9147 var cy = this.cy();
9148
9149 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9150 return this;
9151 }
9152
9153 this.forEachUp(function (ele) {
9154 if (ele.isParent()) {
9155 var _p = ele._private;
9156 _p.compoundBoundsClean = false;
9157 _p.bbCache = null;
9158
9159 if (!silent) {
9160 ele.emitAndNotify('bounds');
9161 }
9162 }
9163 });
9164 return this;
9165};
9166
9167elesfn$b.updateCompoundBounds = function () {
9168 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9169 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9170
9171 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9172 return this;
9173 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9174
9175
9176 if (!force && cy.batching()) {
9177 return this;
9178 }
9179
9180 function update(parent) {
9181 if (!parent.isParent()) {
9182 return;
9183 }
9184
9185 var _p = parent._private;
9186 var children = parent.children();
9187 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9188 var min = {
9189 width: {
9190 val: parent.pstyle('min-width').pfValue,
9191 left: parent.pstyle('min-width-bias-left'),
9192 right: parent.pstyle('min-width-bias-right')
9193 },
9194 height: {
9195 val: parent.pstyle('min-height').pfValue,
9196 top: parent.pstyle('min-height-bias-top'),
9197 bottom: parent.pstyle('min-height-bias-bottom')
9198 }
9199 };
9200 var bb = children.boundingBox({
9201 includeLabels: includeLabels,
9202 includeOverlays: false,
9203 // updating the compound bounds happens outside of the regular
9204 // cache cycle (i.e. before fired events)
9205 useCache: false
9206 });
9207 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9208
9209 if (bb.w === 0 || bb.h === 0) {
9210 bb = {
9211 w: parent.pstyle('width').pfValue,
9212 h: parent.pstyle('height').pfValue
9213 };
9214 bb.x1 = pos.x - bb.w / 2;
9215 bb.x2 = pos.x + bb.w / 2;
9216 bb.y1 = pos.y - bb.h / 2;
9217 bb.y2 = pos.y + bb.h / 2;
9218 }
9219
9220 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9221 var biasDiff = 0;
9222 var biasComplementDiff = 0;
9223 var biasTotal = propBias + propBiasComplement;
9224
9225 if (propDiff > 0 && biasTotal > 0) {
9226 biasDiff = propBias / biasTotal * propDiff;
9227 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9228 }
9229
9230 return {
9231 biasDiff: biasDiff,
9232 biasComplementDiff: biasComplementDiff
9233 };
9234 }
9235
9236 function computePaddingValues(width, height, paddingObject, relativeTo) {
9237 // Assuming percentage is number from 0 to 1
9238 if (paddingObject.units === '%') {
9239 switch (relativeTo) {
9240 case 'width':
9241 return width > 0 ? paddingObject.pfValue * width : 0;
9242
9243 case 'height':
9244 return height > 0 ? paddingObject.pfValue * height : 0;
9245
9246 case 'average':
9247 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9248
9249 case 'min':
9250 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9251
9252 case 'max':
9253 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9254
9255 default:
9256 return 0;
9257 }
9258 } else if (paddingObject.units === 'px') {
9259 return paddingObject.pfValue;
9260 } else {
9261 return 0;
9262 }
9263 }
9264
9265 var leftVal = min.width.left.value;
9266
9267 if (min.width.left.units === 'px' && min.width.val > 0) {
9268 leftVal = leftVal * 100 / min.width.val;
9269 }
9270
9271 var rightVal = min.width.right.value;
9272
9273 if (min.width.right.units === 'px' && min.width.val > 0) {
9274 rightVal = rightVal * 100 / min.width.val;
9275 }
9276
9277 var topVal = min.height.top.value;
9278
9279 if (min.height.top.units === 'px' && min.height.val > 0) {
9280 topVal = topVal * 100 / min.height.val;
9281 }
9282
9283 var bottomVal = min.height.bottom.value;
9284
9285 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9286 bottomVal = bottomVal * 100 / min.height.val;
9287 }
9288
9289 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9290 var diffLeft = widthBiasDiffs.biasDiff;
9291 var diffRight = widthBiasDiffs.biasComplementDiff;
9292 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9293 var diffTop = heightBiasDiffs.biasDiff;
9294 var diffBottom = heightBiasDiffs.biasComplementDiff;
9295 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9296 _p.autoWidth = Math.max(bb.w, min.width.val);
9297 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9298 _p.autoHeight = Math.max(bb.h, min.height.val);
9299 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9300 }
9301
9302 for (var i = 0; i < this.length; i++) {
9303 var ele = this[i];
9304 var _p = ele._private;
9305
9306 if (!_p.compoundBoundsClean || force) {
9307 update(ele);
9308
9309 if (!cy.batching()) {
9310 _p.compoundBoundsClean = true;
9311 }
9312 }
9313 }
9314
9315 return this;
9316};
9317
9318var noninf = function noninf(x) {
9319 if (x === Infinity || x === -Infinity) {
9320 return 0;
9321 }
9322
9323 return x;
9324};
9325
9326var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9327 // don't update with zero area boxes
9328 if (x2 - x1 === 0 || y2 - y1 === 0) {
9329 return;
9330 } // don't update with null dim
9331
9332
9333 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9334 return;
9335 }
9336
9337 b.x1 = x1 < b.x1 ? x1 : b.x1;
9338 b.x2 = x2 > b.x2 ? x2 : b.x2;
9339 b.y1 = y1 < b.y1 ? y1 : b.y1;
9340 b.y2 = y2 > b.y2 ? y2 : b.y2;
9341 b.w = b.x2 - b.x1;
9342 b.h = b.y2 - b.y1;
9343};
9344
9345var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9346 if (b2 == null) {
9347 return b;
9348 }
9349
9350 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9351};
9352
9353var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9354 return getPrefixedProperty(obj, field, prefix);
9355};
9356
9357var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9358 if (ele.cy().headless()) {
9359 return;
9360 }
9361
9362 var _p = ele._private;
9363 var rstyle = _p.rstyle;
9364 var halfArW = rstyle.arrowWidth / 2;
9365 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9366 var x;
9367 var y;
9368
9369 if (arrowType !== 'none') {
9370 if (prefix === 'source') {
9371 x = rstyle.srcX;
9372 y = rstyle.srcY;
9373 } else if (prefix === 'target') {
9374 x = rstyle.tgtX;
9375 y = rstyle.tgtY;
9376 } else {
9377 x = rstyle.midX;
9378 y = rstyle.midY;
9379 } // always store the individual arrow bounds
9380
9381
9382 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9383 var bb = bbs[prefix] = bbs[prefix] || {};
9384 bb.x1 = x - halfArW;
9385 bb.y1 = y - halfArW;
9386 bb.x2 = x + halfArW;
9387 bb.y2 = y + halfArW;
9388 bb.w = bb.x2 - bb.x1;
9389 bb.h = bb.y2 - bb.y1;
9390 expandBoundingBox(bb, 1);
9391 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9392 }
9393};
9394
9395var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9396 if (ele.cy().headless()) {
9397 return;
9398 }
9399
9400 var prefixDash;
9401
9402 if (prefix) {
9403 prefixDash = prefix + '-';
9404 } else {
9405 prefixDash = '';
9406 }
9407
9408 var _p = ele._private;
9409 var rstyle = _p.rstyle;
9410 var label = ele.pstyle(prefixDash + 'label').strValue;
9411
9412 if (label) {
9413 var halign = ele.pstyle('text-halign');
9414 var valign = ele.pstyle('text-valign');
9415 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9416 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9417 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9418 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9419 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9420 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9421 var isEdge = ele.isEdge();
9422 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9423 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9424 var borderWidth = ele.pstyle('text-border-width').pfValue;
9425 var halfBorderWidth = borderWidth / 2;
9426 var padding = ele.pstyle('text-background-padding').pfValue;
9427 var marginOfError = 2; // expand to work around browser dimension inaccuracies
9428
9429 var lh = labelHeight;
9430 var lw = labelWidth;
9431 var lw_2 = lw / 2;
9432 var lh_2 = lh / 2;
9433 var lx1, lx2, ly1, ly2;
9434
9435 if (isEdge) {
9436 lx1 = labelX - lw_2;
9437 lx2 = labelX + lw_2;
9438 ly1 = labelY - lh_2;
9439 ly2 = labelY + lh_2;
9440 } else {
9441 switch (halign.value) {
9442 case 'left':
9443 lx1 = labelX - lw;
9444 lx2 = labelX;
9445 break;
9446
9447 case 'center':
9448 lx1 = labelX - lw_2;
9449 lx2 = labelX + lw_2;
9450 break;
9451
9452 case 'right':
9453 lx1 = labelX;
9454 lx2 = labelX + lw;
9455 break;
9456 }
9457
9458 switch (valign.value) {
9459 case 'top':
9460 ly1 = labelY - lh;
9461 ly2 = labelY;
9462 break;
9463
9464 case 'center':
9465 ly1 = labelY - lh_2;
9466 ly2 = labelY + lh_2;
9467 break;
9468
9469 case 'bottom':
9470 ly1 = labelY;
9471 ly2 = labelY + lh;
9472 break;
9473 }
9474 } // shift by margin and expand by outline and border
9475
9476
9477 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9478 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
9479 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9480 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
9481
9482 var bbPrefix = prefix || 'main';
9483 var bbs = _p.labelBounds;
9484 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9485 bb.x1 = lx1;
9486 bb.y1 = ly1;
9487 bb.x2 = lx2;
9488 bb.y2 = ly2;
9489 bb.w = lx2 - lx1;
9490 bb.h = ly2 - ly1;
9491 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9492 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9493
9494 if (isAutorotate || isPfValue) {
9495 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9496 var cos = Math.cos(theta);
9497 var sin = Math.sin(theta); // rotation point (default value for center-center)
9498
9499 var xo = (lx1 + lx2) / 2;
9500 var yo = (ly1 + ly2) / 2;
9501
9502 if (!isEdge) {
9503 switch (halign.value) {
9504 case 'left':
9505 xo = lx2;
9506 break;
9507
9508 case 'right':
9509 xo = lx1;
9510 break;
9511 }
9512
9513 switch (valign.value) {
9514 case 'top':
9515 yo = ly2;
9516 break;
9517
9518 case 'bottom':
9519 yo = ly1;
9520 break;
9521 }
9522 }
9523
9524 var rotate = function rotate(x, y) {
9525 x = x - xo;
9526 y = y - yo;
9527 return {
9528 x: x * cos - y * sin + xo,
9529 y: x * sin + y * cos + yo
9530 };
9531 };
9532
9533 var px1y1 = rotate(lx1, ly1);
9534 var px1y2 = rotate(lx1, ly2);
9535 var px2y1 = rotate(lx2, ly1);
9536 var px2y2 = rotate(lx2, ly2);
9537 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9538 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9539 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9540 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9541 }
9542
9543 var bbPrefixRot = bbPrefix + 'Rot';
9544 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9545 bbRot.x1 = lx1;
9546 bbRot.y1 = ly1;
9547 bbRot.x2 = lx2;
9548 bbRot.y2 = ly2;
9549 bbRot.w = lx2 - lx1;
9550 bbRot.h = ly2 - ly1;
9551 updateBounds(bounds, lx1, ly1, lx2, ly2);
9552 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9553 }
9554
9555 return bounds;
9556}; // get the bounding box of the elements (in raw model position)
9557
9558
9559var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9560 var cy = ele._private.cy;
9561 var styleEnabled = cy.styleEnabled();
9562 var headless = cy.headless();
9563 var bounds = makeBoundingBox();
9564 var _p = ele._private;
9565 var isNode = ele.isNode();
9566 var isEdge = ele.isEdge();
9567 var ex1, ex2, ey1, ey2; // extrema of body / lines
9568
9569 var x, y; // node pos
9570
9571 var rstyle = _p.rstyle;
9572 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9573 // (other factors like width values will be considered later in this function anyway)
9574
9575 var isDisplayed = function isDisplayed(ele) {
9576 return ele.pstyle('display').value !== 'none';
9577 };
9578
9579 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9580 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9581
9582 if (displayed) {
9583 // displayed suffices, since we will find zero area eles anyway
9584 var overlayOpacity = 0;
9585 var overlayPadding = 0;
9586
9587 if (styleEnabled && options.includeOverlays) {
9588 overlayOpacity = ele.pstyle('overlay-opacity').value;
9589
9590 if (overlayOpacity !== 0) {
9591 overlayPadding = ele.pstyle('overlay-padding').value;
9592 }
9593 }
9594
9595 var underlayOpacity = 0;
9596 var underlayPadding = 0;
9597
9598 if (styleEnabled && options.includeUnderlays) {
9599 underlayOpacity = ele.pstyle('underlay-opacity').value;
9600
9601 if (underlayOpacity !== 0) {
9602 underlayPadding = ele.pstyle('underlay-padding').value;
9603 }
9604 }
9605
9606 var padding = Math.max(overlayPadding, underlayPadding);
9607 var w = 0;
9608 var wHalf = 0;
9609
9610 if (styleEnabled) {
9611 w = ele.pstyle('width').pfValue;
9612 wHalf = w / 2;
9613 }
9614
9615 if (isNode && options.includeNodes) {
9616 var pos = ele.position();
9617 x = pos.x;
9618 y = pos.y;
9619
9620 var _w = ele.outerWidth();
9621
9622 var halfW = _w / 2;
9623 var h = ele.outerHeight();
9624 var halfH = h / 2; // handle node dimensions
9625 /////////////////////////
9626
9627 ex1 = x - halfW;
9628 ex2 = x + halfW;
9629 ey1 = y - halfH;
9630 ey2 = y + halfH;
9631 updateBounds(bounds, ex1, ey1, ex2, ey2);
9632 } else if (isEdge && options.includeEdges) {
9633 if (styleEnabled && !headless) {
9634 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9635 //////////////////////////////////////////////
9636
9637 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9638 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9639 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9640 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9641
9642 ex1 -= wHalf;
9643 ex2 += wHalf;
9644 ey1 -= wHalf;
9645 ey2 += wHalf;
9646 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9647 ////////////////
9648
9649 if (curveStyle === 'haystack') {
9650 var hpts = rstyle.haystackPts;
9651
9652 if (hpts && hpts.length === 2) {
9653 ex1 = hpts[0].x;
9654 ey1 = hpts[0].y;
9655 ex2 = hpts[1].x;
9656 ey2 = hpts[1].y;
9657
9658 if (ex1 > ex2) {
9659 var temp = ex1;
9660 ex1 = ex2;
9661 ex2 = temp;
9662 }
9663
9664 if (ey1 > ey2) {
9665 var _temp = ey1;
9666 ey1 = ey2;
9667 ey2 = _temp;
9668 }
9669
9670 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9671 }
9672 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9673 var pts;
9674
9675 switch (curveStyle) {
9676 case 'bezier':
9677 case 'unbundled-bezier':
9678 pts = rstyle.bezierPts;
9679 break;
9680
9681 case 'segments':
9682 case 'taxi':
9683 pts = rstyle.linePts;
9684 break;
9685 }
9686
9687 if (pts != null) {
9688 for (var j = 0; j < pts.length; j++) {
9689 var pt = pts[j];
9690 ex1 = pt.x - wHalf;
9691 ex2 = pt.x + wHalf;
9692 ey1 = pt.y - wHalf;
9693 ey2 = pt.y + wHalf;
9694 updateBounds(bounds, ex1, ey1, ex2, ey2);
9695 }
9696 }
9697 } // bezier-like or segment-like edge
9698
9699 } else {
9700 // headless or style disabled
9701 // fallback on source and target positions
9702 //////////////////////////////////////////
9703 var n1 = ele.source();
9704 var n1pos = n1.position();
9705 var n2 = ele.target();
9706 var n2pos = n2.position();
9707 ex1 = n1pos.x;
9708 ex2 = n2pos.x;
9709 ey1 = n1pos.y;
9710 ey2 = n2pos.y;
9711
9712 if (ex1 > ex2) {
9713 var _temp2 = ex1;
9714 ex1 = ex2;
9715 ex2 = _temp2;
9716 }
9717
9718 if (ey1 > ey2) {
9719 var _temp3 = ey1;
9720 ey1 = ey2;
9721 ey2 = _temp3;
9722 } // take into account edge width
9723
9724
9725 ex1 -= wHalf;
9726 ex2 += wHalf;
9727 ey1 -= wHalf;
9728 ey2 += wHalf;
9729 updateBounds(bounds, ex1, ey1, ex2, ey2);
9730 } // headless or style disabled
9731
9732 } // edges
9733 // handle edge arrow size
9734 /////////////////////////
9735
9736
9737 if (styleEnabled && options.includeEdges && isEdge) {
9738 updateBoundsFromArrow(bounds, ele, 'mid-source');
9739 updateBoundsFromArrow(bounds, ele, 'mid-target');
9740 updateBoundsFromArrow(bounds, ele, 'source');
9741 updateBoundsFromArrow(bounds, ele, 'target');
9742 } // ghost
9743 ////////
9744
9745
9746 if (styleEnabled) {
9747 var ghost = ele.pstyle('ghost').value === 'yes';
9748
9749 if (ghost) {
9750 var gx = ele.pstyle('ghost-offset-x').pfValue;
9751 var gy = ele.pstyle('ghost-offset-y').pfValue;
9752 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9753 }
9754 } // always store the body bounds separately from the labels
9755
9756
9757 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9758 assignBoundingBox(bbBody, bounds);
9759 expandBoundingBoxSides(bbBody, manualExpansion);
9760 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9761 // overlay
9762 //////////
9763
9764 if (styleEnabled) {
9765 ex1 = bounds.x1;
9766 ex2 = bounds.x2;
9767 ey1 = bounds.y1;
9768 ey2 = bounds.y2;
9769 updateBounds(bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding);
9770 } // always store the body bounds separately from the labels
9771
9772
9773 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9774 assignBoundingBox(bbOverlay, bounds);
9775 expandBoundingBoxSides(bbOverlay, manualExpansion);
9776 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9777 // handle label dimensions
9778 //////////////////////////
9779
9780 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9781
9782 if (bbLabels.all != null) {
9783 clearBoundingBox(bbLabels.all);
9784 } else {
9785 bbLabels.all = makeBoundingBox();
9786 }
9787
9788 if (styleEnabled && options.includeLabels) {
9789 if (options.includeMainLabels) {
9790 updateBoundsFromLabel(bounds, ele, null);
9791 }
9792
9793 if (isEdge) {
9794 if (options.includeSourceLabels) {
9795 updateBoundsFromLabel(bounds, ele, 'source');
9796 }
9797
9798 if (options.includeTargetLabels) {
9799 updateBoundsFromLabel(bounds, ele, 'target');
9800 }
9801 }
9802 } // style enabled for labels
9803
9804 } // if displayed
9805
9806
9807 bounds.x1 = noninf(bounds.x1);
9808 bounds.y1 = noninf(bounds.y1);
9809 bounds.x2 = noninf(bounds.x2);
9810 bounds.y2 = noninf(bounds.y2);
9811 bounds.w = noninf(bounds.x2 - bounds.x1);
9812 bounds.h = noninf(bounds.y2 - bounds.y1);
9813
9814 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9815 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9816
9817 expandBoundingBox(bounds, 1);
9818 }
9819
9820 return bounds;
9821};
9822
9823var getKey = function getKey(opts) {
9824 var i = 0;
9825
9826 var tf = function tf(val) {
9827 return (val ? 1 : 0) << i++;
9828 };
9829
9830 var key = 0;
9831 key += tf(opts.incudeNodes);
9832 key += tf(opts.includeEdges);
9833 key += tf(opts.includeLabels);
9834 key += tf(opts.includeMainLabels);
9835 key += tf(opts.includeSourceLabels);
9836 key += tf(opts.includeTargetLabels);
9837 key += tf(opts.includeOverlays);
9838 return key;
9839};
9840
9841var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9842 if (ele.isEdge()) {
9843 var p1 = ele.source().position();
9844 var p2 = ele.target().position();
9845
9846 var r = function r(x) {
9847 return Math.round(x);
9848 };
9849
9850 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9851 } else {
9852 return 0;
9853 }
9854};
9855
9856var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9857 var _p = ele._private;
9858 var bb;
9859 var isEdge = ele.isEdge();
9860 var key = opts == null ? defBbOptsKey : getKey(opts);
9861 var usingDefOpts = key === defBbOptsKey;
9862 var currPosKey = getBoundingBoxPosKey(ele);
9863 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9864 var useCache = opts.useCache && isPosKeySame;
9865
9866 var isDirty = function isDirty(ele) {
9867 return ele._private.bbCache == null || ele._private.styleDirty;
9868 };
9869
9870 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9871
9872 if (needRecalc) {
9873 if (!isPosKeySame) {
9874 ele.recalculateRenderedStyle(useCache);
9875 }
9876
9877 bb = boundingBoxImpl(ele, defBbOpts);
9878 _p.bbCache = bb;
9879 _p.bbCachePosKey = currPosKey;
9880 } else {
9881 bb = _p.bbCache;
9882 } // not using def opts => need to build up bb from combination of sub bbs
9883
9884
9885 if (!usingDefOpts) {
9886 var isNode = ele.isNode();
9887 bb = makeBoundingBox();
9888
9889 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9890 if (opts.includeOverlays) {
9891 updateBoundsFromBox(bb, _p.overlayBounds);
9892 } else {
9893 updateBoundsFromBox(bb, _p.bodyBounds);
9894 }
9895 }
9896
9897 if (opts.includeLabels) {
9898 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9899 updateBoundsFromBox(bb, _p.labelBounds.all);
9900 } else {
9901 if (opts.includeMainLabels) {
9902 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9903 }
9904
9905 if (opts.includeSourceLabels) {
9906 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9907 }
9908
9909 if (opts.includeTargetLabels) {
9910 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9911 }
9912 }
9913 }
9914
9915 bb.w = bb.x2 - bb.x1;
9916 bb.h = bb.y2 - bb.y1;
9917 }
9918
9919 return bb;
9920};
9921
9922var defBbOpts = {
9923 includeNodes: true,
9924 includeEdges: true,
9925 includeLabels: true,
9926 includeMainLabels: true,
9927 includeSourceLabels: true,
9928 includeTargetLabels: true,
9929 includeOverlays: true,
9930 includeUnderlays: true,
9931 useCache: true
9932};
9933var defBbOptsKey = getKey(defBbOpts);
9934var filledBbOpts = defaults$g(defBbOpts);
9935
9936elesfn$b.boundingBox = function (options) {
9937 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9938 // specified s.t. the cache is used, so check for this case to make it faster by
9939 // avoiding the overhead of the rest of the function
9940
9941 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9942 if (options === undefined) {
9943 options = defBbOpts;
9944 } else {
9945 options = filledBbOpts(options);
9946 }
9947
9948 bounds = cachedBoundingBoxImpl(this[0], options);
9949 } else {
9950 bounds = makeBoundingBox();
9951 options = options || defBbOpts;
9952 var opts = filledBbOpts(options);
9953 var eles = this;
9954 var cy = eles.cy();
9955 var styleEnabled = cy.styleEnabled();
9956
9957 if (styleEnabled) {
9958 for (var i = 0; i < eles.length; i++) {
9959 var ele = eles[i];
9960 var _p = ele._private;
9961 var currPosKey = getBoundingBoxPosKey(ele);
9962 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9963 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
9964 ele.recalculateRenderedStyle(useCache);
9965 }
9966 }
9967
9968 this.updateCompoundBounds(!options.useCache);
9969
9970 for (var _i = 0; _i < eles.length; _i++) {
9971 var _ele = eles[_i];
9972 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9973 }
9974 }
9975
9976 bounds.x1 = noninf(bounds.x1);
9977 bounds.y1 = noninf(bounds.y1);
9978 bounds.x2 = noninf(bounds.x2);
9979 bounds.y2 = noninf(bounds.y2);
9980 bounds.w = noninf(bounds.x2 - bounds.x1);
9981 bounds.h = noninf(bounds.y2 - bounds.y1);
9982 return bounds;
9983};
9984
9985elesfn$b.dirtyBoundingBoxCache = function () {
9986 for (var i = 0; i < this.length; i++) {
9987 var _p = this[i]._private;
9988 _p.bbCache = null;
9989 _p.bbCachePosKey = null;
9990 _p.bodyBounds = null;
9991 _p.overlayBounds = null;
9992 _p.labelBounds.all = null;
9993 _p.labelBounds.source = null;
9994 _p.labelBounds.target = null;
9995 _p.labelBounds.main = null;
9996 _p.labelBounds.sourceRot = null;
9997 _p.labelBounds.targetRot = null;
9998 _p.labelBounds.mainRot = null;
9999 _p.arrowBounds.source = null;
10000 _p.arrowBounds.target = null;
10001 _p.arrowBounds['mid-source'] = null;
10002 _p.arrowBounds['mid-target'] = null;
10003 }
10004
10005 this.emitAndNotify('bounds');
10006 return this;
10007}; // private helper to get bounding box for custom node positions
10008// - good for perf in certain cases but currently requires dirtying the rendered style
10009// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
10010// - try to use for only things like discrete layouts where the node position would change anyway
10011
10012
10013elesfn$b.boundingBoxAt = function (fn) {
10014 var nodes = this.nodes();
10015 var cy = this.cy();
10016 var hasCompoundNodes = cy.hasCompoundNodes();
10017 var parents = cy.collection();
10018
10019 if (hasCompoundNodes) {
10020 parents = nodes.filter(function (node) {
10021 return node.isParent();
10022 });
10023 nodes = nodes.not(parents);
10024 }
10025
10026 if (plainObject(fn)) {
10027 var obj = fn;
10028
10029 fn = function fn() {
10030 return obj;
10031 };
10032 }
10033
10034 var storeOldPos = function storeOldPos(node, i) {
10035 return node._private.bbAtOldPos = fn(node, i);
10036 };
10037
10038 var getOldPos = function getOldPos(node) {
10039 return node._private.bbAtOldPos;
10040 };
10041
10042 cy.startBatch();
10043 nodes.forEach(storeOldPos).silentPositions(fn);
10044
10045 if (hasCompoundNodes) {
10046 parents.dirtyCompoundBoundsCache();
10047 parents.dirtyBoundingBoxCache();
10048 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10049 }
10050
10051 var bb = copyBoundingBox(this.boundingBox({
10052 useCache: false
10053 }));
10054 nodes.silentPositions(getOldPos);
10055
10056 if (hasCompoundNodes) {
10057 parents.dirtyCompoundBoundsCache();
10058 parents.dirtyBoundingBoxCache();
10059 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10060 }
10061
10062 cy.endBatch();
10063 return bb;
10064};
10065
10066fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
10067fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
10068var bounds = elesfn$b;
10069
10070var fn$2, elesfn$a;
10071fn$2 = elesfn$a = {};
10072
10073var defineDimFns = function defineDimFns(opts) {
10074 opts.uppercaseName = capitalize(opts.name);
10075 opts.autoName = 'auto' + opts.uppercaseName;
10076 opts.labelName = 'label' + opts.uppercaseName;
10077 opts.outerName = 'outer' + opts.uppercaseName;
10078 opts.uppercaseOuterName = capitalize(opts.outerName);
10079
10080 fn$2[opts.name] = function dimImpl() {
10081 var ele = this[0];
10082 var _p = ele._private;
10083 var cy = _p.cy;
10084 var styleEnabled = cy._private.styleEnabled;
10085
10086 if (ele) {
10087 if (styleEnabled) {
10088 if (ele.isParent()) {
10089 ele.updateCompoundBounds();
10090 return _p[opts.autoName] || 0;
10091 }
10092
10093 var d = ele.pstyle(opts.name);
10094
10095 switch (d.strValue) {
10096 case 'label':
10097 ele.recalculateRenderedStyle();
10098 return _p.rstyle[opts.labelName] || 0;
10099
10100 default:
10101 return d.pfValue;
10102 }
10103 } else {
10104 return 1;
10105 }
10106 }
10107 };
10108
10109 fn$2['outer' + opts.uppercaseName] = function outerDimImpl() {
10110 var ele = this[0];
10111 var _p = ele._private;
10112 var cy = _p.cy;
10113 var styleEnabled = cy._private.styleEnabled;
10114
10115 if (ele) {
10116 if (styleEnabled) {
10117 var dim = ele[opts.name]();
10118 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10119
10120 var padding = 2 * ele.padding();
10121 return dim + border + padding;
10122 } else {
10123 return 1;
10124 }
10125 }
10126 };
10127
10128 fn$2['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10129 var ele = this[0];
10130
10131 if (ele) {
10132 var d = ele[opts.name]();
10133 return d * this.cy().zoom();
10134 }
10135 };
10136
10137 fn$2['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10138 var ele = this[0];
10139
10140 if (ele) {
10141 var od = ele[opts.outerName]();
10142 return od * this.cy().zoom();
10143 }
10144 };
10145};
10146
10147defineDimFns({
10148 name: 'width'
10149});
10150defineDimFns({
10151 name: 'height'
10152});
10153
10154elesfn$a.padding = function () {
10155 var ele = this[0];
10156 var _p = ele._private;
10157
10158 if (ele.isParent()) {
10159 ele.updateCompoundBounds();
10160
10161 if (_p.autoPadding !== undefined) {
10162 return _p.autoPadding;
10163 } else {
10164 return ele.pstyle('padding').pfValue;
10165 }
10166 } else {
10167 return ele.pstyle('padding').pfValue;
10168 }
10169};
10170
10171elesfn$a.paddedHeight = function () {
10172 var ele = this[0];
10173 return ele.height() + 2 * ele.padding();
10174};
10175
10176elesfn$a.paddedWidth = function () {
10177 var ele = this[0];
10178 return ele.width() + 2 * ele.padding();
10179};
10180
10181var widthHeight = elesfn$a;
10182
10183var ifEdge = function ifEdge(ele, getValue) {
10184 if (ele.isEdge()) {
10185 return getValue(ele);
10186 }
10187};
10188
10189var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10190 if (ele.isEdge()) {
10191 var cy = ele.cy();
10192 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10193 }
10194};
10195
10196var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10197 if (ele.isEdge()) {
10198 var cy = ele.cy();
10199 var pan = cy.pan();
10200 var zoom = cy.zoom();
10201 return getPoints(ele).map(function (p) {
10202 return modelToRenderedPosition(p, zoom, pan);
10203 });
10204 }
10205};
10206
10207var controlPoints = function controlPoints(ele) {
10208 return ele.renderer().getControlPoints(ele);
10209};
10210
10211var segmentPoints = function segmentPoints(ele) {
10212 return ele.renderer().getSegmentPoints(ele);
10213};
10214
10215var sourceEndpoint = function sourceEndpoint(ele) {
10216 return ele.renderer().getSourceEndpoint(ele);
10217};
10218
10219var targetEndpoint = function targetEndpoint(ele) {
10220 return ele.renderer().getTargetEndpoint(ele);
10221};
10222
10223var midpoint = function midpoint(ele) {
10224 return ele.renderer().getEdgeMidpoint(ele);
10225};
10226
10227var pts = {
10228 controlPoints: {
10229 get: controlPoints,
10230 mult: true
10231 },
10232 segmentPoints: {
10233 get: segmentPoints,
10234 mult: true
10235 },
10236 sourceEndpoint: {
10237 get: sourceEndpoint
10238 },
10239 targetEndpoint: {
10240 get: targetEndpoint
10241 },
10242 midpoint: {
10243 get: midpoint
10244 }
10245};
10246
10247var renderedName = function renderedName(name) {
10248 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10249};
10250
10251var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10252 var spec = pts[name];
10253 var rName = renderedName(name);
10254
10255 obj[name] = function () {
10256 return ifEdge(this, spec.get);
10257 };
10258
10259 if (spec.mult) {
10260 obj[rName] = function () {
10261 return ifEdgeRenderedPositions(this, spec.get);
10262 };
10263 } else {
10264 obj[rName] = function () {
10265 return ifEdgeRenderedPosition(this, spec.get);
10266 };
10267 }
10268
10269 return obj;
10270}, {});
10271
10272var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10273
10274/*!
10275Event object based on jQuery events, MIT license
10276
10277https://jquery.org/license/
10278https://tldrlegal.com/license/mit-license
10279https://github.com/jquery/jquery/blob/master/src/event.js
10280*/
10281var Event = function Event(src, props) {
10282 this.recycle(src, props);
10283};
10284
10285function returnFalse() {
10286 return false;
10287}
10288
10289function returnTrue() {
10290 return true;
10291} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10292
10293
10294Event.prototype = {
10295 instanceString: function instanceString() {
10296 return 'event';
10297 },
10298 recycle: function recycle(src, props) {
10299 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10300
10301 if (src != null && src.preventDefault) {
10302 // Browser Event object
10303 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10304 // by a handler lower down the tree; reflect the correct value.
10305
10306 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10307 } else if (src != null && src.type) {
10308 // Plain object containing all event details
10309 props = src;
10310 } else {
10311 // Event string
10312 this.type = src;
10313 } // Put explicitly provided properties onto the event object
10314
10315
10316 if (props != null) {
10317 // more efficient to manually copy fields we use
10318 this.originalEvent = props.originalEvent;
10319 this.type = props.type != null ? props.type : this.type;
10320 this.cy = props.cy;
10321 this.target = props.target;
10322 this.position = props.position;
10323 this.renderedPosition = props.renderedPosition;
10324 this.namespace = props.namespace;
10325 this.layout = props.layout;
10326 }
10327
10328 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10329 // create a rendered position based on the passed position
10330 var pos = this.position;
10331 var zoom = this.cy.zoom();
10332 var pan = this.cy.pan();
10333 this.renderedPosition = {
10334 x: pos.x * zoom + pan.x,
10335 y: pos.y * zoom + pan.y
10336 };
10337 } // Create a timestamp if incoming event doesn't have one
10338
10339
10340 this.timeStamp = src && src.timeStamp || Date.now();
10341 },
10342 preventDefault: function preventDefault() {
10343 this.isDefaultPrevented = returnTrue;
10344 var e = this.originalEvent;
10345
10346 if (!e) {
10347 return;
10348 } // if preventDefault exists run it on the original event
10349
10350
10351 if (e.preventDefault) {
10352 e.preventDefault();
10353 }
10354 },
10355 stopPropagation: function stopPropagation() {
10356 this.isPropagationStopped = returnTrue;
10357 var e = this.originalEvent;
10358
10359 if (!e) {
10360 return;
10361 } // if stopPropagation exists run it on the original event
10362
10363
10364 if (e.stopPropagation) {
10365 e.stopPropagation();
10366 }
10367 },
10368 stopImmediatePropagation: function stopImmediatePropagation() {
10369 this.isImmediatePropagationStopped = returnTrue;
10370 this.stopPropagation();
10371 },
10372 isDefaultPrevented: returnFalse,
10373 isPropagationStopped: returnFalse,
10374 isImmediatePropagationStopped: returnFalse
10375};
10376
10377var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10378
10379var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10380
10381var defaults$8 = {
10382 qualifierCompare: function qualifierCompare(q1, q2) {
10383 return q1 === q2;
10384 },
10385 eventMatches: function
10386 /*context, listener, eventObj*/
10387 eventMatches() {
10388 return true;
10389 },
10390 addEventFields: function
10391 /*context, evt*/
10392 addEventFields() {},
10393 callbackContext: function callbackContext(context
10394 /*, listener, eventObj*/
10395 ) {
10396 return context;
10397 },
10398 beforeEmit: function
10399 /* context, listener, eventObj */
10400 beforeEmit() {},
10401 afterEmit: function
10402 /* context, listener, eventObj */
10403 afterEmit() {},
10404 bubble: function
10405 /*context*/
10406 bubble() {
10407 return false;
10408 },
10409 parent: function
10410 /*context*/
10411 parent() {
10412 return null;
10413 },
10414 context: null
10415};
10416var defaultsKeys = Object.keys(defaults$8);
10417var emptyOpts = {};
10418
10419function Emitter() {
10420 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10421 var context = arguments.length > 1 ? arguments[1] : undefined;
10422
10423 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10424 for (var i = 0; i < defaultsKeys.length; i++) {
10425 var key = defaultsKeys[i];
10426 this[key] = opts[key] || defaults$8[key];
10427 }
10428
10429 this.context = context || this.context;
10430 this.listeners = [];
10431 this.emitting = 0;
10432}
10433
10434var p = Emitter.prototype;
10435
10436var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10437 if (fn$6(qualifier)) {
10438 callback = qualifier;
10439 qualifier = null;
10440 }
10441
10442 if (confOverrides) {
10443 if (conf == null) {
10444 conf = confOverrides;
10445 } else {
10446 conf = extend({}, conf, confOverrides);
10447 }
10448 }
10449
10450 var eventList = array(events) ? events : events.split(/\s+/);
10451
10452 for (var i = 0; i < eventList.length; i++) {
10453 var evt = eventList[i];
10454
10455 if (emptyString(evt)) {
10456 continue;
10457 }
10458
10459 var match = evt.match(eventRegex); // type[.namespace]
10460
10461 if (match) {
10462 var type = match[1];
10463 var namespace = match[2] ? match[2] : null;
10464 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10465
10466 if (ret === false) {
10467 break;
10468 } // allow exiting early
10469
10470 }
10471 }
10472};
10473
10474var makeEventObj = function makeEventObj(self, obj) {
10475 self.addEventFields(self.context, obj);
10476 return new Event(obj.type, obj);
10477};
10478
10479var forEachEventObj = function forEachEventObj(self, handler, events) {
10480 if (event(events)) {
10481 handler(self, events);
10482 return;
10483 } else if (plainObject(events)) {
10484 handler(self, makeEventObj(self, events));
10485 return;
10486 }
10487
10488 var eventList = array(events) ? events : events.split(/\s+/);
10489
10490 for (var i = 0; i < eventList.length; i++) {
10491 var evt = eventList[i];
10492
10493 if (emptyString(evt)) {
10494 continue;
10495 }
10496
10497 var match = evt.match(eventRegex); // type[.namespace]
10498
10499 if (match) {
10500 var type = match[1];
10501 var namespace = match[2] ? match[2] : null;
10502 var eventObj = makeEventObj(self, {
10503 type: type,
10504 namespace: namespace,
10505 target: self.context
10506 });
10507 handler(self, eventObj);
10508 }
10509 }
10510};
10511
10512p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10513 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10514 if (fn$6(callback)) {
10515 self.listeners.push({
10516 event: event,
10517 // full event string
10518 callback: callback,
10519 // callback to run
10520 type: type,
10521 // the event type (e.g. 'click')
10522 namespace: namespace,
10523 // the event namespace (e.g. ".foo")
10524 qualifier: qualifier,
10525 // a restriction on whether to match this emitter
10526 conf: conf // additional configuration
10527
10528 });
10529 }
10530 }, events, qualifier, callback, conf, confOverrides);
10531 return this;
10532};
10533
10534p.one = function (events, qualifier, callback, conf) {
10535 return this.on(events, qualifier, callback, conf, {
10536 one: true
10537 });
10538};
10539
10540p.removeListener = p.off = function (events, qualifier, callback, conf) {
10541 var _this = this;
10542
10543 if (this.emitting !== 0) {
10544 this.listeners = copyArray(this.listeners);
10545 }
10546
10547 var listeners = this.listeners;
10548
10549 var _loop = function _loop(i) {
10550 var listener = listeners[i];
10551 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10552 /*, conf*/
10553 ) {
10554 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10555 listeners.splice(i, 1);
10556 return false;
10557 }
10558 }, events, qualifier, callback, conf);
10559 };
10560
10561 for (var i = listeners.length - 1; i >= 0; i--) {
10562 _loop(i);
10563 }
10564
10565 return this;
10566};
10567
10568p.removeAllListeners = function () {
10569 return this.removeListener('*');
10570};
10571
10572p.emit = p.trigger = function (events, extraParams, manualCallback) {
10573 var listeners = this.listeners;
10574 var numListenersBeforeEmit = listeners.length;
10575 this.emitting++;
10576
10577 if (!array(extraParams)) {
10578 extraParams = [extraParams];
10579 }
10580
10581 forEachEventObj(this, function (self, eventObj) {
10582 if (manualCallback != null) {
10583 listeners = [{
10584 event: eventObj.event,
10585 type: eventObj.type,
10586 namespace: eventObj.namespace,
10587 callback: manualCallback
10588 }];
10589 numListenersBeforeEmit = listeners.length;
10590 }
10591
10592 var _loop2 = function _loop2(i) {
10593 var listener = listeners[i];
10594
10595 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10596 var args = [eventObj];
10597
10598 if (extraParams != null) {
10599 push(args, extraParams);
10600 }
10601
10602 self.beforeEmit(self.context, listener, eventObj);
10603
10604 if (listener.conf && listener.conf.one) {
10605 self.listeners = self.listeners.filter(function (l) {
10606 return l !== listener;
10607 });
10608 }
10609
10610 var context = self.callbackContext(self.context, listener, eventObj);
10611 var ret = listener.callback.apply(context, args);
10612 self.afterEmit(self.context, listener, eventObj);
10613
10614 if (ret === false) {
10615 eventObj.stopPropagation();
10616 eventObj.preventDefault();
10617 }
10618 } // if listener matches
10619
10620 };
10621
10622 for (var i = 0; i < numListenersBeforeEmit; i++) {
10623 _loop2(i);
10624 } // for listener
10625
10626
10627 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10628 self.parent(self.context).emit(eventObj, extraParams);
10629 }
10630 }, events);
10631 this.emitting--;
10632 return this;
10633};
10634
10635var emitterOptions$1 = {
10636 qualifierCompare: function qualifierCompare(selector1, selector2) {
10637 if (selector1 == null || selector2 == null) {
10638 return selector1 == null && selector2 == null;
10639 } else {
10640 return selector1.sameText(selector2);
10641 }
10642 },
10643 eventMatches: function eventMatches(ele, listener, eventObj) {
10644 var selector = listener.qualifier;
10645
10646 if (selector != null) {
10647 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10648 }
10649
10650 return true;
10651 },
10652 addEventFields: function addEventFields(ele, evt) {
10653 evt.cy = ele.cy();
10654 evt.target = ele;
10655 },
10656 callbackContext: function callbackContext(ele, listener, eventObj) {
10657 return listener.qualifier != null ? eventObj.target : ele;
10658 },
10659 beforeEmit: function beforeEmit(context, listener
10660 /*, eventObj*/
10661 ) {
10662 if (listener.conf && listener.conf.once) {
10663 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10664 }
10665 },
10666 bubble: function bubble() {
10667 return true;
10668 },
10669 parent: function parent(ele) {
10670 return ele.isChild() ? ele.parent() : ele.cy();
10671 }
10672};
10673
10674var argSelector$1 = function argSelector(arg) {
10675 if (string(arg)) {
10676 return new Selector(arg);
10677 } else {
10678 return arg;
10679 }
10680};
10681
10682var elesfn$9 = {
10683 createEmitter: function createEmitter() {
10684 for (var i = 0; i < this.length; i++) {
10685 var ele = this[i];
10686 var _p = ele._private;
10687
10688 if (!_p.emitter) {
10689 _p.emitter = new Emitter(emitterOptions$1, ele);
10690 }
10691 }
10692
10693 return this;
10694 },
10695 emitter: function emitter() {
10696 return this._private.emitter;
10697 },
10698 on: function on(events, selector, callback) {
10699 var argSel = argSelector$1(selector);
10700
10701 for (var i = 0; i < this.length; i++) {
10702 var ele = this[i];
10703 ele.emitter().on(events, argSel, callback);
10704 }
10705
10706 return this;
10707 },
10708 removeListener: function removeListener(events, selector, callback) {
10709 var argSel = argSelector$1(selector);
10710
10711 for (var i = 0; i < this.length; i++) {
10712 var ele = this[i];
10713 ele.emitter().removeListener(events, argSel, callback);
10714 }
10715
10716 return this;
10717 },
10718 removeAllListeners: function removeAllListeners() {
10719 for (var i = 0; i < this.length; i++) {
10720 var ele = this[i];
10721 ele.emitter().removeAllListeners();
10722 }
10723
10724 return this;
10725 },
10726 one: function one(events, selector, callback) {
10727 var argSel = argSelector$1(selector);
10728
10729 for (var i = 0; i < this.length; i++) {
10730 var ele = this[i];
10731 ele.emitter().one(events, argSel, callback);
10732 }
10733
10734 return this;
10735 },
10736 once: function once(events, selector, callback) {
10737 var argSel = argSelector$1(selector);
10738
10739 for (var i = 0; i < this.length; i++) {
10740 var ele = this[i];
10741 ele.emitter().on(events, argSel, callback, {
10742 once: true,
10743 onceCollection: this
10744 });
10745 }
10746 },
10747 emit: function emit(events, extraParams) {
10748 for (var i = 0; i < this.length; i++) {
10749 var ele = this[i];
10750 ele.emitter().emit(events, extraParams);
10751 }
10752
10753 return this;
10754 },
10755 emitAndNotify: function emitAndNotify(event, extraParams) {
10756 // for internal use only
10757 if (this.length === 0) {
10758 return;
10759 } // empty collections don't need to notify anything
10760 // notify renderer
10761
10762
10763 this.cy().notify(event, this);
10764 this.emit(event, extraParams);
10765 return this;
10766 }
10767};
10768define.eventAliasesOn(elesfn$9);
10769
10770var elesfn$8 = {
10771 nodes: function nodes(selector) {
10772 return this.filter(function (ele) {
10773 return ele.isNode();
10774 }).filter(selector);
10775 },
10776 edges: function edges(selector) {
10777 return this.filter(function (ele) {
10778 return ele.isEdge();
10779 }).filter(selector);
10780 },
10781 // internal helper to get nodes and edges as separate collections with single iteration over elements
10782 byGroup: function byGroup() {
10783 var nodes = this.spawn();
10784 var edges = this.spawn();
10785
10786 for (var i = 0; i < this.length; i++) {
10787 var ele = this[i];
10788
10789 if (ele.isNode()) {
10790 nodes.push(ele);
10791 } else {
10792 edges.push(ele);
10793 }
10794 }
10795
10796 return {
10797 nodes: nodes,
10798 edges: edges
10799 };
10800 },
10801 filter: function filter(_filter, thisArg) {
10802 if (_filter === undefined) {
10803 // check this first b/c it's the most common/performant case
10804 return this;
10805 } else if (string(_filter) || elementOrCollection(_filter)) {
10806 return new Selector(_filter).filter(this);
10807 } else if (fn$6(_filter)) {
10808 var filterEles = this.spawn();
10809 var eles = this;
10810
10811 for (var i = 0; i < eles.length; i++) {
10812 var ele = eles[i];
10813 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10814
10815 if (include) {
10816 filterEles.push(ele);
10817 }
10818 }
10819
10820 return filterEles;
10821 }
10822
10823 return this.spawn(); // if not handled by above, give 'em an empty collection
10824 },
10825 not: function not(toRemove) {
10826 if (!toRemove) {
10827 return this;
10828 } else {
10829 if (string(toRemove)) {
10830 toRemove = this.filter(toRemove);
10831 }
10832
10833 var elements = this.spawn();
10834
10835 for (var i = 0; i < this.length; i++) {
10836 var element = this[i];
10837 var remove = toRemove.has(element);
10838
10839 if (!remove) {
10840 elements.push(element);
10841 }
10842 }
10843
10844 return elements;
10845 }
10846 },
10847 absoluteComplement: function absoluteComplement() {
10848 var cy = this.cy();
10849 return cy.mutableElements().not(this);
10850 },
10851 intersect: function intersect(other) {
10852 // if a selector is specified, then filter by it instead
10853 if (string(other)) {
10854 var selector = other;
10855 return this.filter(selector);
10856 }
10857
10858 var elements = this.spawn();
10859 var col1 = this;
10860 var col2 = other;
10861 var col1Smaller = this.length < other.length;
10862 var colS = col1Smaller ? col1 : col2;
10863 var colL = col1Smaller ? col2 : col1;
10864
10865 for (var i = 0; i < colS.length; i++) {
10866 var ele = colS[i];
10867
10868 if (colL.has(ele)) {
10869 elements.push(ele);
10870 }
10871 }
10872
10873 return elements;
10874 },
10875 xor: function xor(other) {
10876 var cy = this._private.cy;
10877
10878 if (string(other)) {
10879 other = cy.$(other);
10880 }
10881
10882 var elements = this.spawn();
10883 var col1 = this;
10884 var col2 = other;
10885
10886 var add = function add(col, other) {
10887 for (var i = 0; i < col.length; i++) {
10888 var ele = col[i];
10889 var id = ele._private.data.id;
10890 var inOther = other.hasElementWithId(id);
10891
10892 if (!inOther) {
10893 elements.push(ele);
10894 }
10895 }
10896 };
10897
10898 add(col1, col2);
10899 add(col2, col1);
10900 return elements;
10901 },
10902 diff: function diff(other) {
10903 var cy = this._private.cy;
10904
10905 if (string(other)) {
10906 other = cy.$(other);
10907 }
10908
10909 var left = this.spawn();
10910 var right = this.spawn();
10911 var both = this.spawn();
10912 var col1 = this;
10913 var col2 = other;
10914
10915 var add = function add(col, other, retEles) {
10916 for (var i = 0; i < col.length; i++) {
10917 var ele = col[i];
10918 var id = ele._private.data.id;
10919 var inOther = other.hasElementWithId(id);
10920
10921 if (inOther) {
10922 both.merge(ele);
10923 } else {
10924 retEles.push(ele);
10925 }
10926 }
10927 };
10928
10929 add(col1, col2, left);
10930 add(col2, col1, right);
10931 return {
10932 left: left,
10933 right: right,
10934 both: both
10935 };
10936 },
10937 add: function add(toAdd) {
10938 var cy = this._private.cy;
10939
10940 if (!toAdd) {
10941 return this;
10942 }
10943
10944 if (string(toAdd)) {
10945 var selector = toAdd;
10946 toAdd = cy.mutableElements().filter(selector);
10947 }
10948
10949 var elements = this.spawnSelf();
10950
10951 for (var i = 0; i < toAdd.length; i++) {
10952 var ele = toAdd[i];
10953 var add = !this.has(ele);
10954
10955 if (add) {
10956 elements.push(ele);
10957 }
10958 }
10959
10960 return elements;
10961 },
10962 // in place merge on calling collection
10963 merge: function merge(toAdd) {
10964 var _p = this._private;
10965 var cy = _p.cy;
10966
10967 if (!toAdd) {
10968 return this;
10969 }
10970
10971 if (toAdd && string(toAdd)) {
10972 var selector = toAdd;
10973 toAdd = cy.mutableElements().filter(selector);
10974 }
10975
10976 var map = _p.map;
10977
10978 for (var i = 0; i < toAdd.length; i++) {
10979 var toAddEle = toAdd[i];
10980 var id = toAddEle._private.data.id;
10981 var add = !map.has(id);
10982
10983 if (add) {
10984 var index = this.length++;
10985 this[index] = toAddEle;
10986 map.set(id, {
10987 ele: toAddEle,
10988 index: index
10989 });
10990 }
10991 }
10992
10993 return this; // chaining
10994 },
10995 unmergeAt: function unmergeAt(i) {
10996 var ele = this[i];
10997 var id = ele.id();
10998 var _p = this._private;
10999 var map = _p.map; // remove ele
11000
11001 this[i] = undefined;
11002 map["delete"](id);
11003 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
11004
11005 if (this.length > 1 && !unmergedLastEle) {
11006 var lastEleI = this.length - 1;
11007 var lastEle = this[lastEleI];
11008 var lastEleId = lastEle._private.data.id;
11009 this[lastEleI] = undefined;
11010 this[i] = lastEle;
11011 map.set(lastEleId, {
11012 ele: lastEle,
11013 index: i
11014 });
11015 } // the collection is now 1 ele smaller
11016
11017
11018 this.length--;
11019 return this;
11020 },
11021 // remove single ele in place in calling collection
11022 unmergeOne: function unmergeOne(ele) {
11023 ele = ele[0];
11024 var _p = this._private;
11025 var id = ele._private.data.id;
11026 var map = _p.map;
11027 var entry = map.get(id);
11028
11029 if (!entry) {
11030 return this; // no need to remove
11031 }
11032
11033 var i = entry.index;
11034 this.unmergeAt(i);
11035 return this;
11036 },
11037 // remove eles in place on calling collection
11038 unmerge: function unmerge(toRemove) {
11039 var cy = this._private.cy;
11040
11041 if (!toRemove) {
11042 return this;
11043 }
11044
11045 if (toRemove && string(toRemove)) {
11046 var selector = toRemove;
11047 toRemove = cy.mutableElements().filter(selector);
11048 }
11049
11050 for (var i = 0; i < toRemove.length; i++) {
11051 this.unmergeOne(toRemove[i]);
11052 }
11053
11054 return this; // chaining
11055 },
11056 unmergeBy: function unmergeBy(toRmFn) {
11057 for (var i = this.length - 1; i >= 0; i--) {
11058 var ele = this[i];
11059
11060 if (toRmFn(ele)) {
11061 this.unmergeAt(i);
11062 }
11063 }
11064
11065 return this;
11066 },
11067 map: function map(mapFn, thisArg) {
11068 var arr = [];
11069 var eles = this;
11070
11071 for (var i = 0; i < eles.length; i++) {
11072 var ele = eles[i];
11073 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11074 arr.push(ret);
11075 }
11076
11077 return arr;
11078 },
11079 reduce: function reduce(fn, initialValue) {
11080 var val = initialValue;
11081 var eles = this;
11082
11083 for (var i = 0; i < eles.length; i++) {
11084 val = fn(val, eles[i], i, eles);
11085 }
11086
11087 return val;
11088 },
11089 max: function max(valFn, thisArg) {
11090 var max = -Infinity;
11091 var maxEle;
11092 var eles = this;
11093
11094 for (var i = 0; i < eles.length; i++) {
11095 var ele = eles[i];
11096 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11097
11098 if (val > max) {
11099 max = val;
11100 maxEle = ele;
11101 }
11102 }
11103
11104 return {
11105 value: max,
11106 ele: maxEle
11107 };
11108 },
11109 min: function min(valFn, thisArg) {
11110 var min = Infinity;
11111 var minEle;
11112 var eles = this;
11113
11114 for (var i = 0; i < eles.length; i++) {
11115 var ele = eles[i];
11116 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11117
11118 if (val < min) {
11119 min = val;
11120 minEle = ele;
11121 }
11122 }
11123
11124 return {
11125 value: min,
11126 ele: minEle
11127 };
11128 }
11129}; // aliases
11130
11131var fn$1 = elesfn$8;
11132fn$1['u'] = fn$1['|'] = fn$1['+'] = fn$1.union = fn$1.or = fn$1.add;
11133fn$1['\\'] = fn$1['!'] = fn$1['-'] = fn$1.difference = fn$1.relativeComplement = fn$1.subtract = fn$1.not;
11134fn$1['n'] = fn$1['&'] = fn$1['.'] = fn$1.and = fn$1.intersection = fn$1.intersect;
11135fn$1['^'] = fn$1['(+)'] = fn$1['(-)'] = fn$1.symmetricDifference = fn$1.symdiff = fn$1.xor;
11136fn$1.fnFilter = fn$1.filterFn = fn$1.stdFilter = fn$1.filter;
11137fn$1.complement = fn$1.abscomp = fn$1.absoluteComplement;
11138
11139var elesfn$7 = {
11140 isNode: function isNode() {
11141 return this.group() === 'nodes';
11142 },
11143 isEdge: function isEdge() {
11144 return this.group() === 'edges';
11145 },
11146 isLoop: function isLoop() {
11147 return this.isEdge() && this.source()[0] === this.target()[0];
11148 },
11149 isSimple: function isSimple() {
11150 return this.isEdge() && this.source()[0] !== this.target()[0];
11151 },
11152 group: function group() {
11153 var ele = this[0];
11154
11155 if (ele) {
11156 return ele._private.group;
11157 }
11158 }
11159};
11160
11161/**
11162 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11163 * and z-index (low to high). These styles affect how this applies:
11164 *
11165 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11166 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11167 * root to leaves of the compound graph. The last drawn is `top`.
11168 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11169 * `manual` ignores this convention and draws based on the `z-index` value setting.
11170 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11171 * `z-index` will be drawn on top of an element with a lower `z-index`.
11172 */
11173
11174var zIndexSort = function zIndexSort(a, b) {
11175 var cy = a.cy();
11176 var hasCompoundNodes = cy.hasCompoundNodes();
11177
11178 function getDepth(ele) {
11179 var style = ele.pstyle('z-compound-depth');
11180
11181 if (style.value === 'auto') {
11182 return hasCompoundNodes ? ele.zDepth() : 0;
11183 } else if (style.value === 'bottom') {
11184 return -1;
11185 } else if (style.value === 'top') {
11186 return MAX_INT$1;
11187 } // 'orphan'
11188
11189
11190 return 0;
11191 }
11192
11193 var depthDiff = getDepth(a) - getDepth(b);
11194
11195 if (depthDiff !== 0) {
11196 return depthDiff;
11197 }
11198
11199 function getEleDepth(ele) {
11200 var style = ele.pstyle('z-index-compare');
11201
11202 if (style.value === 'auto') {
11203 return ele.isNode() ? 1 : 0;
11204 } // 'manual'
11205
11206
11207 return 0;
11208 }
11209
11210 var eleDiff = getEleDepth(a) - getEleDepth(b);
11211
11212 if (eleDiff !== 0) {
11213 return eleDiff;
11214 }
11215
11216 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11217
11218 if (zDiff !== 0) {
11219 return zDiff;
11220 } // compare indices in the core (order added to graph w/ last on top)
11221
11222
11223 return a.poolIndex() - b.poolIndex();
11224};
11225
11226var elesfn$6 = {
11227 forEach: function forEach(fn, thisArg) {
11228 if (fn$6(fn)) {
11229 var N = this.length;
11230
11231 for (var i = 0; i < N; i++) {
11232 var ele = this[i];
11233 var ret = thisArg ? fn.apply(thisArg, [ele, i, this]) : fn(ele, i, this);
11234
11235 if (ret === false) {
11236 break;
11237 } // exit each early on return false
11238
11239 }
11240 }
11241
11242 return this;
11243 },
11244 toArray: function toArray() {
11245 var array = [];
11246
11247 for (var i = 0; i < this.length; i++) {
11248 array.push(this[i]);
11249 }
11250
11251 return array;
11252 },
11253 slice: function slice(start, end) {
11254 var array = [];
11255 var thisSize = this.length;
11256
11257 if (end == null) {
11258 end = thisSize;
11259 }
11260
11261 if (start == null) {
11262 start = 0;
11263 }
11264
11265 if (start < 0) {
11266 start = thisSize + start;
11267 }
11268
11269 if (end < 0) {
11270 end = thisSize + end;
11271 }
11272
11273 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11274 array.push(this[i]);
11275 }
11276
11277 return this.spawn(array);
11278 },
11279 size: function size() {
11280 return this.length;
11281 },
11282 eq: function eq(i) {
11283 return this[i] || this.spawn();
11284 },
11285 first: function first() {
11286 return this[0] || this.spawn();
11287 },
11288 last: function last() {
11289 return this[this.length - 1] || this.spawn();
11290 },
11291 empty: function empty() {
11292 return this.length === 0;
11293 },
11294 nonempty: function nonempty() {
11295 return !this.empty();
11296 },
11297 sort: function sort(sortFn) {
11298 if (!fn$6(sortFn)) {
11299 return this;
11300 }
11301
11302 var sorted = this.toArray().sort(sortFn);
11303 return this.spawn(sorted);
11304 },
11305 sortByZIndex: function sortByZIndex() {
11306 return this.sort(zIndexSort);
11307 },
11308 zDepth: function zDepth() {
11309 var ele = this[0];
11310
11311 if (!ele) {
11312 return undefined;
11313 } // let cy = ele.cy();
11314
11315
11316 var _p = ele._private;
11317 var group = _p.group;
11318
11319 if (group === 'nodes') {
11320 var depth = _p.data.parent ? ele.parents().size() : 0;
11321
11322 if (!ele.isParent()) {
11323 return MAX_INT$1 - 1; // childless nodes always on top
11324 }
11325
11326 return depth;
11327 } else {
11328 var src = _p.source;
11329 var tgt = _p.target;
11330 var srcDepth = src.zDepth();
11331 var tgtDepth = tgt.zDepth();
11332 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11333 }
11334 }
11335};
11336elesfn$6.each = elesfn$6.forEach;
11337
11338var defineSymbolIterator = function defineSymbolIterator() {
11339 var typeofUndef = "undefined" ;
11340 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11341
11342 if (isIteratorSupported) {
11343 elesfn$6[Symbol.iterator] = function () {
11344 var _this = this;
11345
11346 // eslint-disable-line no-undef
11347 var entry = {
11348 value: undefined,
11349 done: false
11350 };
11351 var i = 0;
11352 var length = this.length;
11353 return _defineProperty({
11354 next: function next() {
11355 if (i < length) {
11356 entry.value = _this[i++];
11357 } else {
11358 entry.value = undefined;
11359 entry.done = true;
11360 }
11361
11362 return entry;
11363 }
11364 }, Symbol.iterator, function () {
11365 // eslint-disable-line no-undef
11366 return this;
11367 });
11368 };
11369 }
11370};
11371
11372defineSymbolIterator();
11373
11374var getLayoutDimensionOptions = defaults$g({
11375 nodeDimensionsIncludeLabels: false
11376});
11377var elesfn$5 = {
11378 // Calculates and returns node dimensions { x, y } based on options given
11379 layoutDimensions: function layoutDimensions(options) {
11380 options = getLayoutDimensionOptions(options);
11381 var dims;
11382
11383 if (!this.takesUpSpace()) {
11384 dims = {
11385 w: 0,
11386 h: 0
11387 };
11388 } else if (options.nodeDimensionsIncludeLabels) {
11389 var bbDim = this.boundingBox();
11390 dims = {
11391 w: bbDim.w,
11392 h: bbDim.h
11393 };
11394 } else {
11395 dims = {
11396 w: this.outerWidth(),
11397 h: this.outerHeight()
11398 };
11399 } // sanitise the dimensions for external layouts (avoid division by zero)
11400
11401
11402 if (dims.w === 0 || dims.h === 0) {
11403 dims.w = dims.h = 1;
11404 }
11405
11406 return dims;
11407 },
11408 // using standard layout options, apply position function (w/ or w/o animation)
11409 layoutPositions: function layoutPositions(layout, options, fn) {
11410 var nodes = this.nodes().filter(function (n) {
11411 return !n.isParent();
11412 });
11413 var cy = this.cy();
11414 var layoutEles = options.eles; // nodes & edges
11415
11416 var getMemoizeKey = function getMemoizeKey(node) {
11417 return node.id();
11418 };
11419
11420 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11421
11422 layout.emit({
11423 type: 'layoutstart',
11424 layout: layout
11425 });
11426 layout.animations = [];
11427
11428 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11429 var center = {
11430 x: nodesBb.x1 + nodesBb.w / 2,
11431 y: nodesBb.y1 + nodesBb.h / 2
11432 };
11433 var spacingVector = {
11434 // scale from center of bounding box (not necessarily 0,0)
11435 x: (pos.x - center.x) * spacing,
11436 y: (pos.y - center.y) * spacing
11437 };
11438 return {
11439 x: center.x + spacingVector.x,
11440 y: center.y + spacingVector.y
11441 };
11442 };
11443
11444 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11445
11446 var spacingBb = function spacingBb() {
11447 if (!useSpacingFactor) {
11448 return null;
11449 }
11450
11451 var bb = makeBoundingBox();
11452
11453 for (var i = 0; i < nodes.length; i++) {
11454 var node = nodes[i];
11455 var pos = fnMem(node, i);
11456 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11457 }
11458
11459 return bb;
11460 };
11461
11462 var bb = spacingBb();
11463 var getFinalPos = memoize(function (node, i) {
11464 var newPos = fnMem(node, i);
11465
11466 if (useSpacingFactor) {
11467 var spacing = Math.abs(options.spacingFactor);
11468 newPos = calculateSpacing(spacing, bb, newPos);
11469 }
11470
11471 if (options.transform != null) {
11472 newPos = options.transform(node, newPos);
11473 }
11474
11475 return newPos;
11476 }, getMemoizeKey);
11477
11478 if (options.animate) {
11479 for (var i = 0; i < nodes.length; i++) {
11480 var node = nodes[i];
11481 var newPos = getFinalPos(node, i);
11482 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11483
11484 if (animateNode) {
11485 var ani = node.animation({
11486 position: newPos,
11487 duration: options.animationDuration,
11488 easing: options.animationEasing
11489 });
11490 layout.animations.push(ani);
11491 } else {
11492 node.position(newPos);
11493 }
11494 }
11495
11496 if (options.fit) {
11497 var fitAni = cy.animation({
11498 fit: {
11499 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11500 padding: options.padding
11501 },
11502 duration: options.animationDuration,
11503 easing: options.animationEasing
11504 });
11505 layout.animations.push(fitAni);
11506 } else if (options.zoom !== undefined && options.pan !== undefined) {
11507 var zoomPanAni = cy.animation({
11508 zoom: options.zoom,
11509 pan: options.pan,
11510 duration: options.animationDuration,
11511 easing: options.animationEasing
11512 });
11513 layout.animations.push(zoomPanAni);
11514 }
11515
11516 layout.animations.forEach(function (ani) {
11517 return ani.play();
11518 });
11519 layout.one('layoutready', options.ready);
11520 layout.emit({
11521 type: 'layoutready',
11522 layout: layout
11523 });
11524 Promise$1.all(layout.animations.map(function (ani) {
11525 return ani.promise();
11526 })).then(function () {
11527 layout.one('layoutstop', options.stop);
11528 layout.emit({
11529 type: 'layoutstop',
11530 layout: layout
11531 });
11532 });
11533 } else {
11534 nodes.positions(getFinalPos);
11535
11536 if (options.fit) {
11537 cy.fit(options.eles, options.padding);
11538 }
11539
11540 if (options.zoom != null) {
11541 cy.zoom(options.zoom);
11542 }
11543
11544 if (options.pan) {
11545 cy.pan(options.pan);
11546 }
11547
11548 layout.one('layoutready', options.ready);
11549 layout.emit({
11550 type: 'layoutready',
11551 layout: layout
11552 });
11553 layout.one('layoutstop', options.stop);
11554 layout.emit({
11555 type: 'layoutstop',
11556 layout: layout
11557 });
11558 }
11559
11560 return this; // chaining
11561 },
11562 layout: function layout(options) {
11563 var cy = this.cy();
11564 return cy.makeLayout(extend({}, options, {
11565 eles: this
11566 }));
11567 }
11568}; // aliases:
11569
11570elesfn$5.createLayout = elesfn$5.makeLayout = elesfn$5.layout;
11571
11572function styleCache(key, fn, ele) {
11573 var _p = ele._private;
11574 var cache = _p.styleCache = _p.styleCache || [];
11575 var val;
11576
11577 if ((val = cache[key]) != null) {
11578 return val;
11579 } else {
11580 val = cache[key] = fn(ele);
11581 return val;
11582 }
11583}
11584
11585function cacheStyleFunction(key, fn) {
11586 key = hashString(key);
11587 return function cachedStyleFunction(ele) {
11588 return styleCache(key, fn, ele);
11589 };
11590}
11591
11592function cachePrototypeStyleFunction(key, fn) {
11593 key = hashString(key);
11594
11595 var selfFn = function selfFn(ele) {
11596 return fn.call(ele);
11597 };
11598
11599 return function cachedPrototypeStyleFunction() {
11600 var ele = this[0];
11601
11602 if (ele) {
11603 return styleCache(key, selfFn, ele);
11604 }
11605 };
11606}
11607
11608var elesfn$4 = {
11609 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11610 var cy = this.cy();
11611 var renderer = cy.renderer();
11612 var styleEnabled = cy.styleEnabled();
11613
11614 if (renderer && styleEnabled) {
11615 renderer.recalculateRenderedStyle(this, useCache);
11616 }
11617
11618 return this;
11619 },
11620 dirtyStyleCache: function dirtyStyleCache() {
11621 var cy = this.cy();
11622
11623 var dirty = function dirty(ele) {
11624 return ele._private.styleCache = null;
11625 };
11626
11627 if (cy.hasCompoundNodes()) {
11628 var eles;
11629 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11630 eles.merge(eles.connectedEdges());
11631 eles.forEach(dirty);
11632 } else {
11633 this.forEach(function (ele) {
11634 dirty(ele);
11635 ele.connectedEdges().forEach(dirty);
11636 });
11637 }
11638
11639 return this;
11640 },
11641 // fully updates (recalculates) the style for the elements
11642 updateStyle: function updateStyle(notifyRenderer) {
11643 var cy = this._private.cy;
11644
11645 if (!cy.styleEnabled()) {
11646 return this;
11647 }
11648
11649 if (cy.batching()) {
11650 var bEles = cy._private.batchStyleEles;
11651 bEles.merge(this);
11652 return this; // chaining and exit early when batching
11653 }
11654
11655 var hasCompounds = cy.hasCompoundNodes();
11656 var updatedEles = this;
11657 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11658
11659 if (hasCompounds) {
11660 // then add everything up and down for compound selector checks
11661 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11662 } // let changedEles = style.apply( updatedEles );
11663
11664
11665 var changedEles = updatedEles;
11666
11667 if (notifyRenderer) {
11668 changedEles.emitAndNotify('style'); // let renderer know we changed style
11669 } else {
11670 changedEles.emit('style'); // just fire the event
11671 }
11672
11673 updatedEles.forEach(function (ele) {
11674 return ele._private.styleDirty = true;
11675 });
11676 return this; // chaining
11677 },
11678 // private: clears dirty flag and recalculates style
11679 cleanStyle: function cleanStyle() {
11680 var cy = this.cy();
11681
11682 if (!cy.styleEnabled()) {
11683 return;
11684 }
11685
11686 for (var i = 0; i < this.length; i++) {
11687 var ele = this[i];
11688
11689 if (ele._private.styleDirty) {
11690 // n.b. this flag should be set before apply() to avoid potential infinite recursion
11691 ele._private.styleDirty = false;
11692 cy.style().apply(ele);
11693 }
11694 }
11695 },
11696 // get the internal parsed style object for the specified property
11697 parsedStyle: function parsedStyle(property) {
11698 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11699 var ele = this[0];
11700 var cy = ele.cy();
11701
11702 if (!cy.styleEnabled()) {
11703 return;
11704 }
11705
11706 if (ele) {
11707 this.cleanStyle();
11708 var overriddenStyle = ele._private.style[property];
11709
11710 if (overriddenStyle != null) {
11711 return overriddenStyle;
11712 } else if (includeNonDefault) {
11713 return cy.style().getDefaultProperty(property);
11714 } else {
11715 return null;
11716 }
11717 }
11718 },
11719 numericStyle: function numericStyle(property) {
11720 var ele = this[0];
11721
11722 if (!ele.cy().styleEnabled()) {
11723 return;
11724 }
11725
11726 if (ele) {
11727 var pstyle = ele.pstyle(property);
11728 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11729 }
11730 },
11731 numericStyleUnits: function numericStyleUnits(property) {
11732 var ele = this[0];
11733
11734 if (!ele.cy().styleEnabled()) {
11735 return;
11736 }
11737
11738 if (ele) {
11739 return ele.pstyle(property).units;
11740 }
11741 },
11742 // get the specified css property as a rendered value (i.e. on-screen value)
11743 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11744 renderedStyle: function renderedStyle(property) {
11745 var cy = this.cy();
11746
11747 if (!cy.styleEnabled()) {
11748 return this;
11749 }
11750
11751 var ele = this[0];
11752
11753 if (ele) {
11754 return cy.style().getRenderedStyle(ele, property);
11755 }
11756 },
11757 // read the calculated css style of the element or override the style (via a bypass)
11758 style: function style(name, value) {
11759 var cy = this.cy();
11760
11761 if (!cy.styleEnabled()) {
11762 return this;
11763 }
11764
11765 var updateTransitions = false;
11766 var style = cy.style();
11767
11768 if (plainObject(name)) {
11769 // then extend the bypass
11770 var props = name;
11771 style.applyBypass(this, props, updateTransitions);
11772 this.emitAndNotify('style'); // let the renderer know we've updated style
11773 } else if (string(name)) {
11774 if (value === undefined) {
11775 // then get the property from the style
11776 var ele = this[0];
11777
11778 if (ele) {
11779 return style.getStylePropertyValue(ele, name);
11780 } else {
11781 // empty collection => can't get any value
11782 return;
11783 }
11784 } else {
11785 // then set the bypass with the property value
11786 style.applyBypass(this, name, value, updateTransitions);
11787 this.emitAndNotify('style'); // let the renderer know we've updated style
11788 }
11789 } else if (name === undefined) {
11790 var _ele = this[0];
11791
11792 if (_ele) {
11793 return style.getRawStyle(_ele);
11794 } else {
11795 // empty collection => can't get any value
11796 return;
11797 }
11798 }
11799
11800 return this; // chaining
11801 },
11802 removeStyle: function removeStyle(names) {
11803 var cy = this.cy();
11804
11805 if (!cy.styleEnabled()) {
11806 return this;
11807 }
11808
11809 var updateTransitions = false;
11810 var style = cy.style();
11811 var eles = this;
11812
11813 if (names === undefined) {
11814 for (var i = 0; i < eles.length; i++) {
11815 var ele = eles[i];
11816 style.removeAllBypasses(ele, updateTransitions);
11817 }
11818 } else {
11819 names = names.split(/\s+/);
11820
11821 for (var _i = 0; _i < eles.length; _i++) {
11822 var _ele2 = eles[_i];
11823 style.removeBypasses(_ele2, names, updateTransitions);
11824 }
11825 }
11826
11827 this.emitAndNotify('style'); // let the renderer know we've updated style
11828
11829 return this; // chaining
11830 },
11831 show: function show() {
11832 this.css('display', 'element');
11833 return this; // chaining
11834 },
11835 hide: function hide() {
11836 this.css('display', 'none');
11837 return this; // chaining
11838 },
11839 effectiveOpacity: function effectiveOpacity() {
11840 var cy = this.cy();
11841
11842 if (!cy.styleEnabled()) {
11843 return 1;
11844 }
11845
11846 var hasCompoundNodes = cy.hasCompoundNodes();
11847 var ele = this[0];
11848
11849 if (ele) {
11850 var _p = ele._private;
11851 var parentOpacity = ele.pstyle('opacity').value;
11852
11853 if (!hasCompoundNodes) {
11854 return parentOpacity;
11855 }
11856
11857 var parents = !_p.data.parent ? null : ele.parents();
11858
11859 if (parents) {
11860 for (var i = 0; i < parents.length; i++) {
11861 var parent = parents[i];
11862 var opacity = parent.pstyle('opacity').value;
11863 parentOpacity = opacity * parentOpacity;
11864 }
11865 }
11866
11867 return parentOpacity;
11868 }
11869 },
11870 transparent: function transparent() {
11871 var cy = this.cy();
11872
11873 if (!cy.styleEnabled()) {
11874 return false;
11875 }
11876
11877 var ele = this[0];
11878 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11879
11880 if (ele) {
11881 if (!hasCompoundNodes) {
11882 return ele.pstyle('opacity').value === 0;
11883 } else {
11884 return ele.effectiveOpacity() === 0;
11885 }
11886 }
11887 },
11888 backgrounding: function backgrounding() {
11889 var cy = this.cy();
11890
11891 if (!cy.styleEnabled()) {
11892 return false;
11893 }
11894
11895 var ele = this[0];
11896 return ele._private.backgrounding ? true : false;
11897 }
11898};
11899
11900function checkCompound(ele, parentOk) {
11901 var _p = ele._private;
11902 var parents = _p.data.parent ? ele.parents() : null;
11903
11904 if (parents) {
11905 for (var i = 0; i < parents.length; i++) {
11906 var parent = parents[i];
11907
11908 if (!parentOk(parent)) {
11909 return false;
11910 }
11911 }
11912 }
11913
11914 return true;
11915}
11916
11917function defineDerivedStateFunction(specs) {
11918 var ok = specs.ok;
11919 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11920 var parentOk = specs.parentOk || specs.ok;
11921 return function () {
11922 var cy = this.cy();
11923
11924 if (!cy.styleEnabled()) {
11925 return true;
11926 }
11927
11928 var ele = this[0];
11929 var hasCompoundNodes = cy.hasCompoundNodes();
11930
11931 if (ele) {
11932 var _p = ele._private;
11933
11934 if (!ok(ele)) {
11935 return false;
11936 }
11937
11938 if (ele.isNode()) {
11939 return !hasCompoundNodes || checkCompound(ele, parentOk);
11940 } else {
11941 var src = _p.source;
11942 var tgt = _p.target;
11943 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11944 }
11945 }
11946 };
11947}
11948
11949var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11950 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11951});
11952elesfn$4.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11953 ok: eleTakesUpSpace
11954}));
11955var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11956 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11957});
11958var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11959 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11960});
11961elesfn$4.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11962 ok: eleInteractive,
11963 parentOk: parentInteractive,
11964 edgeOkViaNode: eleTakesUpSpace
11965}));
11966
11967elesfn$4.noninteractive = function () {
11968 var ele = this[0];
11969
11970 if (ele) {
11971 return !ele.interactive();
11972 }
11973};
11974
11975var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11976 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11977});
11978var edgeVisibleViaNode = eleTakesUpSpace;
11979elesfn$4.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11980 ok: eleVisible,
11981 edgeOkViaNode: edgeVisibleViaNode
11982}));
11983
11984elesfn$4.hidden = function () {
11985 var ele = this[0];
11986
11987 if (ele) {
11988 return !ele.visible();
11989 }
11990};
11991
11992elesfn$4.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11993 if (!this.cy().styleEnabled()) {
11994 return false;
11995 }
11996
11997 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11998});
11999elesfn$4.bypass = elesfn$4.css = elesfn$4.style;
12000elesfn$4.renderedCss = elesfn$4.renderedStyle;
12001elesfn$4.removeBypass = elesfn$4.removeCss = elesfn$4.removeStyle;
12002elesfn$4.pstyle = elesfn$4.parsedStyle;
12003
12004var elesfn$3 = {};
12005
12006function defineSwitchFunction(params) {
12007 return function () {
12008 var args = arguments;
12009 var changedEles = []; // e.g. cy.nodes().select( data, handler )
12010
12011 if (args.length === 2) {
12012 var data = args[0];
12013 var handler = args[1];
12014 this.on(params.event, data, handler);
12015 } // e.g. cy.nodes().select( handler )
12016 else if (args.length === 1 && fn$6(args[0])) {
12017 var _handler = args[0];
12018 this.on(params.event, _handler);
12019 } // e.g. cy.nodes().select()
12020 // e.g. (private) cy.nodes().select(['tapselect'])
12021 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12022 var addlEvents = args.length === 1 ? args[0] : null;
12023
12024 for (var i = 0; i < this.length; i++) {
12025 var ele = this[i];
12026 var able = !params.ableField || ele._private[params.ableField];
12027 var changed = ele._private[params.field] != params.value;
12028
12029 if (params.overrideAble) {
12030 var overrideAble = params.overrideAble(ele);
12031
12032 if (overrideAble !== undefined) {
12033 able = overrideAble;
12034
12035 if (!overrideAble) {
12036 return this;
12037 } // to save cycles assume not able for all on override
12038
12039 }
12040 }
12041
12042 if (able) {
12043 ele._private[params.field] = params.value;
12044
12045 if (changed) {
12046 changedEles.push(ele);
12047 }
12048 }
12049 }
12050
12051 var changedColl = this.spawn(changedEles);
12052 changedColl.updateStyle(); // change of state => possible change of style
12053
12054 changedColl.emit(params.event);
12055
12056 if (addlEvents) {
12057 changedColl.emit(addlEvents);
12058 }
12059 }
12060
12061 return this;
12062 };
12063}
12064
12065function defineSwitchSet(params) {
12066 elesfn$3[params.field] = function () {
12067 var ele = this[0];
12068
12069 if (ele) {
12070 if (params.overrideField) {
12071 var val = params.overrideField(ele);
12072
12073 if (val !== undefined) {
12074 return val;
12075 }
12076 }
12077
12078 return ele._private[params.field];
12079 }
12080 };
12081
12082 elesfn$3[params.on] = defineSwitchFunction({
12083 event: params.on,
12084 field: params.field,
12085 ableField: params.ableField,
12086 overrideAble: params.overrideAble,
12087 value: true
12088 });
12089 elesfn$3[params.off] = defineSwitchFunction({
12090 event: params.off,
12091 field: params.field,
12092 ableField: params.ableField,
12093 overrideAble: params.overrideAble,
12094 value: false
12095 });
12096}
12097
12098defineSwitchSet({
12099 field: 'locked',
12100 overrideField: function overrideField(ele) {
12101 return ele.cy().autolock() ? true : undefined;
12102 },
12103 on: 'lock',
12104 off: 'unlock'
12105});
12106defineSwitchSet({
12107 field: 'grabbable',
12108 overrideField: function overrideField(ele) {
12109 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12110 },
12111 on: 'grabify',
12112 off: 'ungrabify'
12113});
12114defineSwitchSet({
12115 field: 'selected',
12116 ableField: 'selectable',
12117 overrideAble: function overrideAble(ele) {
12118 return ele.cy().autounselectify() ? false : undefined;
12119 },
12120 on: 'select',
12121 off: 'unselect'
12122});
12123defineSwitchSet({
12124 field: 'selectable',
12125 overrideField: function overrideField(ele) {
12126 return ele.cy().autounselectify() ? false : undefined;
12127 },
12128 on: 'selectify',
12129 off: 'unselectify'
12130});
12131elesfn$3.deselect = elesfn$3.unselect;
12132
12133elesfn$3.grabbed = function () {
12134 var ele = this[0];
12135
12136 if (ele) {
12137 return ele._private.grabbed;
12138 }
12139};
12140
12141defineSwitchSet({
12142 field: 'active',
12143 on: 'activate',
12144 off: 'unactivate'
12145});
12146defineSwitchSet({
12147 field: 'pannable',
12148 on: 'panify',
12149 off: 'unpanify'
12150});
12151
12152elesfn$3.inactive = function () {
12153 var ele = this[0];
12154
12155 if (ele) {
12156 return !ele._private.active;
12157 }
12158};
12159
12160var elesfn$2 = {}; // DAG functions
12161////////////////
12162
12163var defineDagExtremity = function defineDagExtremity(params) {
12164 return function dagExtremityImpl(selector) {
12165 var eles = this;
12166 var ret = [];
12167
12168 for (var i = 0; i < eles.length; i++) {
12169 var ele = eles[i];
12170
12171 if (!ele.isNode()) {
12172 continue;
12173 }
12174
12175 var disqualified = false;
12176 var edges = ele.connectedEdges();
12177
12178 for (var j = 0; j < edges.length; j++) {
12179 var edge = edges[j];
12180 var src = edge.source();
12181 var tgt = edge.target();
12182
12183 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12184 disqualified = true;
12185 break;
12186 }
12187 }
12188
12189 if (!disqualified) {
12190 ret.push(ele);
12191 }
12192 }
12193
12194 return this.spawn(ret, true).filter(selector);
12195 };
12196};
12197
12198var defineDagOneHop = function defineDagOneHop(params) {
12199 return function (selector) {
12200 var eles = this;
12201 var oEles = [];
12202
12203 for (var i = 0; i < eles.length; i++) {
12204 var ele = eles[i];
12205
12206 if (!ele.isNode()) {
12207 continue;
12208 }
12209
12210 var edges = ele.connectedEdges();
12211
12212 for (var j = 0; j < edges.length; j++) {
12213 var edge = edges[j];
12214 var src = edge.source();
12215 var tgt = edge.target();
12216
12217 if (params.outgoing && src === ele) {
12218 oEles.push(edge);
12219 oEles.push(tgt);
12220 } else if (params.incoming && tgt === ele) {
12221 oEles.push(edge);
12222 oEles.push(src);
12223 }
12224 }
12225 }
12226
12227 return this.spawn(oEles, true).filter(selector);
12228 };
12229};
12230
12231var defineDagAllHops = function defineDagAllHops(params) {
12232 return function (selector) {
12233 var eles = this;
12234 var sEles = [];
12235 var sElesIds = {};
12236
12237 for (;;) {
12238 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12239
12240 if (next.length === 0) {
12241 break;
12242 } // done if none left
12243
12244
12245 var newNext = false;
12246
12247 for (var i = 0; i < next.length; i++) {
12248 var n = next[i];
12249 var nid = n.id();
12250
12251 if (!sElesIds[nid]) {
12252 sElesIds[nid] = true;
12253 sEles.push(n);
12254 newNext = true;
12255 }
12256 }
12257
12258 if (!newNext) {
12259 break;
12260 } // done if touched all outgoers already
12261
12262
12263 eles = next;
12264 }
12265
12266 return this.spawn(sEles, true).filter(selector);
12267 };
12268};
12269
12270elesfn$2.clearTraversalCache = function () {
12271 for (var i = 0; i < this.length; i++) {
12272 this[i]._private.traversalCache = null;
12273 }
12274};
12275
12276extend(elesfn$2, {
12277 // get the root nodes in the DAG
12278 roots: defineDagExtremity({
12279 noIncomingEdges: true
12280 }),
12281 // get the leaf nodes in the DAG
12282 leaves: defineDagExtremity({
12283 noOutgoingEdges: true
12284 }),
12285 // normally called children in graph theory
12286 // these nodes =edges=> outgoing nodes
12287 outgoers: cache(defineDagOneHop({
12288 outgoing: true
12289 }), 'outgoers'),
12290 // aka DAG descendants
12291 successors: defineDagAllHops({
12292 outgoing: true
12293 }),
12294 // normally called parents in graph theory
12295 // these nodes <=edges= incoming nodes
12296 incomers: cache(defineDagOneHop({
12297 incoming: true
12298 }), 'incomers'),
12299 // aka DAG ancestors
12300 predecessors: defineDagAllHops({
12301 incoming: true
12302 })
12303}); // Neighbourhood functions
12304//////////////////////////
12305
12306extend(elesfn$2, {
12307 neighborhood: cache(function (selector) {
12308 var elements = [];
12309 var nodes = this.nodes();
12310
12311 for (var i = 0; i < nodes.length; i++) {
12312 // for all nodes
12313 var node = nodes[i];
12314 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12315
12316 for (var j = 0; j < connectedEdges.length; j++) {
12317 var edge = connectedEdges[j];
12318 var src = edge.source();
12319 var tgt = edge.target();
12320 var otherNode = node === src ? tgt : src; // need check in case of loop
12321
12322 if (otherNode.length > 0) {
12323 elements.push(otherNode[0]); // add node 1 hop away
12324 } // add connected edge
12325
12326
12327 elements.push(edge[0]);
12328 }
12329 }
12330
12331 return this.spawn(elements, true).filter(selector);
12332 }, 'neighborhood'),
12333 closedNeighborhood: function closedNeighborhood(selector) {
12334 return this.neighborhood().add(this).filter(selector);
12335 },
12336 openNeighborhood: function openNeighborhood(selector) {
12337 return this.neighborhood(selector);
12338 }
12339}); // aliases
12340
12341elesfn$2.neighbourhood = elesfn$2.neighborhood;
12342elesfn$2.closedNeighbourhood = elesfn$2.closedNeighborhood;
12343elesfn$2.openNeighbourhood = elesfn$2.openNeighborhood; // Edge functions
12344/////////////////
12345
12346extend(elesfn$2, {
12347 source: cache(function sourceImpl(selector) {
12348 var ele = this[0];
12349 var src;
12350
12351 if (ele) {
12352 src = ele._private.source || ele.cy().collection();
12353 }
12354
12355 return src && selector ? src.filter(selector) : src;
12356 }, 'source'),
12357 target: cache(function targetImpl(selector) {
12358 var ele = this[0];
12359 var tgt;
12360
12361 if (ele) {
12362 tgt = ele._private.target || ele.cy().collection();
12363 }
12364
12365 return tgt && selector ? tgt.filter(selector) : tgt;
12366 }, 'target'),
12367 sources: defineSourceFunction({
12368 attr: 'source'
12369 }),
12370 targets: defineSourceFunction({
12371 attr: 'target'
12372 })
12373});
12374
12375function defineSourceFunction(params) {
12376 return function sourceImpl(selector) {
12377 var sources = [];
12378
12379 for (var i = 0; i < this.length; i++) {
12380 var ele = this[i];
12381 var src = ele._private[params.attr];
12382
12383 if (src) {
12384 sources.push(src);
12385 }
12386 }
12387
12388 return this.spawn(sources, true).filter(selector);
12389 };
12390}
12391
12392extend(elesfn$2, {
12393 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12394 edgesTo: cache(defineEdgesWithFunction({
12395 thisIsSrc: true
12396 }), 'edgesTo')
12397});
12398
12399function defineEdgesWithFunction(params) {
12400 return function edgesWithImpl(otherNodes) {
12401 var elements = [];
12402 var cy = this._private.cy;
12403 var p = params || {}; // get elements if a selector is specified
12404
12405 if (string(otherNodes)) {
12406 otherNodes = cy.$(otherNodes);
12407 }
12408
12409 for (var h = 0; h < otherNodes.length; h++) {
12410 var edges = otherNodes[h]._private.edges;
12411
12412 for (var i = 0; i < edges.length; i++) {
12413 var edge = edges[i];
12414 var edgeData = edge._private.data;
12415 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12416 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12417 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12418
12419 if (!edgeConnectsThisAndOther) {
12420 continue;
12421 }
12422
12423 if (p.thisIsSrc || p.thisIsTgt) {
12424 if (p.thisIsSrc && !thisToOther) {
12425 continue;
12426 }
12427
12428 if (p.thisIsTgt && !otherToThis) {
12429 continue;
12430 }
12431 }
12432
12433 elements.push(edge);
12434 }
12435 }
12436
12437 return this.spawn(elements, true);
12438 };
12439}
12440
12441extend(elesfn$2, {
12442 connectedEdges: cache(function (selector) {
12443 var retEles = [];
12444 var eles = this;
12445
12446 for (var i = 0; i < eles.length; i++) {
12447 var node = eles[i];
12448
12449 if (!node.isNode()) {
12450 continue;
12451 }
12452
12453 var edges = node._private.edges;
12454
12455 for (var j = 0; j < edges.length; j++) {
12456 var edge = edges[j];
12457 retEles.push(edge);
12458 }
12459 }
12460
12461 return this.spawn(retEles, true).filter(selector);
12462 }, 'connectedEdges'),
12463 connectedNodes: cache(function (selector) {
12464 var retEles = [];
12465 var eles = this;
12466
12467 for (var i = 0; i < eles.length; i++) {
12468 var edge = eles[i];
12469
12470 if (!edge.isEdge()) {
12471 continue;
12472 }
12473
12474 retEles.push(edge.source()[0]);
12475 retEles.push(edge.target()[0]);
12476 }
12477
12478 return this.spawn(retEles, true).filter(selector);
12479 }, 'connectedNodes'),
12480 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12481 codirectedEdges: cache(defineParallelEdgesFunction({
12482 codirected: true
12483 }), 'codirectedEdges')
12484});
12485
12486function defineParallelEdgesFunction(params) {
12487 var defaults = {
12488 codirected: false
12489 };
12490 params = extend({}, defaults, params);
12491 return function parallelEdgesImpl(selector) {
12492 // micro-optimised for renderer
12493 var elements = [];
12494 var edges = this.edges();
12495 var p = params; // look at all the edges in the collection
12496
12497 for (var i = 0; i < edges.length; i++) {
12498 var edge1 = edges[i];
12499 var edge1_p = edge1._private;
12500 var src1 = edge1_p.source;
12501 var srcid1 = src1._private.data.id;
12502 var tgtid1 = edge1_p.data.target;
12503 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12504
12505 for (var j = 0; j < srcEdges1.length; j++) {
12506 var edge2 = srcEdges1[j];
12507 var edge2data = edge2._private.data;
12508 var tgtid2 = edge2data.target;
12509 var srcid2 = edge2data.source;
12510 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12511 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12512
12513 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12514 elements.push(edge2);
12515 }
12516 }
12517 }
12518
12519 return this.spawn(elements, true).filter(selector);
12520 };
12521} // Misc functions
12522/////////////////
12523
12524
12525extend(elesfn$2, {
12526 components: function components(root) {
12527 var self = this;
12528 var cy = self.cy();
12529 var visited = cy.collection();
12530 var unvisited = root == null ? self.nodes() : root.nodes();
12531 var components = [];
12532
12533 if (root != null && unvisited.empty()) {
12534 // root may contain only edges
12535 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12536 }
12537
12538 var visitInComponent = function visitInComponent(node, component) {
12539 visited.merge(node);
12540 unvisited.unmerge(node);
12541 component.merge(node);
12542 };
12543
12544 if (unvisited.empty()) {
12545 return self.spawn();
12546 }
12547
12548 var _loop = function _loop() {
12549 // each iteration yields a component
12550 var cmpt = cy.collection();
12551 components.push(cmpt);
12552 var root = unvisited[0];
12553 visitInComponent(root, cmpt);
12554 self.bfs({
12555 directed: false,
12556 roots: root,
12557 visit: function visit(v) {
12558 return visitInComponent(v, cmpt);
12559 }
12560 });
12561 cmpt.forEach(function (node) {
12562 node.connectedEdges().forEach(function (e) {
12563 // connectedEdges() usually cached
12564 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12565 // has() is cheap
12566 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12567 }
12568 });
12569 });
12570 };
12571
12572 do {
12573 _loop();
12574 } while (unvisited.length > 0);
12575
12576 return components;
12577 },
12578 component: function component() {
12579 var ele = this[0];
12580 return ele.cy().mutableElements().components(ele)[0];
12581 }
12582});
12583elesfn$2.componentsOf = elesfn$2.components;
12584
12585var Collection = function Collection(cy, elements) {
12586 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12587 var removed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
12588
12589 if (cy === undefined) {
12590 error('A collection must have a reference to the core');
12591 return;
12592 }
12593
12594 var map = new Map$1();
12595 var createdElements = false;
12596
12597 if (!elements) {
12598 elements = [];
12599 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12600 createdElements = true; // make elements from json and restore all at once later
12601
12602 var eles = [];
12603 var elesIds = new Set$1();
12604
12605 for (var i = 0, l = elements.length; i < l; i++) {
12606 var json = elements[i];
12607
12608 if (json.data == null) {
12609 json.data = {};
12610 }
12611
12612 var _data = json.data; // make sure newly created elements have valid ids
12613
12614 if (_data.id == null) {
12615 _data.id = uuid();
12616 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12617 continue; // can't create element if prior id already exists
12618 }
12619
12620 var ele = new Element(cy, json, false);
12621 eles.push(ele);
12622 elesIds.add(_data.id);
12623 }
12624
12625 elements = eles;
12626 }
12627
12628 this.length = 0;
12629
12630 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12631 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12632
12633 if (element$1 == null) {
12634 continue;
12635 }
12636
12637 var id = element$1._private.data.id;
12638
12639 if (!unique || !map.has(id)) {
12640 if (unique) {
12641 map.set(id, {
12642 index: this.length,
12643 ele: element$1
12644 });
12645 }
12646
12647 this[this.length] = element$1;
12648 this.length++;
12649 }
12650 }
12651
12652 this._private = {
12653 eles: this,
12654 cy: cy,
12655
12656 get map() {
12657 if (this.lazyMap == null) {
12658 this.rebuildMap();
12659 }
12660
12661 return this.lazyMap;
12662 },
12663
12664 set map(m) {
12665 this.lazyMap = m;
12666 },
12667
12668 rebuildMap: function rebuildMap() {
12669 var m = this.lazyMap = new Map$1();
12670 var eles = this.eles;
12671
12672 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12673 var _ele = eles[_i2];
12674 m.set(_ele.id(), {
12675 index: _i2,
12676 ele: _ele
12677 });
12678 }
12679 }
12680 };
12681
12682 if (unique) {
12683 this._private.map = map;
12684 } // restore the elements if we created them from json
12685
12686
12687 if (createdElements && !removed) {
12688 this.restore();
12689 }
12690}; // Functions
12691////////////////////////////////////////////////////////////////////////////////////////////////////
12692// keep the prototypes in sync (an element has the same functions as a collection)
12693// and use elefn and elesfn as shorthands to the prototypes
12694
12695
12696var elesfn$1 = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12697
12698elesfn$1.instanceString = function () {
12699 return 'collection';
12700};
12701
12702elesfn$1.spawn = function (eles, unique) {
12703 return new Collection(this.cy(), eles, unique);
12704};
12705
12706elesfn$1.spawnSelf = function () {
12707 return this.spawn(this);
12708};
12709
12710elesfn$1.cy = function () {
12711 return this._private.cy;
12712};
12713
12714elesfn$1.renderer = function () {
12715 return this._private.cy.renderer();
12716};
12717
12718elesfn$1.element = function () {
12719 return this[0];
12720};
12721
12722elesfn$1.collection = function () {
12723 if (collection(this)) {
12724 return this;
12725 } else {
12726 // an element
12727 return new Collection(this._private.cy, [this]);
12728 }
12729};
12730
12731elesfn$1.unique = function () {
12732 return new Collection(this._private.cy, this, true);
12733};
12734
12735elesfn$1.hasElementWithId = function (id) {
12736 id = '' + id; // id must be string
12737
12738 return this._private.map.has(id);
12739};
12740
12741elesfn$1.getElementById = function (id) {
12742 id = '' + id; // id must be string
12743
12744 var cy = this._private.cy;
12745
12746 var entry = this._private.map.get(id);
12747
12748 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12749};
12750
12751elesfn$1.$id = elesfn$1.getElementById;
12752
12753elesfn$1.poolIndex = function () {
12754 var cy = this._private.cy;
12755 var eles = cy._private.elements;
12756 var id = this[0]._private.data.id;
12757 return eles._private.map.get(id).index;
12758};
12759
12760elesfn$1.indexOf = function (ele) {
12761 var id = ele[0]._private.data.id;
12762 return this._private.map.get(id).index;
12763};
12764
12765elesfn$1.indexOfId = function (id) {
12766 id = '' + id; // id must be string
12767
12768 return this._private.map.get(id).index;
12769};
12770
12771elesfn$1.json = function (obj) {
12772 var ele = this.element();
12773 var cy = this.cy();
12774
12775 if (ele == null && obj) {
12776 return this;
12777 } // can't set to no eles
12778
12779
12780 if (ele == null) {
12781 return undefined;
12782 } // can't get from no eles
12783
12784
12785 var p = ele._private;
12786
12787 if (plainObject(obj)) {
12788 // set
12789 cy.startBatch();
12790
12791 if (obj.data) {
12792 ele.data(obj.data);
12793 var _data2 = p.data;
12794
12795 if (ele.isEdge()) {
12796 // source and target are immutable via data()
12797 var move = false;
12798 var spec = {};
12799 var src = obj.data.source;
12800 var tgt = obj.data.target;
12801
12802 if (src != null && src != _data2.source) {
12803 spec.source = '' + src; // id must be string
12804
12805 move = true;
12806 }
12807
12808 if (tgt != null && tgt != _data2.target) {
12809 spec.target = '' + tgt; // id must be string
12810
12811 move = true;
12812 }
12813
12814 if (move) {
12815 ele = ele.move(spec);
12816 }
12817 } else {
12818 // parent is immutable via data()
12819 var newParentValSpecd = ('parent' in obj.data);
12820 var parent = obj.data.parent;
12821
12822 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12823 if (parent === undefined) {
12824 // can't set undefined imperatively, so use null
12825 parent = null;
12826 }
12827
12828 if (parent != null) {
12829 parent = '' + parent; // id must be string
12830 }
12831
12832 ele = ele.move({
12833 parent: parent
12834 });
12835 }
12836 }
12837 }
12838
12839 if (obj.position) {
12840 ele.position(obj.position);
12841 } // ignore group -- immutable
12842
12843
12844 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12845 var obj_k = obj[k];
12846
12847 if (obj_k != null && obj_k !== p[k]) {
12848 if (obj_k) {
12849 ele[trueFnName]();
12850 } else {
12851 ele[falseFnName]();
12852 }
12853 }
12854 };
12855
12856 checkSwitch('removed', 'remove', 'restore');
12857 checkSwitch('selected', 'select', 'unselect');
12858 checkSwitch('selectable', 'selectify', 'unselectify');
12859 checkSwitch('locked', 'lock', 'unlock');
12860 checkSwitch('grabbable', 'grabify', 'ungrabify');
12861 checkSwitch('pannable', 'panify', 'unpanify');
12862
12863 if (obj.classes != null) {
12864 ele.classes(obj.classes);
12865 }
12866
12867 cy.endBatch();
12868 return this;
12869 } else if (obj === undefined) {
12870 // get
12871 var json = {
12872 data: copy(p.data),
12873 position: copy(p.position),
12874 group: p.group,
12875 removed: p.removed,
12876 selected: p.selected,
12877 selectable: p.selectable,
12878 locked: p.locked,
12879 grabbable: p.grabbable,
12880 pannable: p.pannable,
12881 classes: null
12882 };
12883 json.classes = '';
12884 var i = 0;
12885 p.classes.forEach(function (cls) {
12886 return json.classes += i++ === 0 ? cls : ' ' + cls;
12887 });
12888 return json;
12889 }
12890};
12891
12892elesfn$1.jsons = function () {
12893 var jsons = [];
12894
12895 for (var i = 0; i < this.length; i++) {
12896 var ele = this[i];
12897 var json = ele.json();
12898 jsons.push(json);
12899 }
12900
12901 return jsons;
12902};
12903
12904elesfn$1.clone = function () {
12905 var cy = this.cy();
12906 var elesArr = [];
12907
12908 for (var i = 0; i < this.length; i++) {
12909 var ele = this[i];
12910 var json = ele.json();
12911 var clone = new Element(cy, json, false); // NB no restore
12912
12913 elesArr.push(clone);
12914 }
12915
12916 return new Collection(cy, elesArr);
12917};
12918
12919elesfn$1.copy = elesfn$1.clone;
12920
12921elesfn$1.restore = function () {
12922 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12923 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12924 var self = this;
12925 var cy = self.cy();
12926 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12927 // restore the nodes first
12928
12929 var nodes = [];
12930 var edges = [];
12931 var elements;
12932
12933 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12934 var ele = self[_i3];
12935
12936 if (addToPool && !ele.removed()) {
12937 // don't need to handle this ele
12938 continue;
12939 } // keep nodes first in the array and edges after
12940
12941
12942 if (ele.isNode()) {
12943 // put to front of array if node
12944 nodes.push(ele);
12945 } else {
12946 // put to end of array if edge
12947 edges.push(ele);
12948 }
12949 }
12950
12951 elements = nodes.concat(edges);
12952 var i;
12953
12954 var removeFromElements = function removeFromElements() {
12955 elements.splice(i, 1);
12956 i--;
12957 }; // now, restore each element
12958
12959
12960 for (i = 0; i < elements.length; i++) {
12961 var _ele2 = elements[i];
12962 var _private = _ele2._private;
12963 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12964
12965 _ele2.clearTraversalCache(); // set id and validate
12966
12967
12968 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12969 _data3.id = uuid();
12970 } else if (number$1(_data3.id)) {
12971 _data3.id = '' + _data3.id; // now it's a string
12972 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12973 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
12974
12975 removeFromElements();
12976 continue;
12977 } else if (cy.hasElementWithId(_data3.id)) {
12978 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12979
12980 removeFromElements();
12981 continue;
12982 }
12983
12984 var id = _data3.id; // id is finalised, now let's keep a ref
12985
12986 if (_ele2.isNode()) {
12987 // extra checks for nodes
12988 var pos = _private.position; // make sure the nodes have a defined position
12989
12990 if (pos.x == null) {
12991 pos.x = 0;
12992 }
12993
12994 if (pos.y == null) {
12995 pos.y = 0;
12996 }
12997 }
12998
12999 if (_ele2.isEdge()) {
13000 // extra checks for edges
13001 var edge = _ele2;
13002 var fields = ['source', 'target'];
13003 var fieldsLength = fields.length;
13004 var badSourceOrTarget = false;
13005
13006 for (var j = 0; j < fieldsLength; j++) {
13007 var field = fields[j];
13008 var val = _data3[field];
13009
13010 if (number$1(val)) {
13011 val = _data3[field] = '' + _data3[field]; // now string
13012 }
13013
13014 if (val == null || val === '') {
13015 // can't create if source or target is not defined properly
13016 error('Can not create edge `' + id + '` with unspecified ' + field);
13017 badSourceOrTarget = true;
13018 } else if (!cy.hasElementWithId(val)) {
13019 // can't create edge if one of its nodes doesn't exist
13020 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13021 badSourceOrTarget = true;
13022 }
13023 }
13024
13025 if (badSourceOrTarget) {
13026 removeFromElements();
13027 continue;
13028 } // can't create this
13029
13030
13031 var src = cy.getElementById(_data3.source);
13032 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
13033
13034 if (src.same(tgt)) {
13035 src._private.edges.push(edge);
13036 } else {
13037 src._private.edges.push(edge);
13038
13039 tgt._private.edges.push(edge);
13040 }
13041
13042 edge._private.source = src;
13043 edge._private.target = tgt;
13044 } // if is edge
13045 // create mock ids / indexes maps for element so it can be used like collections
13046
13047
13048 _private.map = new Map$1();
13049
13050 _private.map.set(id, {
13051 ele: _ele2,
13052 index: 0
13053 });
13054
13055 _private.removed = false;
13056
13057 if (addToPool) {
13058 cy.addToPool(_ele2);
13059 }
13060 } // for each element
13061 // do compound node sanity checks
13062
13063
13064 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13065 // each node
13066 var node = nodes[_i4];
13067 var _data4 = node._private.data;
13068
13069 if (number$1(_data4.parent)) {
13070 // then automake string
13071 _data4.parent = '' + _data4.parent;
13072 }
13073
13074 var parentId = _data4.parent;
13075 var specifiedParent = parentId != null;
13076
13077 if (specifiedParent || node._private.parent) {
13078 var parent = node._private.parent ? cy.collection().merge(node._private.parent) : cy.getElementById(parentId);
13079
13080 if (parent.empty()) {
13081 // non-existant parent; just remove it
13082 _data4.parent = undefined;
13083 } else if (parent[0].removed()) {
13084 warn('Node added with missing parent, reference to parent removed');
13085 _data4.parent = undefined;
13086 node._private.parent = null;
13087 } else {
13088 var selfAsParent = false;
13089 var ancestor = parent;
13090
13091 while (!ancestor.empty()) {
13092 if (node.same(ancestor)) {
13093 // mark self as parent and remove from data
13094 selfAsParent = true;
13095 _data4.parent = undefined; // remove parent reference
13096 // exit or we loop forever
13097
13098 break;
13099 }
13100
13101 ancestor = ancestor.parent();
13102 }
13103
13104 if (!selfAsParent) {
13105 // connect with children
13106 parent[0]._private.children.push(node);
13107
13108 node._private.parent = parent[0]; // let the core know we have a compound graph
13109
13110 cy_p.hasCompoundNodes = true;
13111 }
13112 } // else
13113
13114 } // if specified parent
13115
13116 } // for each node
13117
13118
13119 if (elements.length > 0) {
13120 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13121
13122 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13123 var _ele3 = restored[_i5];
13124
13125 if (_ele3.isNode()) {
13126 continue;
13127 } // adding an edge invalidates the traversal caches for the parallel edges
13128
13129
13130 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13131
13132
13133 _ele3.source().clearTraversalCache();
13134
13135 _ele3.target().clearTraversalCache();
13136 }
13137
13138 var toUpdateStyle;
13139
13140 if (cy_p.hasCompoundNodes) {
13141 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13142 } else {
13143 toUpdateStyle = restored;
13144 }
13145
13146 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13147
13148 if (notifyRenderer) {
13149 restored.emitAndNotify('add');
13150 } else if (addToPool) {
13151 restored.emit('add');
13152 }
13153 }
13154
13155 return self; // chainability
13156};
13157
13158elesfn$1.removed = function () {
13159 var ele = this[0];
13160 return ele && ele._private.removed;
13161};
13162
13163elesfn$1.inside = function () {
13164 var ele = this[0];
13165 return ele && !ele._private.removed;
13166};
13167
13168elesfn$1.remove = function () {
13169 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13170 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13171 var self = this;
13172 var elesToRemove = [];
13173 var elesToRemoveIds = {};
13174 var cy = self._private.cy; // add connected edges
13175
13176 function addConnectedEdges(node) {
13177 var edges = node._private.edges;
13178
13179 for (var i = 0; i < edges.length; i++) {
13180 add(edges[i]);
13181 }
13182 } // add descendant nodes
13183
13184
13185 function addChildren(node) {
13186 var children = node._private.children;
13187
13188 for (var i = 0; i < children.length; i++) {
13189 add(children[i]);
13190 }
13191 }
13192
13193 function add(ele) {
13194 var alreadyAdded = elesToRemoveIds[ele.id()];
13195
13196 if (removeFromPool && ele.removed() || alreadyAdded) {
13197 return;
13198 } else {
13199 elesToRemoveIds[ele.id()] = true;
13200 }
13201
13202 if (ele.isNode()) {
13203 elesToRemove.push(ele); // nodes are removed last
13204
13205 addConnectedEdges(ele);
13206 addChildren(ele);
13207 } else {
13208 elesToRemove.unshift(ele); // edges are removed first
13209 }
13210 } // make the list of elements to remove
13211 // (may be removing more than specified due to connected edges etc)
13212
13213
13214 for (var i = 0, l = self.length; i < l; i++) {
13215 var ele = self[i];
13216 add(ele);
13217 }
13218
13219 function removeEdgeRef(node, edge) {
13220 var connectedEdges = node._private.edges;
13221 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13222
13223 node.clearTraversalCache();
13224 }
13225
13226 function removeParallelRef(pllEdge) {
13227 // removing an edge invalidates the traversal caches for the parallel edges
13228 pllEdge.clearTraversalCache();
13229 }
13230
13231 var alteredParents = [];
13232 alteredParents.ids = {};
13233
13234 function removeChildRef(parent, ele) {
13235 ele = ele[0];
13236 parent = parent[0];
13237 var children = parent._private.children;
13238 var pid = parent.id();
13239 removeFromArray(children, ele); // remove parent => child ref
13240
13241 ele._private.parent = null; // remove child => parent ref
13242
13243 if (!alteredParents.ids[pid]) {
13244 alteredParents.ids[pid] = true;
13245 alteredParents.push(parent);
13246 }
13247 }
13248
13249 self.dirtyCompoundBoundsCache();
13250
13251 if (removeFromPool) {
13252 cy.removeFromPool(elesToRemove); // remove from core pool
13253 }
13254
13255 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13256 var _ele4 = elesToRemove[_i6];
13257
13258 if (_ele4.isEdge()) {
13259 // remove references to this edge in its connected nodes
13260 var src = _ele4.source()[0];
13261
13262 var tgt = _ele4.target()[0];
13263
13264 removeEdgeRef(src, _ele4);
13265 removeEdgeRef(tgt, _ele4);
13266
13267 var pllEdges = _ele4.parallelEdges();
13268
13269 for (var j = 0; j < pllEdges.length; j++) {
13270 var pllEdge = pllEdges[j];
13271 removeParallelRef(pllEdge);
13272
13273 if (pllEdge.isBundledBezier()) {
13274 pllEdge.dirtyBoundingBoxCache();
13275 }
13276 }
13277 } else {
13278 // remove reference to parent
13279 var parent = _ele4.parent();
13280
13281 if (parent.length !== 0) {
13282 removeChildRef(parent, _ele4);
13283 }
13284 }
13285
13286 if (removeFromPool) {
13287 // mark as removed
13288 _ele4._private.removed = true;
13289 }
13290 } // check to see if we have a compound graph or not
13291
13292
13293 var elesStillInside = cy._private.elements;
13294 cy._private.hasCompoundNodes = false;
13295
13296 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13297 var _ele5 = elesStillInside[_i7];
13298
13299 if (_ele5.isParent()) {
13300 cy._private.hasCompoundNodes = true;
13301 break;
13302 }
13303 }
13304
13305 var removedElements = new Collection(this.cy(), elesToRemove);
13306
13307 if (removedElements.size() > 0) {
13308 // must manually notify since trigger won't do this automatically once removed
13309 if (notifyRenderer) {
13310 removedElements.emitAndNotify('remove');
13311 } else if (removeFromPool) {
13312 removedElements.emit('remove');
13313 }
13314 } // the parents who were modified by the removal need their style updated
13315
13316
13317 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13318 var _ele6 = alteredParents[_i8];
13319
13320 if (!removeFromPool || !_ele6.removed()) {
13321 _ele6.updateStyle();
13322 }
13323 }
13324
13325 return removedElements;
13326};
13327
13328elesfn$1.move = function (struct) {
13329 var cy = this._private.cy;
13330 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13331 // (our calls to remove/restore do not remove from the graph or make events)
13332
13333 var notifyRenderer = false;
13334 var modifyPool = false;
13335
13336 var toString = function toString(id) {
13337 return id == null ? id : '' + id;
13338 }; // id must be string
13339
13340
13341 if (struct.source !== undefined || struct.target !== undefined) {
13342 var srcId = toString(struct.source);
13343 var tgtId = toString(struct.target);
13344 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13345 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13346
13347 if (srcExists || tgtExists) {
13348 cy.batch(function () {
13349 // avoid duplicate style updates
13350 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13351
13352 eles.emitAndNotify('moveout');
13353
13354 for (var i = 0; i < eles.length; i++) {
13355 var ele = eles[i];
13356 var _data5 = ele._private.data;
13357
13358 if (ele.isEdge()) {
13359 if (srcExists) {
13360 _data5.source = srcId;
13361 }
13362
13363 if (tgtExists) {
13364 _data5.target = tgtId;
13365 }
13366 }
13367 }
13368
13369 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13370 });
13371 eles.emitAndNotify('move');
13372 }
13373 } else if (struct.parent !== undefined) {
13374 // move node to new parent
13375 var parentId = toString(struct.parent);
13376 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13377
13378 if (parentExists) {
13379 var pidToAssign = parentId === null ? undefined : parentId;
13380 cy.batch(function () {
13381 // avoid duplicate style updates
13382 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13383
13384 updated.emitAndNotify('moveout');
13385
13386 for (var i = 0; i < eles.length; i++) {
13387 var ele = eles[i];
13388 var _data6 = ele._private.data;
13389
13390 if (ele.isNode()) {
13391 _data6.parent = pidToAssign;
13392 }
13393 }
13394
13395 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13396 });
13397 eles.emitAndNotify('move');
13398 }
13399 }
13400
13401 return this;
13402};
13403
13404[elesfn$j, elesfn$i, elesfn$h, elesfn$g, elesfn$f, data, elesfn$d, dimensions, elesfn$9, elesfn$8, elesfn$7, elesfn$6, elesfn$5, elesfn$4, elesfn$3, elesfn$2].forEach(function (props) {
13405 extend(elesfn$1, props);
13406});
13407
13408var corefn$9 = {
13409 add: function add(opts) {
13410 var elements;
13411 var cy = this; // add the elements
13412
13413 if (elementOrCollection(opts)) {
13414 var eles = opts;
13415
13416 if (eles._private.cy === cy) {
13417 // same instance => just restore
13418 elements = eles.restore();
13419 } else {
13420 // otherwise, copy from json
13421 var jsons = [];
13422
13423 for (var i = 0; i < eles.length; i++) {
13424 var ele = eles[i];
13425 jsons.push(ele.json());
13426 }
13427
13428 elements = new Collection(cy, jsons);
13429 }
13430 } // specify an array of options
13431 else if (array(opts)) {
13432 var _jsons = opts;
13433 elements = new Collection(cy, _jsons);
13434 } // specify via opts.nodes and opts.edges
13435 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13436 var elesByGroup = opts;
13437 var _jsons2 = [];
13438 var grs = ['nodes', 'edges'];
13439
13440 for (var _i = 0, il = grs.length; _i < il; _i++) {
13441 var group = grs[_i];
13442 var elesArray = elesByGroup[group];
13443
13444 if (array(elesArray)) {
13445 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13446 var json = extend({
13447 group: group
13448 }, elesArray[j]);
13449
13450 _jsons2.push(json);
13451 }
13452 }
13453 }
13454
13455 elements = new Collection(cy, _jsons2);
13456 } // specify options for one element
13457 else {
13458 var _json = opts;
13459 elements = new Element(cy, _json).collection();
13460 }
13461
13462 return elements;
13463 },
13464 remove: function remove(collection) {
13465 if (elementOrCollection(collection)) ; else if (string(collection)) {
13466 var selector = collection;
13467 collection = this.$(selector);
13468 }
13469
13470 return collection.remove();
13471 }
13472};
13473
13474/* global Float32Array */
13475
13476/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13477function generateCubicBezier(mX1, mY1, mX2, mY2) {
13478 var NEWTON_ITERATIONS = 4,
13479 NEWTON_MIN_SLOPE = 0.001,
13480 SUBDIVISION_PRECISION = 0.0000001,
13481 SUBDIVISION_MAX_ITERATIONS = 10,
13482 kSplineTableSize = 11,
13483 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13484 float32ArraySupported = typeof Float32Array !== 'undefined';
13485 /* Must contain four arguments. */
13486
13487 if (arguments.length !== 4) {
13488 return false;
13489 }
13490 /* Arguments must be numbers. */
13491
13492
13493 for (var i = 0; i < 4; ++i) {
13494 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13495 return false;
13496 }
13497 }
13498 /* X values must be in the [0, 1] range. */
13499
13500
13501 mX1 = Math.min(mX1, 1);
13502 mX2 = Math.min(mX2, 1);
13503 mX1 = Math.max(mX1, 0);
13504 mX2 = Math.max(mX2, 0);
13505 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13506
13507 function A(aA1, aA2) {
13508 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13509 }
13510
13511 function B(aA1, aA2) {
13512 return 3.0 * aA2 - 6.0 * aA1;
13513 }
13514
13515 function C(aA1) {
13516 return 3.0 * aA1;
13517 }
13518
13519 function calcBezier(aT, aA1, aA2) {
13520 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13521 }
13522
13523 function getSlope(aT, aA1, aA2) {
13524 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13525 }
13526
13527 function newtonRaphsonIterate(aX, aGuessT) {
13528 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13529 var currentSlope = getSlope(aGuessT, mX1, mX2);
13530
13531 if (currentSlope === 0.0) {
13532 return aGuessT;
13533 }
13534
13535 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13536 aGuessT -= currentX / currentSlope;
13537 }
13538
13539 return aGuessT;
13540 }
13541
13542 function calcSampleValues() {
13543 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13544 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13545 }
13546 }
13547
13548 function binarySubdivide(aX, aA, aB) {
13549 var currentX,
13550 currentT,
13551 i = 0;
13552
13553 do {
13554 currentT = aA + (aB - aA) / 2.0;
13555 currentX = calcBezier(currentT, mX1, mX2) - aX;
13556
13557 if (currentX > 0.0) {
13558 aB = currentT;
13559 } else {
13560 aA = currentT;
13561 }
13562 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13563
13564 return currentT;
13565 }
13566
13567 function getTForX(aX) {
13568 var intervalStart = 0.0,
13569 currentSample = 1,
13570 lastSample = kSplineTableSize - 1;
13571
13572 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13573 intervalStart += kSampleStepSize;
13574 }
13575
13576 --currentSample;
13577 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13578 guessForT = intervalStart + dist * kSampleStepSize,
13579 initialSlope = getSlope(guessForT, mX1, mX2);
13580
13581 if (initialSlope >= NEWTON_MIN_SLOPE) {
13582 return newtonRaphsonIterate(aX, guessForT);
13583 } else if (initialSlope === 0.0) {
13584 return guessForT;
13585 } else {
13586 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13587 }
13588 }
13589
13590 var _precomputed = false;
13591
13592 function precompute() {
13593 _precomputed = true;
13594
13595 if (mX1 !== mY1 || mX2 !== mY2) {
13596 calcSampleValues();
13597 }
13598 }
13599
13600 var f = function f(aX) {
13601 if (!_precomputed) {
13602 precompute();
13603 }
13604
13605 if (mX1 === mY1 && mX2 === mY2) {
13606 return aX;
13607 }
13608
13609 if (aX === 0) {
13610 return 0;
13611 }
13612
13613 if (aX === 1) {
13614 return 1;
13615 }
13616
13617 return calcBezier(getTForX(aX), mY1, mY2);
13618 };
13619
13620 f.getControlPoints = function () {
13621 return [{
13622 x: mX1,
13623 y: mY1
13624 }, {
13625 x: mX2,
13626 y: mY2
13627 }];
13628 };
13629
13630 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13631
13632 f.toString = function () {
13633 return str;
13634 };
13635
13636 return f;
13637}
13638
13639/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13640
13641/* 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
13642 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13643var generateSpringRK4 = function () {
13644 function springAccelerationForState(state) {
13645 return -state.tension * state.x - state.friction * state.v;
13646 }
13647
13648 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13649 var state = {
13650 x: initialState.x + derivative.dx * dt,
13651 v: initialState.v + derivative.dv * dt,
13652 tension: initialState.tension,
13653 friction: initialState.friction
13654 };
13655 return {
13656 dx: state.v,
13657 dv: springAccelerationForState(state)
13658 };
13659 }
13660
13661 function springIntegrateState(state, dt) {
13662 var a = {
13663 dx: state.v,
13664 dv: springAccelerationForState(state)
13665 },
13666 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13667 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13668 d = springEvaluateStateWithDerivative(state, dt, c),
13669 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13670 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13671 state.x = state.x + dxdt * dt;
13672 state.v = state.v + dvdt * dt;
13673 return state;
13674 }
13675
13676 return function springRK4Factory(tension, friction, duration) {
13677 var initState = {
13678 x: -1,
13679 v: 0,
13680 tension: null,
13681 friction: null
13682 },
13683 path = [0],
13684 time_lapsed = 0,
13685 tolerance = 1 / 10000,
13686 DT = 16 / 1000,
13687 have_duration,
13688 dt,
13689 last_state;
13690 tension = parseFloat(tension) || 500;
13691 friction = parseFloat(friction) || 20;
13692 duration = duration || null;
13693 initState.tension = tension;
13694 initState.friction = friction;
13695 have_duration = duration !== null;
13696 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13697
13698 if (have_duration) {
13699 /* Run the simulation without a duration. */
13700 time_lapsed = springRK4Factory(tension, friction);
13701 /* Compute the adjusted time delta. */
13702
13703 dt = time_lapsed / duration * DT;
13704 } else {
13705 dt = DT;
13706 }
13707
13708 for (;;) {
13709 /* Next/step function .*/
13710 last_state = springIntegrateState(last_state || initState, dt);
13711 /* Store the position. */
13712
13713 path.push(1 + last_state.x);
13714 time_lapsed += 16;
13715 /* If the change threshold is reached, break. */
13716
13717 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13718 break;
13719 }
13720 }
13721 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13722 computed path and returns a snapshot of the position according to a given percentComplete. */
13723
13724
13725 return !have_duration ? time_lapsed : function (percentComplete) {
13726 return path[percentComplete * (path.length - 1) | 0];
13727 };
13728 };
13729}();
13730
13731var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13732 var bezier = generateCubicBezier(t1, p1, t2, p2);
13733 return function (start, end, percent) {
13734 return start + (end - start) * bezier(percent);
13735 };
13736};
13737
13738var easings = {
13739 'linear': function linear(start, end, percent) {
13740 return start + (end - start) * percent;
13741 },
13742 // default easings
13743 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13744 'ease-in': cubicBezier(0.42, 0, 1, 1),
13745 'ease-out': cubicBezier(0, 0, 0.58, 1),
13746 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13747 // sine
13748 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13749 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13750 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13751 // quad
13752 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13753 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13754 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13755 // cubic
13756 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13757 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13758 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13759 // quart
13760 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13761 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13762 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13763 // quint
13764 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13765 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13766 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13767 // expo
13768 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13769 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13770 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13771 // circ
13772 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13773 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13774 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13775 // user param easings...
13776 'spring': function spring(tension, friction, duration) {
13777 if (duration === 0) {
13778 // can't get a spring w/ duration 0
13779 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13780 }
13781
13782 var spring = generateSpringRK4(tension, friction, duration);
13783 return function (start, end, percent) {
13784 return start + (end - start) * spring(percent);
13785 };
13786 },
13787 'cubic-bezier': cubicBezier
13788};
13789
13790function getEasedValue(type, start, end, percent, easingFn) {
13791 if (percent === 1) {
13792 return end;
13793 }
13794
13795 if (start === end) {
13796 return end;
13797 }
13798
13799 var val = easingFn(start, end, percent);
13800
13801 if (type == null) {
13802 return val;
13803 }
13804
13805 if (type.roundValue || type.color) {
13806 val = Math.round(val);
13807 }
13808
13809 if (type.min !== undefined) {
13810 val = Math.max(val, type.min);
13811 }
13812
13813 if (type.max !== undefined) {
13814 val = Math.min(val, type.max);
13815 }
13816
13817 return val;
13818}
13819
13820function getValue(prop, spec) {
13821 if (prop.pfValue != null || prop.value != null) {
13822 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13823 return prop.pfValue;
13824 } else {
13825 return prop.value;
13826 }
13827 } else {
13828 return prop;
13829 }
13830}
13831
13832function ease(startProp, endProp, percent, easingFn, propSpec) {
13833 var type = propSpec != null ? propSpec.type : null;
13834
13835 if (percent < 0) {
13836 percent = 0;
13837 } else if (percent > 1) {
13838 percent = 1;
13839 }
13840
13841 var start = getValue(startProp, propSpec);
13842 var end = getValue(endProp, propSpec);
13843
13844 if (number$1(start) && number$1(end)) {
13845 return getEasedValue(type, start, end, percent, easingFn);
13846 } else if (array(start) && array(end)) {
13847 var easedArr = [];
13848
13849 for (var i = 0; i < end.length; i++) {
13850 var si = start[i];
13851 var ei = end[i];
13852
13853 if (si != null && ei != null) {
13854 var val = getEasedValue(type, si, ei, percent, easingFn);
13855 easedArr.push(val);
13856 } else {
13857 easedArr.push(ei);
13858 }
13859 }
13860
13861 return easedArr;
13862 }
13863
13864 return undefined;
13865}
13866
13867function step$1(self, ani, now, isCore) {
13868 var isEles = !isCore;
13869 var _p = self._private;
13870 var ani_p = ani._private;
13871 var pEasing = ani_p.easing;
13872 var startTime = ani_p.startTime;
13873 var cy = isCore ? self : self.cy();
13874 var style = cy.style();
13875
13876 if (!ani_p.easingImpl) {
13877 if (pEasing == null) {
13878 // use default
13879 ani_p.easingImpl = easings['linear'];
13880 } else {
13881 // then define w/ name
13882 var easingVals;
13883
13884 if (string(pEasing)) {
13885 var easingProp = style.parse('transition-timing-function', pEasing);
13886 easingVals = easingProp.value;
13887 } else {
13888 // then assume preparsed array
13889 easingVals = pEasing;
13890 }
13891
13892 var name, args;
13893
13894 if (string(easingVals)) {
13895 name = easingVals;
13896 args = [];
13897 } else {
13898 name = easingVals[1];
13899 args = easingVals.slice(2).map(function (n) {
13900 return +n;
13901 });
13902 }
13903
13904 if (args.length > 0) {
13905 // create with args
13906 if (name === 'spring') {
13907 args.push(ani_p.duration); // need duration to generate spring
13908 }
13909
13910 ani_p.easingImpl = easings[name].apply(null, args);
13911 } else {
13912 // static impl by name
13913 ani_p.easingImpl = easings[name];
13914 }
13915 }
13916 }
13917
13918 var easing = ani_p.easingImpl;
13919 var percent;
13920
13921 if (ani_p.duration === 0) {
13922 percent = 1;
13923 } else {
13924 percent = (now - startTime) / ani_p.duration;
13925 }
13926
13927 if (ani_p.applying) {
13928 percent = ani_p.progress;
13929 }
13930
13931 if (percent < 0) {
13932 percent = 0;
13933 } else if (percent > 1) {
13934 percent = 1;
13935 }
13936
13937 if (ani_p.delay == null) {
13938 // then update
13939 var startPos = ani_p.startPosition;
13940 var endPos = ani_p.position;
13941
13942 if (endPos && isEles && !self.locked()) {
13943 var newPos = {};
13944
13945 if (valid(startPos.x, endPos.x)) {
13946 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13947 }
13948
13949 if (valid(startPos.y, endPos.y)) {
13950 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13951 }
13952
13953 self.position(newPos);
13954 }
13955
13956 var startPan = ani_p.startPan;
13957 var endPan = ani_p.pan;
13958 var pan = _p.pan;
13959 var animatingPan = endPan != null && isCore;
13960
13961 if (animatingPan) {
13962 if (valid(startPan.x, endPan.x)) {
13963 pan.x = ease(startPan.x, endPan.x, percent, easing);
13964 }
13965
13966 if (valid(startPan.y, endPan.y)) {
13967 pan.y = ease(startPan.y, endPan.y, percent, easing);
13968 }
13969
13970 self.emit('pan');
13971 }
13972
13973 var startZoom = ani_p.startZoom;
13974 var endZoom = ani_p.zoom;
13975 var animatingZoom = endZoom != null && isCore;
13976
13977 if (animatingZoom) {
13978 if (valid(startZoom, endZoom)) {
13979 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13980 }
13981
13982 self.emit('zoom');
13983 }
13984
13985 if (animatingPan || animatingZoom) {
13986 self.emit('viewport');
13987 }
13988
13989 var props = ani_p.style;
13990
13991 if (props && props.length > 0 && isEles) {
13992 for (var i = 0; i < props.length; i++) {
13993 var prop = props[i];
13994 var _name = prop.name;
13995 var end = prop;
13996 var start = ani_p.startStyle[_name];
13997 var propSpec = style.properties[start.name];
13998 var easedVal = ease(start, end, percent, easing, propSpec);
13999 style.overrideBypass(self, _name, easedVal);
14000 } // for props
14001
14002
14003 self.emit('style');
14004 } // if
14005
14006 }
14007
14008 ani_p.progress = percent;
14009 return percent;
14010}
14011
14012function valid(start, end) {
14013 if (start == null || end == null) {
14014 return false;
14015 }
14016
14017 if (number$1(start) && number$1(end)) {
14018 return true;
14019 } else if (start && end) {
14020 return true;
14021 }
14022
14023 return false;
14024}
14025
14026function startAnimation(self, ani, now, isCore) {
14027 var ani_p = ani._private;
14028 ani_p.started = true;
14029 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14030}
14031
14032function stepAll(now, cy) {
14033 var eles = cy._private.aniEles;
14034 var doneEles = [];
14035
14036 function stepOne(ele, isCore) {
14037 var _p = ele._private;
14038 var current = _p.animation.current;
14039 var queue = _p.animation.queue;
14040 var ranAnis = false; // if nothing currently animating, get something from the queue
14041
14042 if (current.length === 0) {
14043 var next = queue.shift();
14044
14045 if (next) {
14046 current.push(next);
14047 }
14048 }
14049
14050 var callbacks = function callbacks(_callbacks) {
14051 for (var j = _callbacks.length - 1; j >= 0; j--) {
14052 var cb = _callbacks[j];
14053 cb();
14054 }
14055
14056 _callbacks.splice(0, _callbacks.length);
14057 }; // step and remove if done
14058
14059
14060 for (var i = current.length - 1; i >= 0; i--) {
14061 var ani = current[i];
14062 var ani_p = ani._private;
14063
14064 if (ani_p.stopped) {
14065 current.splice(i, 1);
14066 ani_p.hooked = false;
14067 ani_p.playing = false;
14068 ani_p.started = false;
14069 callbacks(ani_p.frames);
14070 continue;
14071 }
14072
14073 if (!ani_p.playing && !ani_p.applying) {
14074 continue;
14075 } // an apply() while playing shouldn't do anything
14076
14077
14078 if (ani_p.playing && ani_p.applying) {
14079 ani_p.applying = false;
14080 }
14081
14082 if (!ani_p.started) {
14083 startAnimation(ele, ani, now);
14084 }
14085
14086 step$1(ele, ani, now, isCore);
14087
14088 if (ani_p.applying) {
14089 ani_p.applying = false;
14090 }
14091
14092 callbacks(ani_p.frames);
14093
14094 if (ani_p.step != null) {
14095 ani_p.step(now);
14096 }
14097
14098 if (ani.completed()) {
14099 current.splice(i, 1);
14100 ani_p.hooked = false;
14101 ani_p.playing = false;
14102 ani_p.started = false;
14103 callbacks(ani_p.completes);
14104 }
14105
14106 ranAnis = true;
14107 }
14108
14109 if (!isCore && current.length === 0 && queue.length === 0) {
14110 doneEles.push(ele);
14111 }
14112
14113 return ranAnis;
14114 } // stepElement
14115 // handle all eles
14116
14117
14118 var ranEleAni = false;
14119
14120 for (var e = 0; e < eles.length; e++) {
14121 var ele = eles[e];
14122 var handledThisEle = stepOne(ele);
14123 ranEleAni = ranEleAni || handledThisEle;
14124 } // each element
14125
14126
14127 var ranCoreAni = stepOne(cy, true); // notify renderer
14128
14129 if (ranEleAni || ranCoreAni) {
14130 if (eles.length > 0) {
14131 cy.notify('draw', eles);
14132 } else {
14133 cy.notify('draw');
14134 }
14135 } // remove elements from list of currently animating if its queues are empty
14136
14137
14138 eles.unmerge(doneEles);
14139 cy.emit('step');
14140} // stepAll
14141
14142var corefn$8 = {
14143 // pull in animation functions
14144 animate: define.animate(),
14145 animation: define.animation(),
14146 animated: define.animated(),
14147 clearQueue: define.clearQueue(),
14148 delay: define.delay(),
14149 delayAnimation: define.delayAnimation(),
14150 stop: define.stop(),
14151 addToAnimationPool: function addToAnimationPool(eles) {
14152 var cy = this;
14153
14154 if (!cy.styleEnabled()) {
14155 return;
14156 } // save cycles when no style used
14157
14158
14159 cy._private.aniEles.merge(eles);
14160 },
14161 stopAnimationLoop: function stopAnimationLoop() {
14162 this._private.animationsRunning = false;
14163 },
14164 startAnimationLoop: function startAnimationLoop() {
14165 var cy = this;
14166 cy._private.animationsRunning = true;
14167
14168 if (!cy.styleEnabled()) {
14169 return;
14170 } // save cycles when no style used
14171 // NB the animation loop will exec in headless environments if style enabled
14172 // and explicit cy.destroy() is necessary to stop the loop
14173
14174
14175 function headlessStep() {
14176 if (!cy._private.animationsRunning) {
14177 return;
14178 }
14179
14180 requestAnimationFrame(function animationStep(now) {
14181 stepAll(now, cy);
14182 headlessStep();
14183 });
14184 }
14185
14186 var renderer = cy.renderer();
14187
14188 if (renderer && renderer.beforeRender) {
14189 // let the renderer schedule animations
14190 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14191 stepAll(now, cy);
14192 }, renderer.beforeRenderPriorities.animations);
14193 } else {
14194 // manage the animation loop ourselves
14195 headlessStep(); // first call
14196 }
14197 }
14198};
14199
14200var emitterOptions = {
14201 qualifierCompare: function qualifierCompare(selector1, selector2) {
14202 if (selector1 == null || selector2 == null) {
14203 return selector1 == null && selector2 == null;
14204 } else {
14205 return selector1.sameText(selector2);
14206 }
14207 },
14208 eventMatches: function eventMatches(cy, listener, eventObj) {
14209 var selector = listener.qualifier;
14210
14211 if (selector != null) {
14212 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14213 }
14214
14215 return true;
14216 },
14217 addEventFields: function addEventFields(cy, evt) {
14218 evt.cy = cy;
14219 evt.target = cy;
14220 },
14221 callbackContext: function callbackContext(cy, listener, eventObj) {
14222 return listener.qualifier != null ? eventObj.target : cy;
14223 }
14224};
14225
14226var argSelector = function argSelector(arg) {
14227 if (string(arg)) {
14228 return new Selector(arg);
14229 } else {
14230 return arg;
14231 }
14232};
14233
14234var elesfn = {
14235 createEmitter: function createEmitter() {
14236 var _p = this._private;
14237
14238 if (!_p.emitter) {
14239 _p.emitter = new Emitter(emitterOptions, this);
14240 }
14241
14242 return this;
14243 },
14244 emitter: function emitter() {
14245 return this._private.emitter;
14246 },
14247 on: function on(events, selector, callback) {
14248 this.emitter().on(events, argSelector(selector), callback);
14249 return this;
14250 },
14251 removeListener: function removeListener(events, selector, callback) {
14252 this.emitter().removeListener(events, argSelector(selector), callback);
14253 return this;
14254 },
14255 removeAllListeners: function removeAllListeners() {
14256 this.emitter().removeAllListeners();
14257 return this;
14258 },
14259 one: function one(events, selector, callback) {
14260 this.emitter().one(events, argSelector(selector), callback);
14261 return this;
14262 },
14263 once: function once(events, selector, callback) {
14264 this.emitter().one(events, argSelector(selector), callback);
14265 return this;
14266 },
14267 emit: function emit(events, extraParams) {
14268 this.emitter().emit(events, extraParams);
14269 return this;
14270 },
14271 emitAndNotify: function emitAndNotify(event, eles) {
14272 this.emit(event);
14273 this.notify(event, eles);
14274 return this;
14275 }
14276};
14277define.eventAliasesOn(elesfn);
14278
14279var corefn$7 = {
14280 png: function png(options) {
14281 var renderer = this._private.renderer;
14282 options = options || {};
14283 return renderer.png(options);
14284 },
14285 jpg: function jpg(options) {
14286 var renderer = this._private.renderer;
14287 options = options || {};
14288 options.bg = options.bg || '#fff';
14289 return renderer.jpg(options);
14290 }
14291};
14292corefn$7.jpeg = corefn$7.jpg;
14293
14294var corefn$6 = {
14295 layout: function layout(options) {
14296 var cy = this;
14297
14298 if (options == null) {
14299 error('Layout options must be specified to make a layout');
14300 return;
14301 }
14302
14303 if (options.name == null) {
14304 error('A `name` must be specified to make a layout');
14305 return;
14306 }
14307
14308 var name = options.name;
14309 var Layout = cy.extension('layout', name);
14310
14311 if (Layout == null) {
14312 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14313 return;
14314 }
14315
14316 var eles;
14317
14318 if (string(options.eles)) {
14319 eles = cy.$(options.eles);
14320 } else {
14321 eles = options.eles != null ? options.eles : cy.$();
14322 }
14323
14324 var layout = new Layout(extend({}, options, {
14325 cy: cy,
14326 eles: eles
14327 }));
14328 return layout;
14329 }
14330};
14331corefn$6.createLayout = corefn$6.makeLayout = corefn$6.layout;
14332
14333var corefn$5 = {
14334 notify: function notify(eventName, eventEles) {
14335 var _p = this._private;
14336
14337 if (this.batching()) {
14338 _p.batchNotifications = _p.batchNotifications || {};
14339 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14340
14341 if (eventEles != null) {
14342 eles.merge(eventEles);
14343 }
14344
14345 return; // notifications are disabled during batching
14346 }
14347
14348 if (!_p.notificationsEnabled) {
14349 return;
14350 } // exit on disabled
14351
14352
14353 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14354
14355 if (this.destroyed() || !renderer) {
14356 return;
14357 }
14358
14359 renderer.notify(eventName, eventEles);
14360 },
14361 notifications: function notifications(bool) {
14362 var p = this._private;
14363
14364 if (bool === undefined) {
14365 return p.notificationsEnabled;
14366 } else {
14367 p.notificationsEnabled = bool ? true : false;
14368 }
14369
14370 return this;
14371 },
14372 noNotifications: function noNotifications(callback) {
14373 this.notifications(false);
14374 callback();
14375 this.notifications(true);
14376 },
14377 batching: function batching() {
14378 return this._private.batchCount > 0;
14379 },
14380 startBatch: function startBatch() {
14381 var _p = this._private;
14382
14383 if (_p.batchCount == null) {
14384 _p.batchCount = 0;
14385 }
14386
14387 if (_p.batchCount === 0) {
14388 _p.batchStyleEles = this.collection();
14389 _p.batchNotifications = {};
14390 }
14391
14392 _p.batchCount++;
14393 return this;
14394 },
14395 endBatch: function endBatch() {
14396 var _p = this._private;
14397
14398 if (_p.batchCount === 0) {
14399 return this;
14400 }
14401
14402 _p.batchCount--;
14403
14404 if (_p.batchCount === 0) {
14405 // update style for dirty eles
14406 _p.batchStyleEles.updateStyle();
14407
14408 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14409
14410 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14411 var eles = _p.batchNotifications[eventName];
14412
14413 if (eles.empty()) {
14414 renderer.notify(eventName);
14415 } else {
14416 renderer.notify(eventName, eles);
14417 }
14418 });
14419 }
14420
14421 return this;
14422 },
14423 batch: function batch(callback) {
14424 this.startBatch();
14425 callback();
14426 this.endBatch();
14427 return this;
14428 },
14429 // for backwards compatibility
14430 batchData: function batchData(map) {
14431 var cy = this;
14432 return this.batch(function () {
14433 var ids = Object.keys(map);
14434
14435 for (var i = 0; i < ids.length; i++) {
14436 var id = ids[i];
14437 var data = map[id];
14438 var ele = cy.getElementById(id);
14439 ele.data(data);
14440 }
14441 });
14442 }
14443};
14444
14445var rendererDefaults = defaults$g({
14446 hideEdgesOnViewport: false,
14447 textureOnViewport: false,
14448 motionBlur: false,
14449 motionBlurOpacity: 0.05,
14450 pixelRatio: undefined,
14451 desktopTapThreshold: 4,
14452 touchTapThreshold: 8,
14453 wheelSensitivity: 1,
14454 debug: false,
14455 showFps: false
14456});
14457var corefn$4 = {
14458 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14459 var r = this._private.renderer;
14460 r.renderTo(context, zoom, pan, pxRatio);
14461 return this;
14462 },
14463 renderer: function renderer() {
14464 return this._private.renderer;
14465 },
14466 forceRender: function forceRender() {
14467 this.notify('draw');
14468 return this;
14469 },
14470 resize: function resize() {
14471 this.invalidateSize();
14472 this.emitAndNotify('resize');
14473 return this;
14474 },
14475 initRenderer: function initRenderer(options) {
14476 var cy = this;
14477 var RendererProto = cy.extension('renderer', options.name);
14478
14479 if (RendererProto == null) {
14480 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14481 return;
14482 }
14483
14484 if (options.wheelSensitivity !== undefined) {
14485 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.");
14486 }
14487
14488 var rOpts = rendererDefaults(options);
14489 rOpts.cy = cy;
14490 cy._private.renderer = new RendererProto(rOpts);
14491 this.notify('init');
14492 },
14493 destroyRenderer: function destroyRenderer() {
14494 var cy = this;
14495 cy.notify('destroy'); // destroy the renderer
14496
14497 var domEle = cy.container();
14498
14499 if (domEle) {
14500 domEle._cyreg = null;
14501
14502 while (domEle.childNodes.length > 0) {
14503 domEle.removeChild(domEle.childNodes[0]);
14504 }
14505 }
14506
14507 cy._private.renderer = null; // to be extra safe, remove the ref
14508
14509 cy.mutableElements().forEach(function (ele) {
14510 var _p = ele._private;
14511 _p.rscratch = {};
14512 _p.rstyle = {};
14513 _p.animation.current = [];
14514 _p.animation.queue = [];
14515 });
14516 },
14517 onRender: function onRender(fn) {
14518 return this.on('render', fn);
14519 },
14520 offRender: function offRender(fn) {
14521 return this.off('render', fn);
14522 }
14523};
14524corefn$4.invalidateDimensions = corefn$4.resize;
14525
14526var corefn$3 = {
14527 // get a collection
14528 // - empty collection on no args
14529 // - collection of elements in the graph on selector arg
14530 // - guarantee a returned collection when elements or collection specified
14531 collection: function collection(eles, opts) {
14532 if (string(eles)) {
14533 return this.$(eles);
14534 } else if (elementOrCollection(eles)) {
14535 return eles.collection();
14536 } else if (array(eles)) {
14537 if (!opts) {
14538 opts = {};
14539 }
14540
14541 return new Collection(this, eles, opts.unique, opts.removed);
14542 }
14543
14544 return new Collection(this);
14545 },
14546 nodes: function nodes(selector) {
14547 var nodes = this.$(function (ele) {
14548 return ele.isNode();
14549 });
14550
14551 if (selector) {
14552 return nodes.filter(selector);
14553 }
14554
14555 return nodes;
14556 },
14557 edges: function edges(selector) {
14558 var edges = this.$(function (ele) {
14559 return ele.isEdge();
14560 });
14561
14562 if (selector) {
14563 return edges.filter(selector);
14564 }
14565
14566 return edges;
14567 },
14568 // search the graph like jQuery
14569 $: function $(selector) {
14570 var eles = this._private.elements;
14571
14572 if (selector) {
14573 return eles.filter(selector);
14574 } else {
14575 return eles.spawnSelf();
14576 }
14577 },
14578 mutableElements: function mutableElements() {
14579 return this._private.elements;
14580 }
14581}; // aliases
14582
14583corefn$3.elements = corefn$3.filter = corefn$3.$;
14584
14585var styfn$8 = {}; // keys for style blocks, e.g. ttfftt
14586
14587var TRUE = 't';
14588var FALSE = 'f'; // (potentially expensive calculation)
14589// apply the style to the element based on
14590// - its bypass
14591// - what selectors match it
14592
14593styfn$8.apply = function (eles) {
14594 var self = this;
14595 var _p = self._private;
14596 var cy = _p.cy;
14597 var updatedEles = cy.collection();
14598
14599 for (var ie = 0; ie < eles.length; ie++) {
14600 var ele = eles[ie];
14601 var cxtMeta = self.getContextMeta(ele);
14602
14603 if (cxtMeta.empty) {
14604 continue;
14605 }
14606
14607 var cxtStyle = self.getContextStyle(cxtMeta);
14608 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14609
14610 if (ele._private.appliedInitStyle) {
14611 self.updateTransitions(ele, app.diffProps);
14612 } else {
14613 ele._private.appliedInitStyle = true;
14614 }
14615
14616 var hintsDiff = self.updateStyleHints(ele);
14617
14618 if (hintsDiff) {
14619 updatedEles.push(ele);
14620 }
14621 } // for elements
14622
14623
14624 return updatedEles;
14625};
14626
14627styfn$8.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14628 var self = this;
14629 var cache = self._private.propDiffs = self._private.propDiffs || {};
14630 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14631 var cachedVal = cache[dualCxtKey];
14632
14633 if (cachedVal) {
14634 return cachedVal;
14635 }
14636
14637 var diffProps = [];
14638 var addedProp = {};
14639
14640 for (var i = 0; i < self.length; i++) {
14641 var cxt = self[i];
14642 var oldHasCxt = oldCxtKey[i] === TRUE;
14643 var newHasCxt = newCxtKey[i] === TRUE;
14644 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14645 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14646
14647 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14648 var props = void 0;
14649
14650 if (cxtHasDiffed && cxtHasMappedProps) {
14651 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14652 } else if (cxtHasDiffed) {
14653 props = cxt.properties; // need to check them all
14654 } else if (cxtHasMappedProps) {
14655 props = cxt.mappedProperties; // only need to check mapped
14656 }
14657
14658 for (var j = 0; j < props.length; j++) {
14659 var prop = props[j];
14660 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14661 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14662 // is cached)
14663
14664 var laterCxtOverrides = false;
14665
14666 for (var k = i + 1; k < self.length; k++) {
14667 var laterCxt = self[k];
14668 var hasLaterCxt = newCxtKey[k] === TRUE;
14669
14670 if (!hasLaterCxt) {
14671 continue;
14672 } // can't override unless the context is active
14673
14674
14675 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14676
14677 if (laterCxtOverrides) {
14678 break;
14679 } // exit early as long as one later context overrides
14680
14681 }
14682
14683 if (!addedProp[name] && !laterCxtOverrides) {
14684 addedProp[name] = true;
14685 diffProps.push(name);
14686 }
14687 } // for props
14688
14689 } // if
14690
14691 } // for contexts
14692
14693
14694 cache[dualCxtKey] = diffProps;
14695 return diffProps;
14696};
14697
14698styfn$8.getContextMeta = function (ele) {
14699 var self = this;
14700 var cxtKey = '';
14701 var diffProps;
14702 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
14703
14704 for (var i = 0; i < self.length; i++) {
14705 var context = self[i];
14706 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14707
14708 if (contextSelectorMatches) {
14709 cxtKey += TRUE;
14710 } else {
14711 cxtKey += FALSE;
14712 }
14713 } // for context
14714
14715
14716 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14717 ele._private.styleCxtKey = cxtKey;
14718 return {
14719 key: cxtKey,
14720 diffPropNames: diffProps,
14721 empty: diffProps.length === 0
14722 };
14723}; // gets a computed ele style object based on matched contexts
14724
14725
14726styfn$8.getContextStyle = function (cxtMeta) {
14727 var cxtKey = cxtMeta.key;
14728 var self = this;
14729 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14730
14731 if (cxtStyles[cxtKey]) {
14732 return cxtStyles[cxtKey];
14733 }
14734
14735 var style = {
14736 _private: {
14737 key: cxtKey
14738 }
14739 };
14740
14741 for (var i = 0; i < self.length; i++) {
14742 var cxt = self[i];
14743 var hasCxt = cxtKey[i] === TRUE;
14744
14745 if (!hasCxt) {
14746 continue;
14747 }
14748
14749 for (var j = 0; j < cxt.properties.length; j++) {
14750 var prop = cxt.properties[j];
14751 style[prop.name] = prop;
14752 }
14753 }
14754
14755 cxtStyles[cxtKey] = style;
14756 return style;
14757};
14758
14759styfn$8.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14760 var self = this;
14761 var diffProps = cxtMeta.diffPropNames;
14762 var retDiffProps = {};
14763 var types = self.types;
14764
14765 for (var i = 0; i < diffProps.length; i++) {
14766 var diffPropName = diffProps[i];
14767 var cxtProp = cxtStyle[diffPropName];
14768 var eleProp = ele.pstyle(diffPropName);
14769
14770 if (!cxtProp) {
14771 // no context prop means delete
14772 if (!eleProp) {
14773 continue; // no existing prop means nothing needs to be removed
14774 // nb affects initial application on mapped values like control-point-distances
14775 } else if (eleProp.bypass) {
14776 cxtProp = {
14777 name: diffPropName,
14778 deleteBypassed: true
14779 };
14780 } else {
14781 cxtProp = {
14782 name: diffPropName,
14783 "delete": true
14784 };
14785 }
14786 } // save cycles when the context prop doesn't need to be applied
14787
14788
14789 if (eleProp === cxtProp) {
14790 continue;
14791 } // save cycles when a mapped context prop doesn't need to be applied
14792
14793
14794 if (cxtProp.mapped === types.fn // context prop is function mapper
14795 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14796 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14797 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14798 ) {
14799 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14800 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14801
14802 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14803
14804 if (fnValue === mapping.prevFnValue) {
14805 continue;
14806 }
14807 }
14808
14809 var retDiffProp = retDiffProps[diffPropName] = {
14810 prev: eleProp
14811 };
14812 self.applyParsedProperty(ele, cxtProp);
14813 retDiffProp.next = ele.pstyle(diffPropName);
14814
14815 if (retDiffProp.next && retDiffProp.next.bypass) {
14816 retDiffProp.next = retDiffProp.next.bypassed;
14817 }
14818 }
14819
14820 return {
14821 diffProps: retDiffProps
14822 };
14823};
14824
14825styfn$8.updateStyleHints = function (ele) {
14826 var _p = ele._private;
14827 var self = this;
14828 var propNames = self.propertyGroupNames;
14829 var propGrKeys = self.propertyGroupKeys;
14830
14831 var propHash = function propHash(ele, propNames, seedKey) {
14832 return self.getPropertiesHash(ele, propNames, seedKey);
14833 };
14834
14835 var oldStyleKey = _p.styleKey;
14836
14837 if (ele.removed()) {
14838 return false;
14839 }
14840
14841 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14842 // but lazily -- only use non-default prop values to reduce the number of hashes
14843 //
14844
14845 var overriddenStyles = ele._private.style;
14846 propNames = Object.keys(overriddenStyles);
14847
14848 for (var i = 0; i < propGrKeys.length; i++) {
14849 var grKey = propGrKeys[i];
14850 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14851 }
14852
14853 var updateGrKey1 = function updateGrKey1(val, grKey) {
14854 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14855 };
14856
14857 var updateGrKey2 = function updateGrKey2(val, grKey) {
14858 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14859 };
14860
14861 var updateGrKey = function updateGrKey(val, grKey) {
14862 updateGrKey1(val, grKey);
14863 updateGrKey2(val, grKey);
14864 };
14865
14866 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14867 for (var j = 0; j < strVal.length; j++) {
14868 var ch = strVal.charCodeAt(j);
14869 updateGrKey1(ch, grKey);
14870 updateGrKey2(ch, grKey);
14871 }
14872 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14873 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14874 // - raise up small numbers so more significant digits are seen by hashing
14875 // - make small numbers larger than a normal value to avoid collisions
14876 // - works in practice and it's relatively cheap
14877
14878
14879 var N = 2000000000;
14880
14881 var cleanNum = function cleanNum(val) {
14882 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14883 };
14884
14885 for (var _i = 0; _i < propNames.length; _i++) {
14886 var name = propNames[_i];
14887 var parsedProp = overriddenStyles[name];
14888
14889 if (parsedProp == null) {
14890 continue;
14891 }
14892
14893 var propInfo = this.properties[name];
14894 var type = propInfo.type;
14895 var _grKey = propInfo.groupKey;
14896 var normalizedNumberVal = void 0;
14897
14898 if (propInfo.hashOverride != null) {
14899 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14900 } else if (parsedProp.pfValue != null) {
14901 normalizedNumberVal = parsedProp.pfValue;
14902 } // might not be a number if it allows enums
14903
14904
14905 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14906 var haveNormNum = normalizedNumberVal != null;
14907 var haveUnitedNum = numberVal != null;
14908 var haveNum = haveNormNum || haveUnitedNum;
14909 var units = parsedProp.units; // numbers are cheaper to hash than strings
14910 // 1 hash op vs n hash ops (for length n string)
14911
14912 if (type.number && haveNum && !type.multiple) {
14913 var v = haveNormNum ? normalizedNumberVal : numberVal;
14914 updateGrKey(cleanNum(v), _grKey);
14915
14916 if (!haveNormNum && units != null) {
14917 updateGrKeyWStr(units, _grKey);
14918 }
14919 } else {
14920 updateGrKeyWStr(parsedProp.strValue, _grKey);
14921 }
14922 } // overall style key
14923 //
14924
14925
14926 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14927
14928 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14929 var _grKey2 = propGrKeys[_i2];
14930 var grHash = _p.styleKeys[_grKey2];
14931 hash[0] = hashInt(grHash[0], hash[0]);
14932 hash[1] = hashIntAlt(grHash[1], hash[1]);
14933 }
14934
14935 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14936 //
14937
14938 var sk = _p.styleKeys;
14939 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14940 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14941 _p.labelKey = combineHashesArray(labelKeys);
14942 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14943
14944 if (!isNode) {
14945 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14946 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14947 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14948 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14949 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14950 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14951 } // node
14952 //
14953
14954
14955 if (isNode) {
14956 var _p$styleKeys = _p.styleKeys,
14957 nodeBody = _p$styleKeys.nodeBody,
14958 nodeBorder = _p$styleKeys.nodeBorder,
14959 backgroundImage = _p$styleKeys.backgroundImage,
14960 compound = _p$styleKeys.compound,
14961 pie = _p$styleKeys.pie;
14962 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14963 return k != null;
14964 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14965 _p.nodeKey = combineHashesArray(nodeKeys);
14966 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14967 }
14968
14969 return oldStyleKey !== _p.styleKey;
14970};
14971
14972styfn$8.clearStyleHints = function (ele) {
14973 var _p = ele._private;
14974 _p.styleCxtKey = '';
14975 _p.styleKeys = {};
14976 _p.styleKey = null;
14977 _p.labelKey = null;
14978 _p.labelStyleKey = null;
14979 _p.sourceLabelKey = null;
14980 _p.sourceLabelStyleKey = null;
14981 _p.targetLabelKey = null;
14982 _p.targetLabelStyleKey = null;
14983 _p.nodeKey = null;
14984 _p.hasPie = null;
14985}; // apply a property to the style (for internal use)
14986// returns whether application was successful
14987//
14988// now, this function flattens the property, and here's how:
14989//
14990// for parsedProp:{ bypass: true, deleteBypass: true }
14991// no property is generated, instead the bypass property in the
14992// element's style is replaced by what's pointed to by the `bypassed`
14993// field in the bypass property (i.e. restoring the property the
14994// bypass was overriding)
14995//
14996// for parsedProp:{ mapped: truthy }
14997// the generated flattenedProp:{ mapping: prop }
14998//
14999// for parsedProp:{ bypass: true }
15000// the generated flattenedProp:{ bypassed: parsedProp }
15001
15002
15003styfn$8.applyParsedProperty = function (ele, parsedProp) {
15004 var self = this;
15005 var prop = parsedProp;
15006 var style = ele._private.style;
15007 var flatProp;
15008 var types = self.types;
15009 var type = self.properties[prop.name].type;
15010 var propIsBypass = prop.bypass;
15011 var origProp = style[prop.name];
15012 var origPropIsBypass = origProp && origProp.bypass;
15013 var _p = ele._private;
15014 var flatPropMapping = 'mapping';
15015
15016 var getVal = function getVal(p) {
15017 if (p == null) {
15018 return null;
15019 } else if (p.pfValue != null) {
15020 return p.pfValue;
15021 } else {
15022 return p.value;
15023 }
15024 };
15025
15026 var checkTriggers = function checkTriggers() {
15027 var fromVal = getVal(origProp);
15028 var toVal = getVal(prop);
15029 self.checkTriggers(ele, prop.name, fromVal, toVal);
15030 };
15031
15032 if (prop && prop.name.substr(0, 3) === 'pie') {
15033 warn('The pie style properties are deprecated. Create charts using background images instead.');
15034 } // edge sanity checks to prevent the client from making serious mistakes
15035
15036
15037 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
15038 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
15039 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15040 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15041 }
15042
15043 if (prop["delete"]) {
15044 // delete the property and use the default value on falsey value
15045 style[prop.name] = undefined;
15046 checkTriggers();
15047 return true;
15048 }
15049
15050 if (prop.deleteBypassed) {
15051 // delete the property that the
15052 if (!origProp) {
15053 checkTriggers();
15054 return true; // can't delete if no prop
15055 } else if (origProp.bypass) {
15056 // delete bypassed
15057 origProp.bypassed = undefined;
15058 checkTriggers();
15059 return true;
15060 } else {
15061 return false; // we're unsuccessful deleting the bypassed
15062 }
15063 } // check if we need to delete the current bypass
15064
15065
15066 if (prop.deleteBypass) {
15067 // then this property is just here to indicate we need to delete
15068 if (!origProp) {
15069 checkTriggers();
15070 return true; // property is already not defined
15071 } else if (origProp.bypass) {
15072 // then replace the bypass property with the original
15073 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15074 style[prop.name] = origProp.bypassed;
15075 checkTriggers();
15076 return true;
15077 } else {
15078 return false; // we're unsuccessful deleting the bypass
15079 }
15080 }
15081
15082 var printMappingErr = function printMappingErr() {
15083 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');
15084 }; // put the property in the style objects
15085
15086
15087 switch (prop.mapped) {
15088 // flatten the property if mapped
15089 case types.mapData:
15090 {
15091 // flatten the field (e.g. data.foo.bar)
15092 var fields = prop.field.split('.');
15093 var fieldVal = _p.data;
15094
15095 for (var i = 0; i < fields.length && fieldVal; i++) {
15096 var field = fields[i];
15097 fieldVal = fieldVal[field];
15098 }
15099
15100 if (fieldVal == null) {
15101 printMappingErr();
15102 return false;
15103 }
15104
15105 var percent;
15106
15107 if (!number$1(fieldVal)) {
15108 // then don't apply and fall back on the existing style
15109 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15110 return false;
15111 } else {
15112 var fieldWidth = prop.fieldMax - prop.fieldMin;
15113
15114 if (fieldWidth === 0) {
15115 // safety check -- not strictly necessary as no props of zero range should be passed here
15116 percent = 0;
15117 } else {
15118 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15119 }
15120 } // make sure to bound percent value
15121
15122
15123 if (percent < 0) {
15124 percent = 0;
15125 } else if (percent > 1) {
15126 percent = 1;
15127 }
15128
15129 if (type.color) {
15130 var r1 = prop.valueMin[0];
15131 var r2 = prop.valueMax[0];
15132 var g1 = prop.valueMin[1];
15133 var g2 = prop.valueMax[1];
15134 var b1 = prop.valueMin[2];
15135 var b2 = prop.valueMax[2];
15136 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15137 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15138 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)];
15139 flatProp = {
15140 // colours are simple, so just create the flat property instead of expensive string parsing
15141 bypass: prop.bypass,
15142 // we're a bypass if the mapping property is a bypass
15143 name: prop.name,
15144 value: clr,
15145 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15146 };
15147 } else if (type.number) {
15148 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15149 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15150 } else {
15151 return false; // can only map to colours and numbers
15152 }
15153
15154 if (!flatProp) {
15155 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15156 printMappingErr();
15157 return false;
15158 }
15159
15160 flatProp.mapping = prop; // keep a reference to the mapping
15161
15162 prop = flatProp; // the flattened (mapped) property is the one we want
15163
15164 break;
15165 }
15166 // direct mapping
15167
15168 case types.data:
15169 {
15170 // flatten the field (e.g. data.foo.bar)
15171 var _fields = prop.field.split('.');
15172
15173 var _fieldVal = _p.data;
15174
15175 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15176 var _field = _fields[_i3];
15177 _fieldVal = _fieldVal[_field];
15178 }
15179
15180 if (_fieldVal != null) {
15181 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15182 }
15183
15184 if (!flatProp) {
15185 // if we can't flatten the property, then don't apply and fall back on the existing style
15186 printMappingErr();
15187 return false;
15188 }
15189
15190 flatProp.mapping = prop; // keep a reference to the mapping
15191
15192 prop = flatProp; // the flattened (mapped) property is the one we want
15193
15194 break;
15195 }
15196
15197 case types.fn:
15198 {
15199 var fn = prop.value;
15200 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15201
15202 prop.prevFnValue = fnRetVal;
15203
15204 if (fnRetVal == null) {
15205 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15206 return false;
15207 }
15208
15209 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15210
15211 if (!flatProp) {
15212 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15213 return false;
15214 }
15215
15216 flatProp.mapping = copy(prop); // keep a reference to the mapping
15217
15218 prop = flatProp; // the flattened (mapped) property is the one we want
15219
15220 break;
15221 }
15222
15223 case undefined:
15224 break;
15225 // just set the property
15226
15227 default:
15228 return false;
15229 // not a valid mapping
15230 } // if the property is a bypass property, then link the resultant property to the original one
15231
15232
15233 if (propIsBypass) {
15234 if (origPropIsBypass) {
15235 // then this bypass overrides the existing one
15236 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15237 } else {
15238 // then link the orig prop to the new bypass
15239 prop.bypassed = origProp;
15240 }
15241
15242 style[prop.name] = prop; // and set
15243 } else {
15244 // prop is not bypass
15245 if (origPropIsBypass) {
15246 // then keep the orig prop (since it's a bypass) and link to the new prop
15247 origProp.bypassed = prop;
15248 } else {
15249 // then just replace the old prop with the new one
15250 style[prop.name] = prop;
15251 }
15252 }
15253
15254 checkTriggers();
15255 return true;
15256};
15257
15258styfn$8.cleanElements = function (eles, keepBypasses) {
15259 for (var i = 0; i < eles.length; i++) {
15260 var ele = eles[i];
15261 this.clearStyleHints(ele);
15262 ele.dirtyCompoundBoundsCache();
15263 ele.dirtyBoundingBoxCache();
15264
15265 if (!keepBypasses) {
15266 ele._private.style = {};
15267 } else {
15268 var style = ele._private.style;
15269 var propNames = Object.keys(style);
15270
15271 for (var j = 0; j < propNames.length; j++) {
15272 var propName = propNames[j];
15273 var eleProp = style[propName];
15274
15275 if (eleProp != null) {
15276 if (eleProp.bypass) {
15277 eleProp.bypassed = null;
15278 } else {
15279 style[propName] = null;
15280 }
15281 }
15282 }
15283 }
15284 }
15285}; // updates the visual style for all elements (useful for manual style modification after init)
15286
15287
15288styfn$8.update = function () {
15289 var cy = this._private.cy;
15290 var eles = cy.mutableElements();
15291 eles.updateStyle();
15292}; // diffProps : { name => { prev, next } }
15293
15294
15295styfn$8.updateTransitions = function (ele, diffProps) {
15296 var self = this;
15297 var _p = ele._private;
15298 var props = ele.pstyle('transition-property').value;
15299 var duration = ele.pstyle('transition-duration').pfValue;
15300 var delay = ele.pstyle('transition-delay').pfValue;
15301
15302 if (props.length > 0 && duration > 0) {
15303 var style = {}; // build up the style to animate towards
15304
15305 var anyPrev = false;
15306
15307 for (var i = 0; i < props.length; i++) {
15308 var prop = props[i];
15309 var styProp = ele.pstyle(prop);
15310 var diffProp = diffProps[prop];
15311
15312 if (!diffProp) {
15313 continue;
15314 }
15315
15316 var prevProp = diffProp.prev;
15317 var fromProp = prevProp;
15318 var toProp = diffProp.next != null ? diffProp.next : styProp;
15319 var diff = false;
15320 var initVal = void 0;
15321 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15322
15323 if (!fromProp) {
15324 continue;
15325 } // consider px values
15326
15327
15328 if (number$1(fromProp.pfValue) && number$1(toProp.pfValue)) {
15329 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15330
15331 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15332 } else if (number$1(fromProp.value) && number$1(toProp.value)) {
15333 diff = toProp.value - fromProp.value; // nonzero is truthy
15334
15335 initVal = fromProp.value + initDt * diff; // consider colour values
15336 } else if (array(fromProp.value) && array(toProp.value)) {
15337 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15338 initVal = fromProp.strValue;
15339 } // the previous value is good for an animation only if it's different
15340
15341
15342 if (diff) {
15343 style[prop] = toProp.strValue; // to val
15344
15345 this.applyBypass(ele, prop, initVal); // from val
15346
15347 anyPrev = true;
15348 }
15349 } // end if props allow ani
15350 // can't transition if there's nothing previous to transition from
15351
15352
15353 if (!anyPrev) {
15354 return;
15355 }
15356
15357 _p.transitioning = true;
15358 new Promise$1(function (resolve) {
15359 if (delay > 0) {
15360 ele.delayAnimation(delay).play().promise().then(resolve);
15361 } else {
15362 resolve();
15363 }
15364 }).then(function () {
15365 return ele.animation({
15366 style: style,
15367 duration: duration,
15368 easing: ele.pstyle('transition-timing-function').value,
15369 queue: false
15370 }).play().promise();
15371 }).then(function () {
15372 // if( !isBypass ){
15373 self.removeBypasses(ele, props);
15374 ele.emitAndNotify('style'); // }
15375
15376 _p.transitioning = false;
15377 });
15378 } else if (_p.transitioning) {
15379 this.removeBypasses(ele, props);
15380 ele.emitAndNotify('style');
15381 _p.transitioning = false;
15382 }
15383};
15384
15385styfn$8.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15386 var prop = this.properties[name];
15387 var triggerCheck = getTrigger(prop);
15388
15389 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15390 onTrigger(prop);
15391 }
15392};
15393
15394styfn$8.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15395 var _this = this;
15396
15397 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15398 return prop.triggersZOrder;
15399 }, function () {
15400 _this._private.cy.notify('zorder', ele);
15401 });
15402};
15403
15404styfn$8.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15405 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15406 return prop.triggersBounds;
15407 }, function (prop) {
15408 ele.dirtyCompoundBoundsCache();
15409 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15410 // then dirty the pll edge bb cache as well
15411
15412 if ( // only for beziers -- so performance of other edges isn't affected
15413 prop.triggersBoundsOfParallelBeziers && (name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') || name === 'display' && (fromValue === 'none' || toValue === 'none'))) {
15414 ele.parallelEdges().forEach(function (pllEdge) {
15415 if (pllEdge.isBundledBezier()) {
15416 pllEdge.dirtyBoundingBoxCache();
15417 }
15418 });
15419 }
15420 });
15421};
15422
15423styfn$8.checkTriggers = function (ele, name, fromValue, toValue) {
15424 ele.dirtyStyleCache();
15425 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15426 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15427};
15428
15429var styfn$7 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15430// returns true iff application was successful for at least 1 specified property
15431
15432styfn$7.applyBypass = function (eles, name, value, updateTransitions) {
15433 var self = this;
15434 var props = [];
15435 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15436
15437 if (name === '*' || name === '**') {
15438 // apply to all property names
15439 if (value !== undefined) {
15440 for (var i = 0; i < self.properties.length; i++) {
15441 var prop = self.properties[i];
15442 var _name = prop.name;
15443 var parsedProp = this.parse(_name, value, true);
15444
15445 if (parsedProp) {
15446 props.push(parsedProp);
15447 }
15448 }
15449 }
15450 } else if (string(name)) {
15451 // then parse the single property
15452 var _parsedProp = this.parse(name, value, true);
15453
15454 if (_parsedProp) {
15455 props.push(_parsedProp);
15456 }
15457 } else if (plainObject(name)) {
15458 // then parse each property
15459 var specifiedProps = name;
15460 updateTransitions = value;
15461 var names = Object.keys(specifiedProps);
15462
15463 for (var _i = 0; _i < names.length; _i++) {
15464 var _name2 = names[_i];
15465 var _value = specifiedProps[_name2];
15466
15467 if (_value === undefined) {
15468 // try camel case name too
15469 _value = specifiedProps[dash2camel(_name2)];
15470 }
15471
15472 if (_value !== undefined) {
15473 var _parsedProp2 = this.parse(_name2, _value, true);
15474
15475 if (_parsedProp2) {
15476 props.push(_parsedProp2);
15477 }
15478 }
15479 }
15480 } else {
15481 // can't do anything without well defined properties
15482 return false;
15483 } // we've failed if there are no valid properties
15484
15485
15486 if (props.length === 0) {
15487 return false;
15488 } // now, apply the bypass properties on the elements
15489
15490
15491 var ret = false; // return true if at least one succesful bypass applied
15492
15493 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15494 // for each ele
15495 var ele = eles[_i2];
15496 var diffProps = {};
15497 var diffProp = void 0;
15498
15499 for (var j = 0; j < props.length; j++) {
15500 // for each prop
15501 var _prop = props[j];
15502
15503 if (updateTransitions) {
15504 var prevProp = ele.pstyle(_prop.name);
15505 diffProp = diffProps[_prop.name] = {
15506 prev: prevProp
15507 };
15508 }
15509
15510 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15511
15512 if (updateTransitions) {
15513 diffProp.next = ele.pstyle(_prop.name);
15514 }
15515 } // for props
15516
15517
15518 if (ret) {
15519 this.updateStyleHints(ele);
15520 }
15521
15522 if (updateTransitions) {
15523 this.updateTransitions(ele, diffProps, isBypass);
15524 }
15525 } // for eles
15526
15527
15528 return ret;
15529}; // only useful in specific cases like animation
15530
15531
15532styfn$7.overrideBypass = function (eles, name, value) {
15533 name = camel2dash(name);
15534
15535 for (var i = 0; i < eles.length; i++) {
15536 var ele = eles[i];
15537 var prop = ele._private.style[name];
15538 var type = this.properties[name].type;
15539 var isColor = type.color;
15540 var isMulti = type.mutiple;
15541 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15542
15543 if (!prop || !prop.bypass) {
15544 // need a bypass if one doesn't exist
15545 this.applyBypass(ele, name, value);
15546 } else {
15547 prop.value = value;
15548
15549 if (prop.pfValue != null) {
15550 prop.pfValue = value;
15551 }
15552
15553 if (isColor) {
15554 prop.strValue = 'rgb(' + value.join(',') + ')';
15555 } else if (isMulti) {
15556 prop.strValue = value.join(' ');
15557 } else {
15558 prop.strValue = '' + value;
15559 }
15560
15561 this.updateStyleHints(ele);
15562 }
15563
15564 this.checkTriggers(ele, name, oldValue, value);
15565 }
15566};
15567
15568styfn$7.removeAllBypasses = function (eles, updateTransitions) {
15569 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15570};
15571
15572styfn$7.removeBypasses = function (eles, props, updateTransitions) {
15573 var isBypass = true;
15574
15575 for (var j = 0; j < eles.length; j++) {
15576 var ele = eles[j];
15577 var diffProps = {};
15578
15579 for (var i = 0; i < props.length; i++) {
15580 var name = props[i];
15581 var prop = this.properties[name];
15582 var prevProp = ele.pstyle(prop.name);
15583
15584 if (!prevProp || !prevProp.bypass) {
15585 // if a bypass doesn't exist for the prop, nothing needs to be removed
15586 continue;
15587 }
15588
15589 var value = ''; // empty => remove bypass
15590
15591 var parsedProp = this.parse(name, value, true);
15592 var diffProp = diffProps[prop.name] = {
15593 prev: prevProp
15594 };
15595 this.applyParsedProperty(ele, parsedProp);
15596 diffProp.next = ele.pstyle(prop.name);
15597 } // for props
15598
15599
15600 this.updateStyleHints(ele);
15601
15602 if (updateTransitions) {
15603 this.updateTransitions(ele, diffProps, isBypass);
15604 }
15605 } // for eles
15606
15607};
15608
15609var styfn$6 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15610
15611styfn$6.getEmSizeInPixels = function () {
15612 var px = this.containerCss('font-size');
15613
15614 if (px != null) {
15615 return parseFloat(px);
15616 } else {
15617 return 1; // for headless
15618 }
15619}; // gets css property from the core container
15620
15621
15622styfn$6.containerCss = function (propName) {
15623 var cy = this._private.cy;
15624 var domElement = cy.container();
15625
15626 if (window$1 && domElement && window$1.getComputedStyle) {
15627 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15628 }
15629};
15630
15631var styfn$5 = {}; // gets the rendered style for an element
15632
15633styfn$5.getRenderedStyle = function (ele, prop) {
15634 if (prop) {
15635 return this.getStylePropertyValue(ele, prop, true);
15636 } else {
15637 return this.getRawStyle(ele, true);
15638 }
15639}; // gets the raw style for an element
15640
15641
15642styfn$5.getRawStyle = function (ele, isRenderedVal) {
15643 var self = this;
15644 ele = ele[0]; // insure it's an element
15645
15646 if (ele) {
15647 var rstyle = {};
15648
15649 for (var i = 0; i < self.properties.length; i++) {
15650 var prop = self.properties[i];
15651 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15652
15653 if (val != null) {
15654 rstyle[prop.name] = val;
15655 rstyle[dash2camel(prop.name)] = val;
15656 }
15657 }
15658
15659 return rstyle;
15660 }
15661};
15662
15663styfn$5.getIndexedStyle = function (ele, property, subproperty, index) {
15664 var pstyle = ele.pstyle(property)[subproperty][index];
15665 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15666};
15667
15668styfn$5.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15669 var self = this;
15670 ele = ele[0]; // insure it's an element
15671
15672 if (ele) {
15673 var prop = self.properties[propName];
15674
15675 if (prop.alias) {
15676 prop = prop.pointsTo;
15677 }
15678
15679 var type = prop.type;
15680 var styleProp = ele.pstyle(prop.name);
15681
15682 if (styleProp) {
15683 var value = styleProp.value,
15684 units = styleProp.units,
15685 strValue = styleProp.strValue;
15686
15687 if (isRenderedVal && type.number && value != null && number$1(value)) {
15688 var zoom = ele.cy().zoom();
15689
15690 var getRenderedValue = function getRenderedValue(val) {
15691 return val * zoom;
15692 };
15693
15694 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15695 return getRenderedValue(val) + units;
15696 };
15697
15698 var isArrayValue = array(value);
15699 var haveUnits = isArrayValue ? units.every(function (u) {
15700 return u != null;
15701 }) : units != null;
15702
15703 if (haveUnits) {
15704 if (isArrayValue) {
15705 return value.map(function (v, i) {
15706 return getValueStringWithUnits(v, units[i]);
15707 }).join(' ');
15708 } else {
15709 return getValueStringWithUnits(value, units);
15710 }
15711 } else {
15712 if (isArrayValue) {
15713 return value.map(function (v) {
15714 return string(v) ? v : '' + getRenderedValue(v);
15715 }).join(' ');
15716 } else {
15717 return '' + getRenderedValue(value);
15718 }
15719 }
15720 } else if (strValue != null) {
15721 return strValue;
15722 }
15723 }
15724
15725 return null;
15726 }
15727};
15728
15729styfn$5.getAnimationStartStyle = function (ele, aniProps) {
15730 var rstyle = {};
15731
15732 for (var i = 0; i < aniProps.length; i++) {
15733 var aniProp = aniProps[i];
15734 var name = aniProp.name;
15735 var styleProp = ele.pstyle(name);
15736
15737 if (styleProp !== undefined) {
15738 // then make a prop of it
15739 if (plainObject(styleProp)) {
15740 styleProp = this.parse(name, styleProp.strValue);
15741 } else {
15742 styleProp = this.parse(name, styleProp);
15743 }
15744 }
15745
15746 if (styleProp) {
15747 rstyle[name] = styleProp;
15748 }
15749 }
15750
15751 return rstyle;
15752};
15753
15754styfn$5.getPropsList = function (propsObj) {
15755 var self = this;
15756 var rstyle = [];
15757 var style = propsObj;
15758 var props = self.properties;
15759
15760 if (style) {
15761 var names = Object.keys(style);
15762
15763 for (var i = 0; i < names.length; i++) {
15764 var name = names[i];
15765 var val = style[name];
15766 var prop = props[name] || props[camel2dash(name)];
15767 var styleProp = this.parse(prop.name, val);
15768
15769 if (styleProp) {
15770 rstyle.push(styleProp);
15771 }
15772 }
15773 }
15774
15775 return rstyle;
15776};
15777
15778styfn$5.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15779 var hash = seed.slice();
15780 var name, val, strVal, chVal;
15781 var i, j;
15782
15783 for (i = 0; i < propNames.length; i++) {
15784 name = propNames[i];
15785 val = ele.pstyle(name, false);
15786
15787 if (val == null) {
15788 continue;
15789 } else if (val.pfValue != null) {
15790 hash[0] = hashInt(chVal, hash[0]);
15791 hash[1] = hashIntAlt(chVal, hash[1]);
15792 } else {
15793 strVal = val.strValue;
15794
15795 for (j = 0; j < strVal.length; j++) {
15796 chVal = strVal.charCodeAt(j);
15797 hash[0] = hashInt(chVal, hash[0]);
15798 hash[1] = hashIntAlt(chVal, hash[1]);
15799 }
15800 }
15801 }
15802
15803 return hash;
15804};
15805
15806styfn$5.getPropertiesHash = styfn$5.getNonDefaultPropertiesHash;
15807
15808var styfn$4 = {};
15809
15810styfn$4.appendFromJson = function (json) {
15811 var style = this;
15812
15813 for (var i = 0; i < json.length; i++) {
15814 var context = json[i];
15815 var selector = context.selector;
15816 var props = context.style || context.css;
15817 var names = Object.keys(props);
15818 style.selector(selector); // apply selector
15819
15820 for (var j = 0; j < names.length; j++) {
15821 var name = names[j];
15822 var value = props[name];
15823 style.css(name, value); // apply property
15824 }
15825 }
15826
15827 return style;
15828}; // accessible cy.style() function
15829
15830
15831styfn$4.fromJson = function (json) {
15832 var style = this;
15833 style.resetToDefault();
15834 style.appendFromJson(json);
15835 return style;
15836}; // get json from cy.style() api
15837
15838
15839styfn$4.json = function () {
15840 var json = [];
15841
15842 for (var i = this.defaultLength; i < this.length; i++) {
15843 var cxt = this[i];
15844 var selector = cxt.selector;
15845 var props = cxt.properties;
15846 var css = {};
15847
15848 for (var j = 0; j < props.length; j++) {
15849 var prop = props[j];
15850 css[prop.name] = prop.strValue;
15851 }
15852
15853 json.push({
15854 selector: !selector ? 'core' : selector.toString(),
15855 style: css
15856 });
15857 }
15858
15859 return json;
15860};
15861
15862var styfn$3 = {};
15863
15864styfn$3.appendFromString = function (string) {
15865 var self = this;
15866 var style = this;
15867 var remaining = '' + string;
15868 var selAndBlockStr;
15869 var blockRem;
15870 var propAndValStr; // remove comments from the style string
15871
15872 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15873
15874 function removeSelAndBlockFromRemaining() {
15875 // remove the parsed selector and block from the remaining text to parse
15876 if (remaining.length > selAndBlockStr.length) {
15877 remaining = remaining.substr(selAndBlockStr.length);
15878 } else {
15879 remaining = '';
15880 }
15881 }
15882
15883 function removePropAndValFromRem() {
15884 // remove the parsed property and value from the remaining block text to parse
15885 if (blockRem.length > propAndValStr.length) {
15886 blockRem = blockRem.substr(propAndValStr.length);
15887 } else {
15888 blockRem = '';
15889 }
15890 }
15891
15892 for (;;) {
15893 var nothingLeftToParse = remaining.match(/^\s*$/);
15894
15895 if (nothingLeftToParse) {
15896 break;
15897 }
15898
15899 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15900
15901 if (!selAndBlock) {
15902 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15903 break;
15904 }
15905
15906 selAndBlockStr = selAndBlock[0]; // parse the selector
15907
15908 var selectorStr = selAndBlock[1];
15909
15910 if (selectorStr !== 'core') {
15911 var selector = new Selector(selectorStr);
15912
15913 if (selector.invalid) {
15914 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15915
15916 removeSelAndBlockFromRemaining();
15917 continue;
15918 }
15919 } // parse the block of properties and values
15920
15921
15922 var blockStr = selAndBlock[2];
15923 var invalidBlock = false;
15924 blockRem = blockStr;
15925 var props = [];
15926
15927 for (;;) {
15928 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15929
15930 if (_nothingLeftToParse) {
15931 break;
15932 }
15933
15934 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)(?:\s*;|\s*$)/);
15935
15936 if (!propAndVal) {
15937 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15938 invalidBlock = true;
15939 break;
15940 }
15941
15942 propAndValStr = propAndVal[0];
15943 var propStr = propAndVal[1];
15944 var valStr = propAndVal[2];
15945 var prop = self.properties[propStr];
15946
15947 if (!prop) {
15948 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15949
15950 removePropAndValFromRem();
15951 continue;
15952 }
15953
15954 var parsedProp = style.parse(propStr, valStr);
15955
15956 if (!parsedProp) {
15957 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15958
15959 removePropAndValFromRem();
15960 continue;
15961 }
15962
15963 props.push({
15964 name: propStr,
15965 val: valStr
15966 });
15967 removePropAndValFromRem();
15968 }
15969
15970 if (invalidBlock) {
15971 removeSelAndBlockFromRemaining();
15972 break;
15973 } // put the parsed block in the style
15974
15975
15976 style.selector(selectorStr);
15977
15978 for (var i = 0; i < props.length; i++) {
15979 var _prop = props[i];
15980 style.css(_prop.name, _prop.val);
15981 }
15982
15983 removeSelAndBlockFromRemaining();
15984 }
15985
15986 return style;
15987};
15988
15989styfn$3.fromString = function (string) {
15990 var style = this;
15991 style.resetToDefault();
15992 style.appendFromString(string);
15993 return style;
15994};
15995
15996var styfn$2 = {};
15997
15998(function () {
15999 var number$1 = number;
16000 var rgba = rgbaNoBackRefs;
16001 var hsla = hslaNoBackRefs;
16002 var hex3$1 = hex3;
16003 var hex6$1 = hex6;
16004
16005 var data = function data(prefix) {
16006 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
16007 };
16008
16009 var mapData = function mapData(prefix) {
16010 var mapArg = number$1 + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
16011 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number$1 + ')\\s*\\,\\s*(' + number$1 + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
16012 };
16013
16014 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
16015
16016 styfn$2.types = {
16017 time: {
16018 number: true,
16019 min: 0,
16020 units: 's|ms',
16021 implicitUnits: 'ms'
16022 },
16023 percent: {
16024 number: true,
16025 min: 0,
16026 max: 100,
16027 units: '%',
16028 implicitUnits: '%'
16029 },
16030 percentages: {
16031 number: true,
16032 min: 0,
16033 max: 100,
16034 units: '%',
16035 implicitUnits: '%',
16036 multiple: true
16037 },
16038 zeroOneNumber: {
16039 number: true,
16040 min: 0,
16041 max: 1,
16042 unitless: true
16043 },
16044 zeroOneNumbers: {
16045 number: true,
16046 min: 0,
16047 max: 1,
16048 unitless: true,
16049 multiple: true
16050 },
16051 nOneOneNumber: {
16052 number: true,
16053 min: -1,
16054 max: 1,
16055 unitless: true
16056 },
16057 nonNegativeInt: {
16058 number: true,
16059 min: 0,
16060 integer: true,
16061 unitless: true
16062 },
16063 position: {
16064 enums: ['parent', 'origin']
16065 },
16066 nodeSize: {
16067 number: true,
16068 min: 0,
16069 enums: ['label']
16070 },
16071 number: {
16072 number: true,
16073 unitless: true
16074 },
16075 numbers: {
16076 number: true,
16077 unitless: true,
16078 multiple: true
16079 },
16080 positiveNumber: {
16081 number: true,
16082 unitless: true,
16083 min: 0,
16084 strictMin: true
16085 },
16086 size: {
16087 number: true,
16088 min: 0
16089 },
16090 bidirectionalSize: {
16091 number: true
16092 },
16093 // allows negative
16094 bidirectionalSizeMaybePercent: {
16095 number: true,
16096 allowPercent: true
16097 },
16098 // allows negative
16099 bidirectionalSizes: {
16100 number: true,
16101 multiple: true
16102 },
16103 // allows negative
16104 sizeMaybePercent: {
16105 number: true,
16106 min: 0,
16107 allowPercent: true
16108 },
16109 axisDirection: {
16110 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16111 },
16112 paddingRelativeTo: {
16113 enums: ['width', 'height', 'average', 'min', 'max']
16114 },
16115 bgWH: {
16116 number: true,
16117 min: 0,
16118 allowPercent: true,
16119 enums: ['auto'],
16120 multiple: true
16121 },
16122 bgPos: {
16123 number: true,
16124 allowPercent: true,
16125 multiple: true
16126 },
16127 bgRelativeTo: {
16128 enums: ['inner', 'include-padding'],
16129 multiple: true
16130 },
16131 bgRepeat: {
16132 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16133 multiple: true
16134 },
16135 bgFit: {
16136 enums: ['none', 'contain', 'cover'],
16137 multiple: true
16138 },
16139 bgCrossOrigin: {
16140 enums: ['anonymous', 'use-credentials'],
16141 multiple: true
16142 },
16143 bgClip: {
16144 enums: ['none', 'node'],
16145 multiple: true
16146 },
16147 bgContainment: {
16148 enums: ['inside', 'over'],
16149 multiple: true
16150 },
16151 color: {
16152 color: true
16153 },
16154 colors: {
16155 color: true,
16156 multiple: true
16157 },
16158 fill: {
16159 enums: ['solid', 'linear-gradient', 'radial-gradient']
16160 },
16161 bool: {
16162 enums: ['yes', 'no']
16163 },
16164 bools: {
16165 enums: ['yes', 'no'],
16166 multiple: true
16167 },
16168 lineStyle: {
16169 enums: ['solid', 'dotted', 'dashed']
16170 },
16171 lineCap: {
16172 enums: ['butt', 'round', 'square']
16173 },
16174 borderStyle: {
16175 enums: ['solid', 'dotted', 'dashed', 'double']
16176 },
16177 curveStyle: {
16178 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'straight-triangle', 'taxi']
16179 },
16180 fontFamily: {
16181 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16182 },
16183 fontStyle: {
16184 enums: ['italic', 'normal', 'oblique']
16185 },
16186 fontWeight: {
16187 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16188 },
16189 textDecoration: {
16190 enums: ['none', 'underline', 'overline', 'line-through']
16191 },
16192 textTransform: {
16193 enums: ['none', 'uppercase', 'lowercase']
16194 },
16195 textWrap: {
16196 enums: ['none', 'wrap', 'ellipsis']
16197 },
16198 textOverflowWrap: {
16199 enums: ['whitespace', 'anywhere']
16200 },
16201 textBackgroundShape: {
16202 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16203 },
16204 nodeShape: {
16205 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']
16206 },
16207 overlayShape: {
16208 enums: ['roundrectangle', 'round-rectangle', 'ellipse']
16209 },
16210 compoundIncludeLabels: {
16211 enums: ['include', 'exclude']
16212 },
16213 arrowShape: {
16214 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16215 },
16216 arrowFill: {
16217 enums: ['filled', 'hollow']
16218 },
16219 display: {
16220 enums: ['element', 'none']
16221 },
16222 visibility: {
16223 enums: ['hidden', 'visible']
16224 },
16225 zCompoundDepth: {
16226 enums: ['bottom', 'orphan', 'auto', 'top']
16227 },
16228 zIndexCompare: {
16229 enums: ['auto', 'manual']
16230 },
16231 valign: {
16232 enums: ['top', 'center', 'bottom']
16233 },
16234 halign: {
16235 enums: ['left', 'center', 'right']
16236 },
16237 justification: {
16238 enums: ['left', 'center', 'right', 'auto']
16239 },
16240 text: {
16241 string: true
16242 },
16243 data: {
16244 mapping: true,
16245 regex: data('data')
16246 },
16247 layoutData: {
16248 mapping: true,
16249 regex: data('layoutData')
16250 },
16251 scratch: {
16252 mapping: true,
16253 regex: data('scratch')
16254 },
16255 mapData: {
16256 mapping: true,
16257 regex: mapData('mapData')
16258 },
16259 mapLayoutData: {
16260 mapping: true,
16261 regex: mapData('mapLayoutData')
16262 },
16263 mapScratch: {
16264 mapping: true,
16265 regex: mapData('mapScratch')
16266 },
16267 fn: {
16268 mapping: true,
16269 fn: true
16270 },
16271 url: {
16272 regexes: urlRegexes,
16273 singleRegexMatchValue: true
16274 },
16275 urls: {
16276 regexes: urlRegexes,
16277 singleRegexMatchValue: true,
16278 multiple: true
16279 },
16280 propList: {
16281 propList: true
16282 },
16283 angle: {
16284 number: true,
16285 units: 'deg|rad',
16286 implicitUnits: 'rad'
16287 },
16288 textRotation: {
16289 number: true,
16290 units: 'deg|rad',
16291 implicitUnits: 'rad',
16292 enums: ['none', 'autorotate']
16293 },
16294 polygonPointList: {
16295 number: true,
16296 multiple: true,
16297 evenMultiple: true,
16298 min: -1,
16299 max: 1,
16300 unitless: true
16301 },
16302 edgeDistances: {
16303 enums: ['intersection', 'node-position']
16304 },
16305 edgeEndpoint: {
16306 number: true,
16307 multiple: true,
16308 units: '%|px|em|deg|rad',
16309 implicitUnits: 'px',
16310 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16311 singleEnum: true,
16312 validate: function validate(valArr, unitsArr) {
16313 switch (valArr.length) {
16314 case 2:
16315 // can be % or px only
16316 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16317
16318 case 1:
16319 // can be enum, deg, or rad only
16320 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16321
16322 default:
16323 return false;
16324 }
16325 }
16326 },
16327 easing: {
16328 regexes: ['^(spring)\\s*\\(\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*\\)$'],
16329 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']
16330 },
16331 gradientDirection: {
16332 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' // different order
16333 ]
16334 },
16335 boundsExpansion: {
16336 number: true,
16337 multiple: true,
16338 min: 0,
16339 validate: function validate(valArr) {
16340 var length = valArr.length;
16341 return length === 1 || length === 2 || length === 4;
16342 }
16343 }
16344 };
16345 var diff = {
16346 zeroNonZero: function zeroNonZero(val1, val2) {
16347 if ((val1 == null || val2 == null) && val1 !== val2) {
16348 return true; // null cases could represent any value
16349 }
16350
16351 if (val1 == 0 && val2 != 0) {
16352 return true;
16353 } else if (val1 != 0 && val2 == 0) {
16354 return true;
16355 } else {
16356 return false;
16357 }
16358 },
16359 any: function any(val1, val2) {
16360 return val1 != val2;
16361 },
16362 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16363 var empty1 = emptyString(str1);
16364 var empty2 = emptyString(str2);
16365 return empty1 && !empty2 || !empty1 && empty2;
16366 }
16367 }; // define visual style properties
16368 //
16369 // - n.b. adding a new group of props may require updates to updateStyleHints()
16370 // - adding new props to an existing group gets handled automatically
16371
16372 var t = styfn$2.types;
16373 var mainLabel = [{
16374 name: 'label',
16375 type: t.text,
16376 triggersBounds: diff.any,
16377 triggersZOrder: diff.emptyNonEmpty
16378 }, {
16379 name: 'text-rotation',
16380 type: t.textRotation,
16381 triggersBounds: diff.any
16382 }, {
16383 name: 'text-margin-x',
16384 type: t.bidirectionalSize,
16385 triggersBounds: diff.any
16386 }, {
16387 name: 'text-margin-y',
16388 type: t.bidirectionalSize,
16389 triggersBounds: diff.any
16390 }];
16391 var sourceLabel = [{
16392 name: 'source-label',
16393 type: t.text,
16394 triggersBounds: diff.any
16395 }, {
16396 name: 'source-text-rotation',
16397 type: t.textRotation,
16398 triggersBounds: diff.any
16399 }, {
16400 name: 'source-text-margin-x',
16401 type: t.bidirectionalSize,
16402 triggersBounds: diff.any
16403 }, {
16404 name: 'source-text-margin-y',
16405 type: t.bidirectionalSize,
16406 triggersBounds: diff.any
16407 }, {
16408 name: 'source-text-offset',
16409 type: t.size,
16410 triggersBounds: diff.any
16411 }];
16412 var targetLabel = [{
16413 name: 'target-label',
16414 type: t.text,
16415 triggersBounds: diff.any
16416 }, {
16417 name: 'target-text-rotation',
16418 type: t.textRotation,
16419 triggersBounds: diff.any
16420 }, {
16421 name: 'target-text-margin-x',
16422 type: t.bidirectionalSize,
16423 triggersBounds: diff.any
16424 }, {
16425 name: 'target-text-margin-y',
16426 type: t.bidirectionalSize,
16427 triggersBounds: diff.any
16428 }, {
16429 name: 'target-text-offset',
16430 type: t.size,
16431 triggersBounds: diff.any
16432 }];
16433 var labelDimensions = [{
16434 name: 'font-family',
16435 type: t.fontFamily,
16436 triggersBounds: diff.any
16437 }, {
16438 name: 'font-style',
16439 type: t.fontStyle,
16440 triggersBounds: diff.any
16441 }, {
16442 name: 'font-weight',
16443 type: t.fontWeight,
16444 triggersBounds: diff.any
16445 }, {
16446 name: 'font-size',
16447 type: t.size,
16448 triggersBounds: diff.any
16449 }, {
16450 name: 'text-transform',
16451 type: t.textTransform,
16452 triggersBounds: diff.any
16453 }, {
16454 name: 'text-wrap',
16455 type: t.textWrap,
16456 triggersBounds: diff.any
16457 }, {
16458 name: 'text-overflow-wrap',
16459 type: t.textOverflowWrap,
16460 triggersBounds: diff.any
16461 }, {
16462 name: 'text-max-width',
16463 type: t.size,
16464 triggersBounds: diff.any
16465 }, {
16466 name: 'text-outline-width',
16467 type: t.size,
16468 triggersBounds: diff.any
16469 }, {
16470 name: 'line-height',
16471 type: t.positiveNumber,
16472 triggersBounds: diff.any
16473 }];
16474 var commonLabel = [{
16475 name: 'text-valign',
16476 type: t.valign,
16477 triggersBounds: diff.any
16478 }, {
16479 name: 'text-halign',
16480 type: t.halign,
16481 triggersBounds: diff.any
16482 }, {
16483 name: 'color',
16484 type: t.color
16485 }, {
16486 name: 'text-outline-color',
16487 type: t.color
16488 }, {
16489 name: 'text-outline-opacity',
16490 type: t.zeroOneNumber
16491 }, {
16492 name: 'text-background-color',
16493 type: t.color
16494 }, {
16495 name: 'text-background-opacity',
16496 type: t.zeroOneNumber
16497 }, {
16498 name: 'text-background-padding',
16499 type: t.size,
16500 triggersBounds: diff.any
16501 }, {
16502 name: 'text-border-opacity',
16503 type: t.zeroOneNumber
16504 }, {
16505 name: 'text-border-color',
16506 type: t.color
16507 }, {
16508 name: 'text-border-width',
16509 type: t.size,
16510 triggersBounds: diff.any
16511 }, {
16512 name: 'text-border-style',
16513 type: t.borderStyle,
16514 triggersBounds: diff.any
16515 }, {
16516 name: 'text-background-shape',
16517 type: t.textBackgroundShape,
16518 triggersBounds: diff.any
16519 }, {
16520 name: 'text-justification',
16521 type: t.justification
16522 }];
16523 var behavior = [{
16524 name: 'events',
16525 type: t.bool
16526 }, {
16527 name: 'text-events',
16528 type: t.bool
16529 }];
16530 var visibility = [{
16531 name: 'display',
16532 type: t.display,
16533 triggersZOrder: diff.any,
16534 triggersBounds: diff.any,
16535 triggersBoundsOfParallelBeziers: true
16536 }, {
16537 name: 'visibility',
16538 type: t.visibility,
16539 triggersZOrder: diff.any
16540 }, {
16541 name: 'opacity',
16542 type: t.zeroOneNumber,
16543 triggersZOrder: diff.zeroNonZero
16544 }, {
16545 name: 'text-opacity',
16546 type: t.zeroOneNumber
16547 }, {
16548 name: 'min-zoomed-font-size',
16549 type: t.size
16550 }, {
16551 name: 'z-compound-depth',
16552 type: t.zCompoundDepth,
16553 triggersZOrder: diff.any
16554 }, {
16555 name: 'z-index-compare',
16556 type: t.zIndexCompare,
16557 triggersZOrder: diff.any
16558 }, {
16559 name: 'z-index',
16560 type: t.nonNegativeInt,
16561 triggersZOrder: diff.any
16562 }];
16563 var overlay = [{
16564 name: 'overlay-padding',
16565 type: t.size,
16566 triggersBounds: diff.any
16567 }, {
16568 name: 'overlay-color',
16569 type: t.color
16570 }, {
16571 name: 'overlay-opacity',
16572 type: t.zeroOneNumber,
16573 triggersBounds: diff.zeroNonZero
16574 }, {
16575 name: 'overlay-shape',
16576 type: t.overlayShape,
16577 triggersBounds: diff.any
16578 }];
16579 var underlay = [{
16580 name: 'underlay-padding',
16581 type: t.size,
16582 triggersBounds: diff.any
16583 }, {
16584 name: 'underlay-color',
16585 type: t.color
16586 }, {
16587 name: 'underlay-opacity',
16588 type: t.zeroOneNumber,
16589 triggersBounds: diff.zeroNonZero
16590 }, {
16591 name: 'underlay-shape',
16592 type: t.overlayShape,
16593 triggersBounds: diff.any
16594 }];
16595 var transition = [{
16596 name: 'transition-property',
16597 type: t.propList
16598 }, {
16599 name: 'transition-duration',
16600 type: t.time
16601 }, {
16602 name: 'transition-delay',
16603 type: t.time
16604 }, {
16605 name: 'transition-timing-function',
16606 type: t.easing
16607 }];
16608
16609 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16610 if (parsedProp.value === 'label') {
16611 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16612 } else {
16613 return parsedProp.pfValue;
16614 }
16615 };
16616
16617 var nodeBody = [{
16618 name: 'height',
16619 type: t.nodeSize,
16620 triggersBounds: diff.any,
16621 hashOverride: nodeSizeHashOverride
16622 }, {
16623 name: 'width',
16624 type: t.nodeSize,
16625 triggersBounds: diff.any,
16626 hashOverride: nodeSizeHashOverride
16627 }, {
16628 name: 'shape',
16629 type: t.nodeShape,
16630 triggersBounds: diff.any
16631 }, {
16632 name: 'shape-polygon-points',
16633 type: t.polygonPointList,
16634 triggersBounds: diff.any
16635 }, {
16636 name: 'background-color',
16637 type: t.color
16638 }, {
16639 name: 'background-fill',
16640 type: t.fill
16641 }, {
16642 name: 'background-opacity',
16643 type: t.zeroOneNumber
16644 }, {
16645 name: 'background-blacken',
16646 type: t.nOneOneNumber
16647 }, {
16648 name: 'background-gradient-stop-colors',
16649 type: t.colors
16650 }, {
16651 name: 'background-gradient-stop-positions',
16652 type: t.percentages
16653 }, {
16654 name: 'background-gradient-direction',
16655 type: t.gradientDirection
16656 }, {
16657 name: 'padding',
16658 type: t.sizeMaybePercent,
16659 triggersBounds: diff.any
16660 }, {
16661 name: 'padding-relative-to',
16662 type: t.paddingRelativeTo,
16663 triggersBounds: diff.any
16664 }, {
16665 name: 'bounds-expansion',
16666 type: t.boundsExpansion,
16667 triggersBounds: diff.any
16668 }];
16669 var nodeBorder = [{
16670 name: 'border-color',
16671 type: t.color
16672 }, {
16673 name: 'border-opacity',
16674 type: t.zeroOneNumber
16675 }, {
16676 name: 'border-width',
16677 type: t.size,
16678 triggersBounds: diff.any
16679 }, {
16680 name: 'border-style',
16681 type: t.borderStyle
16682 }];
16683 var backgroundImage = [{
16684 name: 'background-image',
16685 type: t.urls
16686 }, {
16687 name: 'background-image-crossorigin',
16688 type: t.bgCrossOrigin
16689 }, {
16690 name: 'background-image-opacity',
16691 type: t.zeroOneNumbers
16692 }, {
16693 name: 'background-image-containment',
16694 type: t.bgContainment
16695 }, {
16696 name: 'background-image-smoothing',
16697 type: t.bools
16698 }, {
16699 name: 'background-position-x',
16700 type: t.bgPos
16701 }, {
16702 name: 'background-position-y',
16703 type: t.bgPos
16704 }, {
16705 name: 'background-width-relative-to',
16706 type: t.bgRelativeTo
16707 }, {
16708 name: 'background-height-relative-to',
16709 type: t.bgRelativeTo
16710 }, {
16711 name: 'background-repeat',
16712 type: t.bgRepeat
16713 }, {
16714 name: 'background-fit',
16715 type: t.bgFit
16716 }, {
16717 name: 'background-clip',
16718 type: t.bgClip
16719 }, {
16720 name: 'background-width',
16721 type: t.bgWH
16722 }, {
16723 name: 'background-height',
16724 type: t.bgWH
16725 }, {
16726 name: 'background-offset-x',
16727 type: t.bgPos
16728 }, {
16729 name: 'background-offset-y',
16730 type: t.bgPos
16731 }];
16732 var compound = [{
16733 name: 'position',
16734 type: t.position,
16735 triggersBounds: diff.any
16736 }, {
16737 name: 'compound-sizing-wrt-labels',
16738 type: t.compoundIncludeLabels,
16739 triggersBounds: diff.any
16740 }, {
16741 name: 'min-width',
16742 type: t.size,
16743 triggersBounds: diff.any
16744 }, {
16745 name: 'min-width-bias-left',
16746 type: t.sizeMaybePercent,
16747 triggersBounds: diff.any
16748 }, {
16749 name: 'min-width-bias-right',
16750 type: t.sizeMaybePercent,
16751 triggersBounds: diff.any
16752 }, {
16753 name: 'min-height',
16754 type: t.size,
16755 triggersBounds: diff.any
16756 }, {
16757 name: 'min-height-bias-top',
16758 type: t.sizeMaybePercent,
16759 triggersBounds: diff.any
16760 }, {
16761 name: 'min-height-bias-bottom',
16762 type: t.sizeMaybePercent,
16763 triggersBounds: diff.any
16764 }];
16765 var edgeLine = [{
16766 name: 'line-style',
16767 type: t.lineStyle
16768 }, {
16769 name: 'line-color',
16770 type: t.color
16771 }, {
16772 name: 'line-fill',
16773 type: t.fill
16774 }, {
16775 name: 'line-cap',
16776 type: t.lineCap
16777 }, {
16778 name: 'line-opacity',
16779 type: t.zeroOneNumber
16780 }, {
16781 name: 'line-dash-pattern',
16782 type: t.numbers
16783 }, {
16784 name: 'line-dash-offset',
16785 type: t.number
16786 }, {
16787 name: 'line-gradient-stop-colors',
16788 type: t.colors
16789 }, {
16790 name: 'line-gradient-stop-positions',
16791 type: t.percentages
16792 }, {
16793 name: 'curve-style',
16794 type: t.curveStyle,
16795 triggersBounds: diff.any,
16796 triggersBoundsOfParallelBeziers: true
16797 }, {
16798 name: 'haystack-radius',
16799 type: t.zeroOneNumber,
16800 triggersBounds: diff.any
16801 }, {
16802 name: 'source-endpoint',
16803 type: t.edgeEndpoint,
16804 triggersBounds: diff.any
16805 }, {
16806 name: 'target-endpoint',
16807 type: t.edgeEndpoint,
16808 triggersBounds: diff.any
16809 }, {
16810 name: 'control-point-step-size',
16811 type: t.size,
16812 triggersBounds: diff.any
16813 }, {
16814 name: 'control-point-distances',
16815 type: t.bidirectionalSizes,
16816 triggersBounds: diff.any
16817 }, {
16818 name: 'control-point-weights',
16819 type: t.numbers,
16820 triggersBounds: diff.any
16821 }, {
16822 name: 'segment-distances',
16823 type: t.bidirectionalSizes,
16824 triggersBounds: diff.any
16825 }, {
16826 name: 'segment-weights',
16827 type: t.numbers,
16828 triggersBounds: diff.any
16829 }, {
16830 name: 'taxi-turn',
16831 type: t.bidirectionalSizeMaybePercent,
16832 triggersBounds: diff.any
16833 }, {
16834 name: 'taxi-turn-min-distance',
16835 type: t.size,
16836 triggersBounds: diff.any
16837 }, {
16838 name: 'taxi-direction',
16839 type: t.axisDirection,
16840 triggersBounds: diff.any
16841 }, {
16842 name: 'edge-distances',
16843 type: t.edgeDistances,
16844 triggersBounds: diff.any
16845 }, {
16846 name: 'arrow-scale',
16847 type: t.positiveNumber,
16848 triggersBounds: diff.any
16849 }, {
16850 name: 'loop-direction',
16851 type: t.angle,
16852 triggersBounds: diff.any
16853 }, {
16854 name: 'loop-sweep',
16855 type: t.angle,
16856 triggersBounds: diff.any
16857 }, {
16858 name: 'source-distance-from-node',
16859 type: t.size,
16860 triggersBounds: diff.any
16861 }, {
16862 name: 'target-distance-from-node',
16863 type: t.size,
16864 triggersBounds: diff.any
16865 }];
16866 var ghost = [{
16867 name: 'ghost',
16868 type: t.bool,
16869 triggersBounds: diff.any
16870 }, {
16871 name: 'ghost-offset-x',
16872 type: t.bidirectionalSize,
16873 triggersBounds: diff.any
16874 }, {
16875 name: 'ghost-offset-y',
16876 type: t.bidirectionalSize,
16877 triggersBounds: diff.any
16878 }, {
16879 name: 'ghost-opacity',
16880 type: t.zeroOneNumber
16881 }];
16882 var core = [{
16883 name: 'selection-box-color',
16884 type: t.color
16885 }, {
16886 name: 'selection-box-opacity',
16887 type: t.zeroOneNumber
16888 }, {
16889 name: 'selection-box-border-color',
16890 type: t.color
16891 }, {
16892 name: 'selection-box-border-width',
16893 type: t.size
16894 }, {
16895 name: 'active-bg-color',
16896 type: t.color
16897 }, {
16898 name: 'active-bg-opacity',
16899 type: t.zeroOneNumber
16900 }, {
16901 name: 'active-bg-size',
16902 type: t.size
16903 }, {
16904 name: 'outside-texture-bg-color',
16905 type: t.color
16906 }, {
16907 name: 'outside-texture-bg-opacity',
16908 type: t.zeroOneNumber
16909 }]; // pie backgrounds for nodes
16910
16911 var pie = [];
16912 styfn$2.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16913
16914 pie.push({
16915 name: 'pie-size',
16916 type: t.sizeMaybePercent
16917 });
16918
16919 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
16920 pie.push({
16921 name: 'pie-' + i + '-background-color',
16922 type: t.color
16923 });
16924 pie.push({
16925 name: 'pie-' + i + '-background-size',
16926 type: t.percent
16927 });
16928 pie.push({
16929 name: 'pie-' + i + '-background-opacity',
16930 type: t.zeroOneNumber
16931 });
16932 } // edge arrows
16933
16934
16935 var edgeArrow = [];
16936 var arrowPrefixes = styfn$2.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16937 [{
16938 name: 'arrow-shape',
16939 type: t.arrowShape,
16940 triggersBounds: diff.any
16941 }, {
16942 name: 'arrow-color',
16943 type: t.color
16944 }, {
16945 name: 'arrow-fill',
16946 type: t.arrowFill
16947 }].forEach(function (prop) {
16948 arrowPrefixes.forEach(function (prefix) {
16949 var name = prefix + '-' + prop.name;
16950 var type = prop.type,
16951 triggersBounds = prop.triggersBounds;
16952 edgeArrow.push({
16953 name: name,
16954 type: type,
16955 triggersBounds: triggersBounds
16956 });
16957 });
16958 }, {});
16959 var props = styfn$2.properties = [].concat(behavior, transition, visibility, overlay, underlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16960 var propGroups = styfn$2.propertyGroups = {
16961 // common to all eles
16962 behavior: behavior,
16963 transition: transition,
16964 visibility: visibility,
16965 overlay: overlay,
16966 underlay: underlay,
16967 ghost: ghost,
16968 // labels
16969 commonLabel: commonLabel,
16970 labelDimensions: labelDimensions,
16971 mainLabel: mainLabel,
16972 sourceLabel: sourceLabel,
16973 targetLabel: targetLabel,
16974 // node props
16975 nodeBody: nodeBody,
16976 nodeBorder: nodeBorder,
16977 backgroundImage: backgroundImage,
16978 pie: pie,
16979 compound: compound,
16980 // edge props
16981 edgeLine: edgeLine,
16982 edgeArrow: edgeArrow,
16983 core: core
16984 };
16985 var propGroupNames = styfn$2.propertyGroupNames = {};
16986 var propGroupKeys = styfn$2.propertyGroupKeys = Object.keys(propGroups);
16987 propGroupKeys.forEach(function (key) {
16988 propGroupNames[key] = propGroups[key].map(function (prop) {
16989 return prop.name;
16990 });
16991 propGroups[key].forEach(function (prop) {
16992 return prop.groupKey = key;
16993 });
16994 }); // define aliases
16995
16996 var aliases = styfn$2.aliases = [{
16997 name: 'content',
16998 pointsTo: 'label'
16999 }, {
17000 name: 'control-point-distance',
17001 pointsTo: 'control-point-distances'
17002 }, {
17003 name: 'control-point-weight',
17004 pointsTo: 'control-point-weights'
17005 }, {
17006 name: 'edge-text-rotation',
17007 pointsTo: 'text-rotation'
17008 }, {
17009 name: 'padding-left',
17010 pointsTo: 'padding'
17011 }, {
17012 name: 'padding-right',
17013 pointsTo: 'padding'
17014 }, {
17015 name: 'padding-top',
17016 pointsTo: 'padding'
17017 }, {
17018 name: 'padding-bottom',
17019 pointsTo: 'padding'
17020 }]; // list of property names
17021
17022 styfn$2.propertyNames = props.map(function (p) {
17023 return p.name;
17024 }); // allow access of properties by name ( e.g. style.properties.height )
17025
17026 for (var _i = 0; _i < props.length; _i++) {
17027 var prop = props[_i];
17028 props[prop.name] = prop; // allow lookup by name
17029 } // map aliases
17030
17031
17032 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17033 var alias = aliases[_i2];
17034 var pointsToProp = props[alias.pointsTo];
17035 var aliasProp = {
17036 name: alias.name,
17037 alias: true,
17038 pointsTo: pointsToProp
17039 }; // add alias prop for parsing
17040
17041 props.push(aliasProp);
17042 props[alias.name] = aliasProp; // allow lookup by name
17043 }
17044})();
17045
17046styfn$2.getDefaultProperty = function (name) {
17047 return this.getDefaultProperties()[name];
17048};
17049
17050styfn$2.getDefaultProperties = function () {
17051 var _p = this._private;
17052
17053 if (_p.defaultProperties != null) {
17054 return _p.defaultProperties;
17055 }
17056
17057 var rawProps = extend({
17058 // core props
17059 'selection-box-color': '#ddd',
17060 'selection-box-opacity': 0.65,
17061 'selection-box-border-color': '#aaa',
17062 'selection-box-border-width': 1,
17063 'active-bg-color': 'black',
17064 'active-bg-opacity': 0.15,
17065 'active-bg-size': 30,
17066 'outside-texture-bg-color': '#000',
17067 'outside-texture-bg-opacity': 0.125,
17068 // common node/edge props
17069 'events': 'yes',
17070 'text-events': 'no',
17071 'text-valign': 'top',
17072 'text-halign': 'center',
17073 'text-justification': 'auto',
17074 'line-height': 1,
17075 'color': '#000',
17076 'text-outline-color': '#000',
17077 'text-outline-width': 0,
17078 'text-outline-opacity': 1,
17079 'text-opacity': 1,
17080 'text-decoration': 'none',
17081 'text-transform': 'none',
17082 'text-wrap': 'none',
17083 'text-overflow-wrap': 'whitespace',
17084 'text-max-width': 9999,
17085 'text-background-color': '#000',
17086 'text-background-opacity': 0,
17087 'text-background-shape': 'rectangle',
17088 'text-background-padding': 0,
17089 'text-border-opacity': 0,
17090 'text-border-width': 0,
17091 'text-border-style': 'solid',
17092 'text-border-color': '#000',
17093 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17094 'font-style': 'normal',
17095 'font-weight': 'normal',
17096 'font-size': 16,
17097 'min-zoomed-font-size': 0,
17098 'text-rotation': 'none',
17099 'source-text-rotation': 'none',
17100 'target-text-rotation': 'none',
17101 'visibility': 'visible',
17102 'display': 'element',
17103 'opacity': 1,
17104 'z-compound-depth': 'auto',
17105 'z-index-compare': 'auto',
17106 'z-index': 0,
17107 'label': '',
17108 'text-margin-x': 0,
17109 'text-margin-y': 0,
17110 'source-label': '',
17111 'source-text-offset': 0,
17112 'source-text-margin-x': 0,
17113 'source-text-margin-y': 0,
17114 'target-label': '',
17115 'target-text-offset': 0,
17116 'target-text-margin-x': 0,
17117 'target-text-margin-y': 0,
17118 'overlay-opacity': 0,
17119 'overlay-color': '#000',
17120 'overlay-padding': 10,
17121 'overlay-shape': 'round-rectangle',
17122 'underlay-opacity': 0,
17123 'underlay-color': '#000',
17124 'underlay-padding': 10,
17125 'underlay-shape': 'round-rectangle',
17126 'transition-property': 'none',
17127 'transition-duration': 0,
17128 'transition-delay': 0,
17129 'transition-timing-function': 'linear',
17130 // node props
17131 'background-blacken': 0,
17132 'background-color': '#999',
17133 'background-fill': 'solid',
17134 'background-opacity': 1,
17135 'background-image': 'none',
17136 'background-image-crossorigin': 'anonymous',
17137 'background-image-opacity': 1,
17138 'background-image-containment': 'inside',
17139 'background-image-smoothing': 'yes',
17140 'background-position-x': '50%',
17141 'background-position-y': '50%',
17142 'background-offset-x': 0,
17143 'background-offset-y': 0,
17144 'background-width-relative-to': 'include-padding',
17145 'background-height-relative-to': 'include-padding',
17146 'background-repeat': 'no-repeat',
17147 'background-fit': 'none',
17148 'background-clip': 'node',
17149 'background-width': 'auto',
17150 'background-height': 'auto',
17151 'border-color': '#000',
17152 'border-opacity': 1,
17153 'border-width': 0,
17154 'border-style': 'solid',
17155 'height': 30,
17156 'width': 30,
17157 'shape': 'ellipse',
17158 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17159 'bounds-expansion': 0,
17160 // node gradient
17161 'background-gradient-direction': 'to-bottom',
17162 'background-gradient-stop-colors': '#999',
17163 'background-gradient-stop-positions': '0%',
17164 // ghost props
17165 'ghost': 'no',
17166 'ghost-offset-y': 0,
17167 'ghost-offset-x': 0,
17168 'ghost-opacity': 0,
17169 // compound props
17170 'padding': 0,
17171 'padding-relative-to': 'width',
17172 'position': 'origin',
17173 'compound-sizing-wrt-labels': 'include',
17174 'min-width': 0,
17175 'min-width-bias-left': 0,
17176 'min-width-bias-right': 0,
17177 'min-height': 0,
17178 'min-height-bias-top': 0,
17179 'min-height-bias-bottom': 0
17180 }, {
17181 // node pie bg
17182 'pie-size': '100%'
17183 }, [{
17184 name: 'pie-{{i}}-background-color',
17185 value: 'black'
17186 }, {
17187 name: 'pie-{{i}}-background-size',
17188 value: '0%'
17189 }, {
17190 name: 'pie-{{i}}-background-opacity',
17191 value: 1
17192 }].reduce(function (css, prop) {
17193 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
17194 var name = prop.name.replace('{{i}}', i);
17195 var val = prop.value;
17196 css[name] = val;
17197 }
17198
17199 return css;
17200 }, {}), {
17201 // edge props
17202 'line-style': 'solid',
17203 'line-color': '#999',
17204 'line-fill': 'solid',
17205 'line-cap': 'butt',
17206 'line-opacity': 1,
17207 'line-gradient-stop-colors': '#999',
17208 'line-gradient-stop-positions': '0%',
17209 'control-point-step-size': 40,
17210 'control-point-weights': 0.5,
17211 'segment-weights': 0.5,
17212 'segment-distances': 20,
17213 'taxi-turn': '50%',
17214 'taxi-turn-min-distance': 10,
17215 'taxi-direction': 'auto',
17216 'edge-distances': 'intersection',
17217 'curve-style': 'haystack',
17218 'haystack-radius': 0,
17219 'arrow-scale': 1,
17220 'loop-direction': '-45deg',
17221 'loop-sweep': '-90deg',
17222 'source-distance-from-node': 0,
17223 'target-distance-from-node': 0,
17224 'source-endpoint': 'outside-to-node',
17225 'target-endpoint': 'outside-to-node',
17226 'line-dash-pattern': [6, 3],
17227 'line-dash-offset': 0
17228 }, [{
17229 name: 'arrow-shape',
17230 value: 'none'
17231 }, {
17232 name: 'arrow-color',
17233 value: '#999'
17234 }, {
17235 name: 'arrow-fill',
17236 value: 'filled'
17237 }].reduce(function (css, prop) {
17238 styfn$2.arrowPrefixes.forEach(function (prefix) {
17239 var name = prefix + '-' + prop.name;
17240 var val = prop.value;
17241 css[name] = val;
17242 });
17243 return css;
17244 }, {}));
17245 var parsedProps = {};
17246
17247 for (var i = 0; i < this.properties.length; i++) {
17248 var prop = this.properties[i];
17249
17250 if (prop.pointsTo) {
17251 continue;
17252 }
17253
17254 var name = prop.name;
17255 var val = rawProps[name];
17256 var parsedProp = this.parse(name, val);
17257 parsedProps[name] = parsedProp;
17258 }
17259
17260 _p.defaultProperties = parsedProps;
17261 return _p.defaultProperties;
17262};
17263
17264styfn$2.addDefaultStylesheet = function () {
17265 this.selector(':parent').css({
17266 'shape': 'rectangle',
17267 'padding': 10,
17268 'background-color': '#eee',
17269 'border-color': '#ccc',
17270 'border-width': 1
17271 }).selector('edge').css({
17272 'width': 3
17273 }).selector(':loop').css({
17274 'curve-style': 'bezier'
17275 }).selector('edge:compound').css({
17276 'curve-style': 'bezier',
17277 'source-endpoint': 'outside-to-line',
17278 'target-endpoint': 'outside-to-line'
17279 }).selector(':selected').css({
17280 'background-color': '#0169D9',
17281 'line-color': '#0169D9',
17282 'source-arrow-color': '#0169D9',
17283 'target-arrow-color': '#0169D9',
17284 'mid-source-arrow-color': '#0169D9',
17285 'mid-target-arrow-color': '#0169D9'
17286 }).selector(':parent:selected').css({
17287 'background-color': '#CCE1F9',
17288 'border-color': '#aec8e5'
17289 }).selector(':active').css({
17290 'overlay-color': 'black',
17291 'overlay-padding': 10,
17292 'overlay-opacity': 0.25
17293 });
17294 this.defaultLength = this.length;
17295};
17296
17297var styfn$1 = {}; // a caching layer for property parsing
17298
17299styfn$1.parse = function (name, value, propIsBypass, propIsFlat) {
17300 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17301
17302 if (fn$6(value)) {
17303 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17304 }
17305
17306 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17307 var bypassKey = propIsBypass ? 't' : 'f';
17308 var valueKey = '' + value;
17309 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17310 var propCache = self.propCache = self.propCache || [];
17311 var ret;
17312
17313 if (!(ret = propCache[argHash])) {
17314 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17315 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17316 // - mappings can't be shared b/c mappings are per-element
17317
17318
17319 if (propIsBypass || propIsFlat === 'mapping') {
17320 // need a copy since props are mutated later in their lifecycles
17321 ret = copy(ret);
17322
17323 if (ret) {
17324 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17325 }
17326 }
17327
17328 return ret;
17329};
17330
17331styfn$1.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17332 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17333
17334 if (!prop && value != null) {
17335 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17336 }
17337
17338 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17339 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17340 }
17341
17342 return prop;
17343}; // parse a property; return null on invalid; return parsed property otherwise
17344// fields :
17345// - name : the name of the property
17346// - value : the parsed, native-typed value of the property
17347// - strValue : a string value that represents the property value in valid css
17348// - bypass : true iff the property is a bypass property
17349
17350
17351styfn$1.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17352 var self = this;
17353 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17354
17355 var property = self.properties[name];
17356 var passedValue = value;
17357 var types = self.types;
17358
17359 if (!property) {
17360 return null;
17361 } // return null on property of unknown name
17362
17363
17364 if (value === undefined) {
17365 return null;
17366 } // can't assign undefined
17367 // the property may be an alias
17368
17369
17370 if (property.alias) {
17371 property = property.pointsTo;
17372 name = property.name;
17373 }
17374
17375 var valueIsString = string(value);
17376
17377 if (valueIsString) {
17378 // trim the value to make parsing easier
17379 value = value.trim();
17380 }
17381
17382 var type = property.type;
17383
17384 if (!type) {
17385 return null;
17386 } // no type, no luck
17387 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17388
17389
17390 if (propIsBypass && (value === '' || value === null)) {
17391 return {
17392 name: name,
17393 value: value,
17394 bypass: true,
17395 deleteBypass: true
17396 };
17397 } // check if value is a function used as a mapper
17398
17399
17400 if (fn$6(value)) {
17401 return {
17402 name: name,
17403 value: value,
17404 strValue: 'fn',
17405 mapped: types.fn,
17406 bypass: propIsBypass
17407 };
17408 } // check if value is mapped
17409
17410
17411 var data, mapData;
17412
17413 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))) {
17414 if (propIsBypass) {
17415 return false;
17416 } // mappers not allowed in bypass
17417
17418
17419 var mapped = types.data;
17420 return {
17421 name: name,
17422 value: data,
17423 strValue: '' + value,
17424 mapped: mapped,
17425 field: data[1],
17426 bypass: propIsBypass
17427 };
17428 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17429 if (propIsBypass) {
17430 return false;
17431 } // mappers not allowed in bypass
17432
17433
17434 if (type.multiple) {
17435 return false;
17436 } // impossible to map to num
17437
17438
17439 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17440
17441 if (!(type.color || type.number)) {
17442 return false;
17443 }
17444
17445 var valueMin = this.parse(name, mapData[4]); // parse to validate
17446
17447 if (!valueMin || valueMin.mapped) {
17448 return false;
17449 } // can't be invalid or mapped
17450
17451
17452 var valueMax = this.parse(name, mapData[5]); // parse to validate
17453
17454 if (!valueMax || valueMax.mapped) {
17455 return false;
17456 } // can't be invalid or mapped
17457 // check if valueMin and valueMax are the same
17458
17459
17460 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17461 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17462 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17463 } else if (type.color) {
17464 var c1 = valueMin.value;
17465 var c2 = valueMax.value;
17466 var same = c1[0] === c2[0] // red
17467 && c1[1] === c2[1] // green
17468 && c1[2] === c2[2] // blue
17469 && ( // optional alpha
17470 c1[3] === c2[3] // same alpha outright
17471 || (c1[3] == null || c1[3] === 1 // full opacity for colour 1?
17472 ) && (c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17473 );
17474
17475 if (same) {
17476 return false;
17477 } // can't make a mapper without a range
17478
17479 }
17480
17481 return {
17482 name: name,
17483 value: mapData,
17484 strValue: '' + value,
17485 mapped: _mapped,
17486 field: mapData[1],
17487 fieldMin: parseFloat(mapData[2]),
17488 // min & max are numeric
17489 fieldMax: parseFloat(mapData[3]),
17490 valueMin: valueMin.value,
17491 valueMax: valueMax.value,
17492 bypass: propIsBypass
17493 };
17494 }
17495
17496 if (type.multiple && propIsFlat !== 'multiple') {
17497 var vals;
17498
17499 if (valueIsString) {
17500 vals = value.split(/\s+/);
17501 } else if (array(value)) {
17502 vals = value;
17503 } else {
17504 vals = [value];
17505 }
17506
17507 if (type.evenMultiple && vals.length % 2 !== 0) {
17508 return null;
17509 }
17510
17511 var valArr = [];
17512 var unitsArr = [];
17513 var pfValArr = [];
17514 var strVal = '';
17515 var hasEnum = false;
17516
17517 for (var i = 0; i < vals.length; i++) {
17518 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17519 hasEnum = hasEnum || string(p.value);
17520 valArr.push(p.value);
17521 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17522 unitsArr.push(p.units);
17523 strVal += (i > 0 ? ' ' : '') + p.strValue;
17524 }
17525
17526 if (type.validate && !type.validate(valArr, unitsArr)) {
17527 return null;
17528 }
17529
17530 if (type.singleEnum && hasEnum) {
17531 if (valArr.length === 1 && string(valArr[0])) {
17532 return {
17533 name: name,
17534 value: valArr[0],
17535 strValue: valArr[0],
17536 bypass: propIsBypass
17537 };
17538 } else {
17539 return null;
17540 }
17541 }
17542
17543 return {
17544 name: name,
17545 value: valArr,
17546 pfValue: pfValArr,
17547 strValue: strVal,
17548 bypass: propIsBypass,
17549 units: unitsArr
17550 };
17551 } // several types also allow enums
17552
17553
17554 var checkEnums = function checkEnums() {
17555 for (var _i = 0; _i < type.enums.length; _i++) {
17556 var en = type.enums[_i];
17557
17558 if (en === value) {
17559 return {
17560 name: name,
17561 value: value,
17562 strValue: '' + value,
17563 bypass: propIsBypass
17564 };
17565 }
17566 }
17567
17568 return null;
17569 }; // check the type and return the appropriate object
17570
17571
17572 if (type.number) {
17573 var units;
17574 var implicitUnits = 'px'; // not set => px
17575
17576 if (type.units) {
17577 // use specified units if set
17578 units = type.units;
17579 }
17580
17581 if (type.implicitUnits) {
17582 implicitUnits = type.implicitUnits;
17583 }
17584
17585 if (!type.unitless) {
17586 if (valueIsString) {
17587 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17588
17589 if (units) {
17590 unitsRegex = units;
17591 } // only allow explicit units if so set
17592
17593
17594 var match = value.match('^(' + number + ')(' + unitsRegex + ')?' + '$');
17595
17596 if (match) {
17597 value = match[1];
17598 units = match[2] || implicitUnits;
17599 }
17600 } else if (!units || type.implicitUnits) {
17601 units = implicitUnits; // implicitly px if unspecified
17602 }
17603 }
17604
17605 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17606
17607 if (isNaN(value) && type.enums === undefined) {
17608 return null;
17609 } // check if this number type also accepts special keywords in place of numbers
17610 // (i.e. `left`, `auto`, etc)
17611
17612
17613 if (isNaN(value) && type.enums !== undefined) {
17614 value = passedValue;
17615 return checkEnums();
17616 } // check if value must be an integer
17617
17618
17619 if (type.integer && !integer(value)) {
17620 return null;
17621 } // check value is within range
17622
17623
17624 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17625 return null;
17626 }
17627
17628 var ret = {
17629 name: name,
17630 value: value,
17631 strValue: '' + value + (units ? units : ''),
17632 units: units,
17633 bypass: propIsBypass
17634 }; // normalise value in pixels
17635
17636 if (type.unitless || units !== 'px' && units !== 'em') {
17637 ret.pfValue = value;
17638 } else {
17639 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17640 } // normalise value in ms
17641
17642
17643 if (units === 'ms' || units === 's') {
17644 ret.pfValue = units === 'ms' ? value : 1000 * value;
17645 } // normalise value in rad
17646
17647
17648 if (units === 'deg' || units === 'rad') {
17649 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17650 } // normalize value in %
17651
17652
17653 if (units === '%') {
17654 ret.pfValue = value / 100;
17655 }
17656
17657 return ret;
17658 } else if (type.propList) {
17659 var props = [];
17660 var propsStr = '' + value;
17661
17662 if (propsStr === 'none') ; else {
17663 // go over each prop
17664 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17665
17666 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17667 var propName = propsSplit[_i2].trim();
17668
17669 if (self.properties[propName]) {
17670 props.push(propName);
17671 } else {
17672 warn('`' + propName + '` is not a valid property name');
17673 }
17674 }
17675
17676 if (props.length === 0) {
17677 return null;
17678 }
17679 }
17680
17681 return {
17682 name: name,
17683 value: props,
17684 strValue: props.length === 0 ? 'none' : props.join(' '),
17685 bypass: propIsBypass
17686 };
17687 } else if (type.color) {
17688 var tuple = color2tuple(value);
17689
17690 if (!tuple) {
17691 return null;
17692 }
17693
17694 return {
17695 name: name,
17696 value: tuple,
17697 pfValue: tuple,
17698 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17699 // n.b. no spaces b/c of multiple support
17700 bypass: propIsBypass
17701 };
17702 } else if (type.regex || type.regexes) {
17703 // first check enums
17704 if (type.enums) {
17705 var enumProp = checkEnums();
17706
17707 if (enumProp) {
17708 return enumProp;
17709 }
17710 }
17711
17712 var regexes = type.regexes ? type.regexes : [type.regex];
17713
17714 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17715 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17716
17717 var m = regex.exec(value);
17718
17719 if (m) {
17720 // regex matches
17721 return {
17722 name: name,
17723 value: type.singleRegexMatchValue ? m[1] : m,
17724 strValue: '' + value,
17725 bypass: propIsBypass
17726 };
17727 }
17728 }
17729
17730 return null; // didn't match any
17731 } else if (type.string) {
17732 // just return
17733 return {
17734 name: name,
17735 value: '' + value,
17736 strValue: '' + value,
17737 bypass: propIsBypass
17738 };
17739 } else if (type.enums) {
17740 // check enums last because it's a combo type in others
17741 return checkEnums();
17742 } else {
17743 return null; // not a type we can handle
17744 }
17745};
17746
17747var Style = function Style(cy) {
17748 if (!(this instanceof Style)) {
17749 return new Style(cy);
17750 }
17751
17752 if (!core(cy)) {
17753 error('A style must have a core reference');
17754 return;
17755 }
17756
17757 this._private = {
17758 cy: cy,
17759 coreStyle: {}
17760 };
17761 this.length = 0;
17762 this.resetToDefault();
17763};
17764
17765var styfn = Style.prototype;
17766
17767styfn.instanceString = function () {
17768 return 'style';
17769}; // remove all contexts
17770
17771
17772styfn.clear = function () {
17773 var _p = this._private;
17774 var cy = _p.cy;
17775 var eles = cy.elements();
17776
17777 for (var i = 0; i < this.length; i++) {
17778 this[i] = undefined;
17779 }
17780
17781 this.length = 0;
17782 _p.contextStyles = {};
17783 _p.propDiffs = {};
17784 this.cleanElements(eles, true);
17785 eles.forEach(function (ele) {
17786 var ele_p = ele[0]._private;
17787 ele_p.styleDirty = true;
17788 ele_p.appliedInitStyle = false;
17789 });
17790 return this; // chaining
17791};
17792
17793styfn.resetToDefault = function () {
17794 this.clear();
17795 this.addDefaultStylesheet();
17796 return this;
17797}; // builds a style object for the 'core' selector
17798
17799
17800styfn.core = function (propName) {
17801 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17802}; // create a new context from the specified selector string and switch to that context
17803
17804
17805styfn.selector = function (selectorStr) {
17806 // 'core' is a special case and does not need a selector
17807 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17808 var i = this.length++; // new context means new index
17809
17810 this[i] = {
17811 selector: selector,
17812 properties: [],
17813 mappedProperties: [],
17814 index: i
17815 };
17816 return this; // chaining
17817}; // add one or many css rules to the current context
17818
17819
17820styfn.css = function () {
17821 var self = this;
17822 var args = arguments;
17823
17824 if (args.length === 1) {
17825 var map = args[0];
17826
17827 for (var i = 0; i < self.properties.length; i++) {
17828 var prop = self.properties[i];
17829 var mapVal = map[prop.name];
17830
17831 if (mapVal === undefined) {
17832 mapVal = map[dash2camel(prop.name)];
17833 }
17834
17835 if (mapVal !== undefined) {
17836 this.cssRule(prop.name, mapVal);
17837 }
17838 }
17839 } else if (args.length === 2) {
17840 this.cssRule(args[0], args[1]);
17841 } // do nothing if args are invalid
17842
17843
17844 return this; // chaining
17845};
17846
17847styfn.style = styfn.css; // add a single css rule to the current context
17848
17849styfn.cssRule = function (name, value) {
17850 // name-value pair
17851 var property = this.parse(name, value); // add property to current context if valid
17852
17853 if (property) {
17854 var i = this.length - 1;
17855 this[i].properties.push(property);
17856 this[i].properties[property.name] = property; // allow access by name as well
17857
17858 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17859 this._private.hasPie = true;
17860 }
17861
17862 if (property.mapped) {
17863 this[i].mappedProperties.push(property);
17864 } // add to core style if necessary
17865
17866
17867 var currentSelectorIsCore = !this[i].selector;
17868
17869 if (currentSelectorIsCore) {
17870 this._private.coreStyle[property.name] = property;
17871 }
17872 }
17873
17874 return this; // chaining
17875};
17876
17877styfn.append = function (style) {
17878 if (stylesheet(style)) {
17879 style.appendToStyle(this);
17880 } else if (array(style)) {
17881 this.appendFromJson(style);
17882 } else if (string(style)) {
17883 this.appendFromString(style);
17884 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17885
17886
17887 return this;
17888}; // static function
17889
17890
17891Style.fromJson = function (cy, json) {
17892 var style = new Style(cy);
17893 style.fromJson(json);
17894 return style;
17895};
17896
17897Style.fromString = function (cy, string) {
17898 return new Style(cy).fromString(string);
17899};
17900
17901[styfn$8, styfn$7, styfn$6, styfn$5, styfn$4, styfn$3, styfn$2, styfn$1].forEach(function (props) {
17902 extend(styfn, props);
17903});
17904Style.types = styfn.types;
17905Style.properties = styfn.properties;
17906Style.propertyGroups = styfn.propertyGroups;
17907Style.propertyGroupNames = styfn.propertyGroupNames;
17908Style.propertyGroupKeys = styfn.propertyGroupKeys;
17909
17910var corefn$2 = {
17911 style: function style(newStyle) {
17912 if (newStyle) {
17913 var s = this.setStyle(newStyle);
17914 s.update();
17915 }
17916
17917 return this._private.style;
17918 },
17919 setStyle: function setStyle(style) {
17920 var _p = this._private;
17921
17922 if (stylesheet(style)) {
17923 _p.style = style.generateStyle(this);
17924 } else if (array(style)) {
17925 _p.style = Style.fromJson(this, style);
17926 } else if (string(style)) {
17927 _p.style = Style.fromString(this, style);
17928 } else {
17929 _p.style = Style(this);
17930 }
17931
17932 return _p.style;
17933 },
17934 // e.g. cy.data() changed => recalc ele mappers
17935 updateStyle: function updateStyle() {
17936 this.mutableElements().updateStyle(); // just send to all eles
17937 }
17938};
17939
17940var defaultSelectionType = 'single';
17941var corefn$1 = {
17942 autolock: function autolock(bool) {
17943 if (bool !== undefined) {
17944 this._private.autolock = bool ? true : false;
17945 } else {
17946 return this._private.autolock;
17947 }
17948
17949 return this; // chaining
17950 },
17951 autoungrabify: function autoungrabify(bool) {
17952 if (bool !== undefined) {
17953 this._private.autoungrabify = bool ? true : false;
17954 } else {
17955 return this._private.autoungrabify;
17956 }
17957
17958 return this; // chaining
17959 },
17960 autounselectify: function autounselectify(bool) {
17961 if (bool !== undefined) {
17962 this._private.autounselectify = bool ? true : false;
17963 } else {
17964 return this._private.autounselectify;
17965 }
17966
17967 return this; // chaining
17968 },
17969 selectionType: function selectionType(selType) {
17970 var _p = this._private;
17971
17972 if (_p.selectionType == null) {
17973 _p.selectionType = defaultSelectionType;
17974 }
17975
17976 if (selType !== undefined) {
17977 if (selType === 'additive' || selType === 'single') {
17978 _p.selectionType = selType;
17979 }
17980 } else {
17981 return _p.selectionType;
17982 }
17983
17984 return this;
17985 },
17986 panningEnabled: function panningEnabled(bool) {
17987 if (bool !== undefined) {
17988 this._private.panningEnabled = bool ? true : false;
17989 } else {
17990 return this._private.panningEnabled;
17991 }
17992
17993 return this; // chaining
17994 },
17995 userPanningEnabled: function userPanningEnabled(bool) {
17996 if (bool !== undefined) {
17997 this._private.userPanningEnabled = bool ? true : false;
17998 } else {
17999 return this._private.userPanningEnabled;
18000 }
18001
18002 return this; // chaining
18003 },
18004 zoomingEnabled: function zoomingEnabled(bool) {
18005 if (bool !== undefined) {
18006 this._private.zoomingEnabled = bool ? true : false;
18007 } else {
18008 return this._private.zoomingEnabled;
18009 }
18010
18011 return this; // chaining
18012 },
18013 userZoomingEnabled: function userZoomingEnabled(bool) {
18014 if (bool !== undefined) {
18015 this._private.userZoomingEnabled = bool ? true : false;
18016 } else {
18017 return this._private.userZoomingEnabled;
18018 }
18019
18020 return this; // chaining
18021 },
18022 boxSelectionEnabled: function boxSelectionEnabled(bool) {
18023 if (bool !== undefined) {
18024 this._private.boxSelectionEnabled = bool ? true : false;
18025 } else {
18026 return this._private.boxSelectionEnabled;
18027 }
18028
18029 return this; // chaining
18030 },
18031 pan: function pan() {
18032 var args = arguments;
18033 var pan = this._private.pan;
18034 var dim, val, dims, x, y;
18035
18036 switch (args.length) {
18037 case 0:
18038 // .pan()
18039 return pan;
18040
18041 case 1:
18042 if (string(args[0])) {
18043 // .pan('x')
18044 dim = args[0];
18045 return pan[dim];
18046 } else if (plainObject(args[0])) {
18047 // .pan({ x: 0, y: 100 })
18048 if (!this._private.panningEnabled) {
18049 return this;
18050 }
18051
18052 dims = args[0];
18053 x = dims.x;
18054 y = dims.y;
18055
18056 if (number$1(x)) {
18057 pan.x = x;
18058 }
18059
18060 if (number$1(y)) {
18061 pan.y = y;
18062 }
18063
18064 this.emit('pan viewport');
18065 }
18066
18067 break;
18068
18069 case 2:
18070 // .pan('x', 100)
18071 if (!this._private.panningEnabled) {
18072 return this;
18073 }
18074
18075 dim = args[0];
18076 val = args[1];
18077
18078 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18079 pan[dim] = val;
18080 }
18081
18082 this.emit('pan viewport');
18083 break;
18084 // invalid
18085 }
18086
18087 this.notify('viewport');
18088 return this; // chaining
18089 },
18090 panBy: function panBy(arg0, arg1) {
18091 var args = arguments;
18092 var pan = this._private.pan;
18093 var dim, val, dims, x, y;
18094
18095 if (!this._private.panningEnabled) {
18096 return this;
18097 }
18098
18099 switch (args.length) {
18100 case 1:
18101 if (plainObject(arg0)) {
18102 // .panBy({ x: 0, y: 100 })
18103 dims = args[0];
18104 x = dims.x;
18105 y = dims.y;
18106
18107 if (number$1(x)) {
18108 pan.x += x;
18109 }
18110
18111 if (number$1(y)) {
18112 pan.y += y;
18113 }
18114
18115 this.emit('pan viewport');
18116 }
18117
18118 break;
18119
18120 case 2:
18121 // .panBy('x', 100)
18122 dim = arg0;
18123 val = arg1;
18124
18125 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18126 pan[dim] += val;
18127 }
18128
18129 this.emit('pan viewport');
18130 break;
18131 // invalid
18132 }
18133
18134 this.notify('viewport');
18135 return this; // chaining
18136 },
18137 fit: function fit(elements, padding) {
18138 var viewportState = this.getFitViewport(elements, padding);
18139
18140 if (viewportState) {
18141 var _p = this._private;
18142 _p.zoom = viewportState.zoom;
18143 _p.pan = viewportState.pan;
18144 this.emit('pan zoom viewport');
18145 this.notify('viewport');
18146 }
18147
18148 return this; // chaining
18149 },
18150 getFitViewport: function getFitViewport(elements, padding) {
18151 if (number$1(elements) && padding === undefined) {
18152 // elements is optional
18153 padding = elements;
18154 elements = undefined;
18155 }
18156
18157 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18158 return;
18159 }
18160
18161 var bb;
18162
18163 if (string(elements)) {
18164 var sel = elements;
18165 elements = this.$(sel);
18166 } else if (boundingBox(elements)) {
18167 // assume bb
18168 var bbe = elements;
18169 bb = {
18170 x1: bbe.x1,
18171 y1: bbe.y1,
18172 x2: bbe.x2,
18173 y2: bbe.y2
18174 };
18175 bb.w = bb.x2 - bb.x1;
18176 bb.h = bb.y2 - bb.y1;
18177 } else if (!elementOrCollection(elements)) {
18178 elements = this.mutableElements();
18179 }
18180
18181 if (elementOrCollection(elements) && elements.empty()) {
18182 return;
18183 } // can't fit to nothing
18184
18185
18186 bb = bb || elements.boundingBox();
18187 var w = this.width();
18188 var h = this.height();
18189 var zoom;
18190 padding = number$1(padding) ? padding : 0;
18191
18192 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18193 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18194
18195 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18196 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18197 var pan = {
18198 // now pan to middle
18199 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18200 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18201 };
18202 return {
18203 zoom: zoom,
18204 pan: pan
18205 };
18206 }
18207
18208 return;
18209 },
18210 zoomRange: function zoomRange(min, max) {
18211 var _p = this._private;
18212
18213 if (max == null) {
18214 var opts = min;
18215 min = opts.min;
18216 max = opts.max;
18217 }
18218
18219 if (number$1(min) && number$1(max) && min <= max) {
18220 _p.minZoom = min;
18221 _p.maxZoom = max;
18222 } else if (number$1(min) && max === undefined && min <= _p.maxZoom) {
18223 _p.minZoom = min;
18224 } else if (number$1(max) && min === undefined && max >= _p.minZoom) {
18225 _p.maxZoom = max;
18226 }
18227
18228 return this;
18229 },
18230 minZoom: function minZoom(zoom) {
18231 if (zoom === undefined) {
18232 return this._private.minZoom;
18233 } else {
18234 return this.zoomRange({
18235 min: zoom
18236 });
18237 }
18238 },
18239 maxZoom: function maxZoom(zoom) {
18240 if (zoom === undefined) {
18241 return this._private.maxZoom;
18242 } else {
18243 return this.zoomRange({
18244 max: zoom
18245 });
18246 }
18247 },
18248 getZoomedViewport: function getZoomedViewport(params) {
18249 var _p = this._private;
18250 var currentPan = _p.pan;
18251 var currentZoom = _p.zoom;
18252 var pos; // in rendered px
18253
18254 var zoom;
18255 var bail = false;
18256
18257 if (!_p.zoomingEnabled) {
18258 // zooming disabled
18259 bail = true;
18260 }
18261
18262 if (number$1(params)) {
18263 // then set the zoom
18264 zoom = params;
18265 } else if (plainObject(params)) {
18266 // then zoom about a point
18267 zoom = params.level;
18268
18269 if (params.position != null) {
18270 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18271 } else if (params.renderedPosition != null) {
18272 pos = params.renderedPosition;
18273 }
18274
18275 if (pos != null && !_p.panningEnabled) {
18276 // panning disabled
18277 bail = true;
18278 }
18279 } // crop zoom
18280
18281
18282 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18283 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18284
18285 if (bail || !number$1(zoom) || zoom === currentZoom || pos != null && (!number$1(pos.x) || !number$1(pos.y))) {
18286 return null;
18287 }
18288
18289 if (pos != null) {
18290 // set zoom about position
18291 var pan1 = currentPan;
18292 var zoom1 = currentZoom;
18293 var zoom2 = zoom;
18294 var pan2 = {
18295 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18296 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18297 };
18298 return {
18299 zoomed: true,
18300 panned: true,
18301 zoom: zoom2,
18302 pan: pan2
18303 };
18304 } else {
18305 // just set the zoom
18306 return {
18307 zoomed: true,
18308 panned: false,
18309 zoom: zoom,
18310 pan: currentPan
18311 };
18312 }
18313 },
18314 zoom: function zoom(params) {
18315 if (params === undefined) {
18316 // get
18317 return this._private.zoom;
18318 } else {
18319 // set
18320 var vp = this.getZoomedViewport(params);
18321 var _p = this._private;
18322
18323 if (vp == null || !vp.zoomed) {
18324 return this;
18325 }
18326
18327 _p.zoom = vp.zoom;
18328
18329 if (vp.panned) {
18330 _p.pan.x = vp.pan.x;
18331 _p.pan.y = vp.pan.y;
18332 }
18333
18334 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18335 this.notify('viewport');
18336 return this; // chaining
18337 }
18338 },
18339 viewport: function viewport(opts) {
18340 var _p = this._private;
18341 var zoomDefd = true;
18342 var panDefd = true;
18343 var events = []; // to trigger
18344
18345 var zoomFailed = false;
18346 var panFailed = false;
18347
18348 if (!opts) {
18349 return this;
18350 }
18351
18352 if (!number$1(opts.zoom)) {
18353 zoomDefd = false;
18354 }
18355
18356 if (!plainObject(opts.pan)) {
18357 panDefd = false;
18358 }
18359
18360 if (!zoomDefd && !panDefd) {
18361 return this;
18362 }
18363
18364 if (zoomDefd) {
18365 var z = opts.zoom;
18366
18367 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18368 zoomFailed = true;
18369 } else {
18370 _p.zoom = z;
18371 events.push('zoom');
18372 }
18373 }
18374
18375 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18376 var p = opts.pan;
18377
18378 if (number$1(p.x)) {
18379 _p.pan.x = p.x;
18380 panFailed = false;
18381 }
18382
18383 if (number$1(p.y)) {
18384 _p.pan.y = p.y;
18385 panFailed = false;
18386 }
18387
18388 if (!panFailed) {
18389 events.push('pan');
18390 }
18391 }
18392
18393 if (events.length > 0) {
18394 events.push('viewport');
18395 this.emit(events.join(' '));
18396 this.notify('viewport');
18397 }
18398
18399 return this; // chaining
18400 },
18401 center: function center(elements) {
18402 var pan = this.getCenterPan(elements);
18403
18404 if (pan) {
18405 this._private.pan = pan;
18406 this.emit('pan viewport');
18407 this.notify('viewport');
18408 }
18409
18410 return this; // chaining
18411 },
18412 getCenterPan: function getCenterPan(elements, zoom) {
18413 if (!this._private.panningEnabled) {
18414 return;
18415 }
18416
18417 if (string(elements)) {
18418 var selector = elements;
18419 elements = this.mutableElements().filter(selector);
18420 } else if (!elementOrCollection(elements)) {
18421 elements = this.mutableElements();
18422 }
18423
18424 if (elements.length === 0) {
18425 return;
18426 } // can't centre pan to nothing
18427
18428
18429 var bb = elements.boundingBox();
18430 var w = this.width();
18431 var h = this.height();
18432 zoom = zoom === undefined ? this._private.zoom : zoom;
18433 var pan = {
18434 // middle
18435 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18436 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18437 };
18438 return pan;
18439 },
18440 reset: function reset() {
18441 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18442 return this;
18443 }
18444
18445 this.viewport({
18446 pan: {
18447 x: 0,
18448 y: 0
18449 },
18450 zoom: 1
18451 });
18452 return this; // chaining
18453 },
18454 invalidateSize: function invalidateSize() {
18455 this._private.sizeCache = null;
18456 },
18457 size: function size() {
18458 var _p = this._private;
18459 var container = _p.container;
18460 return _p.sizeCache = _p.sizeCache || (container ? function () {
18461 var style = window$1.getComputedStyle(container);
18462
18463 var val = function val(name) {
18464 return parseFloat(style.getPropertyValue(name));
18465 };
18466
18467 return {
18468 width: container.clientWidth - val('padding-left') - val('padding-right'),
18469 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18470 };
18471 }() : {
18472 // fallback if no container (not 0 b/c can be used for dividing etc)
18473 width: 1,
18474 height: 1
18475 });
18476 },
18477 width: function width() {
18478 return this.size().width;
18479 },
18480 height: function height() {
18481 return this.size().height;
18482 },
18483 extent: function extent() {
18484 var pan = this._private.pan;
18485 var zoom = this._private.zoom;
18486 var rb = this.renderedExtent();
18487 var b = {
18488 x1: (rb.x1 - pan.x) / zoom,
18489 x2: (rb.x2 - pan.x) / zoom,
18490 y1: (rb.y1 - pan.y) / zoom,
18491 y2: (rb.y2 - pan.y) / zoom
18492 };
18493 b.w = b.x2 - b.x1;
18494 b.h = b.y2 - b.y1;
18495 return b;
18496 },
18497 renderedExtent: function renderedExtent() {
18498 var width = this.width();
18499 var height = this.height();
18500 return {
18501 x1: 0,
18502 y1: 0,
18503 x2: width,
18504 y2: height,
18505 w: width,
18506 h: height
18507 };
18508 },
18509 multiClickDebounceTime: function multiClickDebounceTime(_int) {
18510 if (_int) this._private.multiClickDebounceTime = _int;else return this._private.multiClickDebounceTime;
18511 return this; // chaining
18512 }
18513}; // aliases
18514
18515corefn$1.centre = corefn$1.center; // backwards compatibility
18516
18517corefn$1.autolockNodes = corefn$1.autolock;
18518corefn$1.autoungrabifyNodes = corefn$1.autoungrabify;
18519
18520var fn = {
18521 data: define.data({
18522 field: 'data',
18523 bindingEvent: 'data',
18524 allowBinding: true,
18525 allowSetting: true,
18526 settingEvent: 'data',
18527 settingTriggersEvent: true,
18528 triggerFnName: 'trigger',
18529 allowGetting: true,
18530 updateStyle: true
18531 }),
18532 removeData: define.removeData({
18533 field: 'data',
18534 event: 'data',
18535 triggerFnName: 'trigger',
18536 triggerEvent: true,
18537 updateStyle: true
18538 }),
18539 scratch: define.data({
18540 field: 'scratch',
18541 bindingEvent: 'scratch',
18542 allowBinding: true,
18543 allowSetting: true,
18544 settingEvent: 'scratch',
18545 settingTriggersEvent: true,
18546 triggerFnName: 'trigger',
18547 allowGetting: true,
18548 updateStyle: true
18549 }),
18550 removeScratch: define.removeData({
18551 field: 'scratch',
18552 event: 'scratch',
18553 triggerFnName: 'trigger',
18554 triggerEvent: true,
18555 updateStyle: true
18556 })
18557}; // aliases
18558
18559fn.attr = fn.data;
18560fn.removeAttr = fn.removeData;
18561
18562var Core = function Core(opts) {
18563 var cy = this;
18564 opts = extend({}, opts);
18565 var container = opts.container; // allow for passing a wrapped jquery object
18566 // e.g. cytoscape({ container: $('#cy') })
18567
18568 if (container && !htmlElement(container) && htmlElement(container[0])) {
18569 container = container[0];
18570 }
18571
18572 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18573
18574 reg = reg || {};
18575
18576 if (reg && reg.cy) {
18577 reg.cy.destroy();
18578 reg = {}; // old instance => replace reg completely
18579 }
18580
18581 var readies = reg.readies = reg.readies || [];
18582
18583 if (container) {
18584 container._cyreg = reg;
18585 } // make sure container assoc'd reg points to this cy
18586
18587
18588 reg.cy = cy;
18589 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18590 var options = opts;
18591 options.layout = extend({
18592 name: head ? 'grid' : 'null'
18593 }, options.layout);
18594 options.renderer = extend({
18595 name: head ? 'canvas' : 'null'
18596 }, options.renderer);
18597
18598 var defVal = function defVal(def, val, altVal) {
18599 if (val !== undefined) {
18600 return val;
18601 } else if (altVal !== undefined) {
18602 return altVal;
18603 } else {
18604 return def;
18605 }
18606 };
18607
18608 var _p = this._private = {
18609 container: container,
18610 // html dom ele container
18611 ready: false,
18612 // whether ready has been triggered
18613 options: options,
18614 // cached options
18615 elements: new Collection(this),
18616 // elements in the graph
18617 listeners: [],
18618 // list of listeners
18619 aniEles: new Collection(this),
18620 // elements being animated
18621 data: options.data || {},
18622 // data for the core
18623 scratch: {},
18624 // scratch object for core
18625 layout: null,
18626 renderer: null,
18627 destroyed: false,
18628 // whether destroy was called
18629 notificationsEnabled: true,
18630 // whether notifications are sent to the renderer
18631 minZoom: 1e-50,
18632 maxZoom: 1e50,
18633 zoomingEnabled: defVal(true, options.zoomingEnabled),
18634 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18635 panningEnabled: defVal(true, options.panningEnabled),
18636 userPanningEnabled: defVal(true, options.userPanningEnabled),
18637 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18638 autolock: defVal(false, options.autolock, options.autolockNodes),
18639 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18640 autounselectify: defVal(false, options.autounselectify),
18641 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18642 zoom: number$1(options.zoom) ? options.zoom : 1,
18643 pan: {
18644 x: plainObject(options.pan) && number$1(options.pan.x) ? options.pan.x : 0,
18645 y: plainObject(options.pan) && number$1(options.pan.y) ? options.pan.y : 0
18646 },
18647 animation: {
18648 // object for currently-running animations
18649 current: [],
18650 queue: []
18651 },
18652 hasCompoundNodes: false,
18653 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
18654 };
18655
18656 this.createEmitter(); // set selection type
18657
18658 this.selectionType(options.selectionType); // init zoom bounds
18659
18660 this.zoomRange({
18661 min: options.minZoom,
18662 max: options.maxZoom
18663 });
18664
18665 var loadExtData = function loadExtData(extData, next) {
18666 var anyIsPromise = extData.some(promise);
18667
18668 if (anyIsPromise) {
18669 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18670 } else {
18671 next(extData); // exec synchronously for convenience
18672 }
18673 }; // start with the default stylesheet so we have something before loading an external stylesheet
18674
18675
18676 if (_p.styleEnabled) {
18677 cy.setStyle([]);
18678 } // create the renderer
18679
18680
18681 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18682
18683 cy.initRenderer(rendererOptions);
18684
18685 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18686 cy.notifications(false); // remove old elements
18687
18688 var oldEles = cy.mutableElements();
18689
18690 if (oldEles.length > 0) {
18691 oldEles.remove();
18692 }
18693
18694 if (elements != null) {
18695 if (plainObject(elements) || array(elements)) {
18696 cy.add(elements);
18697 }
18698 }
18699
18700 cy.one('layoutready', function (e) {
18701 cy.notifications(true);
18702 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18703
18704 cy.one('load', onload);
18705 cy.emitAndNotify('load');
18706 }).one('layoutstop', function () {
18707 cy.one('done', ondone);
18708 cy.emit('done');
18709 });
18710 var layoutOpts = extend({}, cy._private.options.layout);
18711 layoutOpts.eles = cy.elements();
18712 cy.layout(layoutOpts).run();
18713 };
18714
18715 loadExtData([options.style, options.elements], function (thens) {
18716 var initStyle = thens[0];
18717 var initEles = thens[1]; // init style
18718
18719 if (_p.styleEnabled) {
18720 cy.style().append(initStyle);
18721 } // initial load
18722
18723
18724 setElesAndLayout(initEles, function () {
18725 // onready
18726 cy.startAnimationLoop();
18727 _p.ready = true; // if a ready callback is specified as an option, the bind it
18728
18729 if (fn$6(options.ready)) {
18730 cy.on('ready', options.ready);
18731 } // bind all the ready handlers registered before creating this instance
18732
18733
18734 for (var i = 0; i < readies.length; i++) {
18735 var fn = readies[i];
18736 cy.on('ready', fn);
18737 }
18738
18739 if (reg) {
18740 reg.readies = [];
18741 } // 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
18742
18743
18744 cy.emit('ready');
18745 }, options.done);
18746 });
18747};
18748
18749var corefn = Core.prototype; // short alias
18750
18751extend(corefn, {
18752 instanceString: function instanceString() {
18753 return 'core';
18754 },
18755 isReady: function isReady() {
18756 return this._private.ready;
18757 },
18758 destroyed: function destroyed() {
18759 return this._private.destroyed;
18760 },
18761 ready: function ready(fn) {
18762 if (this.isReady()) {
18763 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18764 } else {
18765 this.on('ready', fn);
18766 }
18767
18768 return this;
18769 },
18770 destroy: function destroy() {
18771 var cy = this;
18772 if (cy.destroyed()) return;
18773 cy.stopAnimationLoop();
18774 cy.destroyRenderer();
18775 this.emit('destroy');
18776 cy._private.destroyed = true;
18777 return cy;
18778 },
18779 hasElementWithId: function hasElementWithId(id) {
18780 return this._private.elements.hasElementWithId(id);
18781 },
18782 getElementById: function getElementById(id) {
18783 return this._private.elements.getElementById(id);
18784 },
18785 hasCompoundNodes: function hasCompoundNodes() {
18786 return this._private.hasCompoundNodes;
18787 },
18788 headless: function headless() {
18789 return this._private.renderer.isHeadless();
18790 },
18791 styleEnabled: function styleEnabled() {
18792 return this._private.styleEnabled;
18793 },
18794 addToPool: function addToPool(eles) {
18795 this._private.elements.merge(eles);
18796
18797 return this; // chaining
18798 },
18799 removeFromPool: function removeFromPool(eles) {
18800 this._private.elements.unmerge(eles);
18801
18802 return this;
18803 },
18804 container: function container() {
18805 return this._private.container || null;
18806 },
18807 mount: function mount(container) {
18808 if (container == null) {
18809 return;
18810 }
18811
18812 var cy = this;
18813 var _p = cy._private;
18814 var options = _p.options;
18815
18816 if (!htmlElement(container) && htmlElement(container[0])) {
18817 container = container[0];
18818 }
18819
18820 cy.stopAnimationLoop();
18821 cy.destroyRenderer();
18822 _p.container = container;
18823 _p.styleEnabled = true;
18824 cy.invalidateSize();
18825 cy.initRenderer(extend({}, options, options.renderer, {
18826 // allow custom renderer name to be re-used, otherwise use canvas
18827 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18828 }));
18829 cy.startAnimationLoop();
18830 cy.style(options.style);
18831 cy.emit('mount');
18832 return cy;
18833 },
18834 unmount: function unmount() {
18835 var cy = this;
18836 cy.stopAnimationLoop();
18837 cy.destroyRenderer();
18838 cy.initRenderer({
18839 name: 'null'
18840 });
18841 cy.emit('unmount');
18842 return cy;
18843 },
18844 options: function options() {
18845 return copy(this._private.options);
18846 },
18847 json: function json(obj) {
18848 var cy = this;
18849 var _p = cy._private;
18850 var eles = cy.mutableElements();
18851
18852 var getFreshRef = function getFreshRef(ele) {
18853 return cy.getElementById(ele.id());
18854 };
18855
18856 if (plainObject(obj)) {
18857 // set
18858 cy.startBatch();
18859
18860 if (obj.elements) {
18861 var idInJson = {};
18862
18863 var updateEles = function updateEles(jsons, gr) {
18864 var toAdd = [];
18865 var toMod = [];
18866
18867 for (var i = 0; i < jsons.length; i++) {
18868 var json = jsons[i];
18869
18870 if (!json.data.id) {
18871 warn('cy.json() cannot handle elements without an ID attribute');
18872 continue;
18873 }
18874
18875 var id = '' + json.data.id; // id must be string
18876
18877 var ele = cy.getElementById(id);
18878 idInJson[id] = true;
18879
18880 if (ele.length !== 0) {
18881 // existing element should be updated
18882 toMod.push({
18883 ele: ele,
18884 json: json
18885 });
18886 } else {
18887 // otherwise should be added
18888 if (gr) {
18889 json.group = gr;
18890 toAdd.push(json);
18891 } else {
18892 toAdd.push(json);
18893 }
18894 }
18895 }
18896
18897 cy.add(toAdd);
18898
18899 for (var _i = 0; _i < toMod.length; _i++) {
18900 var _toMod$_i = toMod[_i],
18901 _ele = _toMod$_i.ele,
18902 _json = _toMod$_i.json;
18903
18904 _ele.json(_json);
18905 }
18906 };
18907
18908 if (array(obj.elements)) {
18909 // elements: []
18910 updateEles(obj.elements);
18911 } else {
18912 // elements: { nodes: [], edges: [] }
18913 var grs = ['nodes', 'edges'];
18914
18915 for (var i = 0; i < grs.length; i++) {
18916 var gr = grs[i];
18917 var elements = obj.elements[gr];
18918
18919 if (array(elements)) {
18920 updateEles(elements, gr);
18921 }
18922 }
18923 }
18924
18925 var parentsToRemove = cy.collection();
18926 eles.filter(function (ele) {
18927 return !idInJson[ele.id()];
18928 }).forEach(function (ele) {
18929 if (ele.isParent()) {
18930 parentsToRemove.merge(ele);
18931 } else {
18932 ele.remove();
18933 }
18934 }); // so that children are not removed w/parent
18935
18936 parentsToRemove.forEach(function (ele) {
18937 return ele.children().move({
18938 parent: null
18939 });
18940 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18941
18942 parentsToRemove.forEach(function (ele) {
18943 return getFreshRef(ele).remove();
18944 });
18945 }
18946
18947 if (obj.style) {
18948 cy.style(obj.style);
18949 }
18950
18951 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18952 cy.zoom(obj.zoom);
18953 }
18954
18955 if (obj.pan) {
18956 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18957 cy.pan(obj.pan);
18958 }
18959 }
18960
18961 if (obj.data) {
18962 cy.data(obj.data);
18963 }
18964
18965 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify', 'multiClickDebounceTime'];
18966
18967 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18968 var f = fields[_i2];
18969
18970 if (obj[f] != null) {
18971 cy[f](obj[f]);
18972 }
18973 }
18974
18975 cy.endBatch();
18976 return this; // chaining
18977 } else {
18978 // get
18979 var flat = !!obj;
18980 var json = {};
18981
18982 if (flat) {
18983 json.elements = this.elements().map(function (ele) {
18984 return ele.json();
18985 });
18986 } else {
18987 json.elements = {};
18988 eles.forEach(function (ele) {
18989 var group = ele.group();
18990
18991 if (!json.elements[group]) {
18992 json.elements[group] = [];
18993 }
18994
18995 json.elements[group].push(ele.json());
18996 });
18997 }
18998
18999 if (this._private.styleEnabled) {
19000 json.style = cy.style().json();
19001 }
19002
19003 json.data = copy(cy.data());
19004 var options = _p.options;
19005 json.zoomingEnabled = _p.zoomingEnabled;
19006 json.userZoomingEnabled = _p.userZoomingEnabled;
19007 json.zoom = _p.zoom;
19008 json.minZoom = _p.minZoom;
19009 json.maxZoom = _p.maxZoom;
19010 json.panningEnabled = _p.panningEnabled;
19011 json.userPanningEnabled = _p.userPanningEnabled;
19012 json.pan = copy(_p.pan);
19013 json.boxSelectionEnabled = _p.boxSelectionEnabled;
19014 json.renderer = copy(options.renderer);
19015 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
19016 json.textureOnViewport = options.textureOnViewport;
19017 json.wheelSensitivity = options.wheelSensitivity;
19018 json.motionBlur = options.motionBlur;
19019 json.multiClickDebounceTime = options.multiClickDebounceTime;
19020 return json;
19021 }
19022 }
19023});
19024corefn.$id = corefn.getElementById;
19025[corefn$9, corefn$8, elesfn, corefn$7, corefn$6, corefn$5, corefn$4, corefn$3, corefn$2, corefn$1, fn].forEach(function (props) {
19026 extend(corefn, props);
19027});
19028
19029/* eslint-disable no-unused-vars */
19030
19031var defaults$7 = {
19032 fit: true,
19033 // whether to fit the viewport to the graph
19034 directed: false,
19035 // whether the tree is directed downwards (or edges can point in any direction if false)
19036 padding: 30,
19037 // padding on fit
19038 circle: false,
19039 // put depths in concentric circles if true, put depths top down if false
19040 grid: false,
19041 // whether to create an even grid into which the DAG is placed (circle:false only)
19042 spacingFactor: 1.75,
19043 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19044 boundingBox: undefined,
19045 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19046 avoidOverlap: true,
19047 // prevents node overlap, may overflow boundingBox if not enough space
19048 nodeDimensionsIncludeLabels: false,
19049 // Excludes the label when calculating node bounding boxes for the layout algorithm
19050 roots: undefined,
19051 // the roots of the trees
19052 maximal: false,
19053 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
19054 depthSort: undefined,
19055 // a sorting function to order nodes at equal depth. e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19056 animate: false,
19057 // whether to transition the node positions
19058 animationDuration: 500,
19059 // duration of animation in ms if enabled
19060 animationEasing: undefined,
19061 // easing of animation if enabled,
19062 animateFilter: function animateFilter(node, i) {
19063 return true;
19064 },
19065 // 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
19066 ready: undefined,
19067 // callback on layoutready
19068 stop: undefined,
19069 // callback on layoutstop
19070 transform: function transform(node, position) {
19071 return position;
19072 } // transform a given node position. Useful for changing flow direction in discrete layouts
19073
19074};
19075/* eslint-enable */
19076
19077var getInfo = function getInfo(ele) {
19078 return ele.scratch('breadthfirst');
19079};
19080
19081var setInfo = function setInfo(ele, obj) {
19082 return ele.scratch('breadthfirst', obj);
19083};
19084
19085function BreadthFirstLayout(options) {
19086 this.options = extend({}, defaults$7, options);
19087}
19088
19089BreadthFirstLayout.prototype.run = function () {
19090 var params = this.options;
19091 var options = params;
19092 var cy = params.cy;
19093 var eles = options.eles;
19094 var nodes = eles.nodes().filter(function (n) {
19095 return !n.isParent();
19096 });
19097 var graph = eles;
19098 var directed = options.directed;
19099 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
19100
19101 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19102 x1: 0,
19103 y1: 0,
19104 w: cy.width(),
19105 h: cy.height()
19106 });
19107 var roots;
19108
19109 if (elementOrCollection(options.roots)) {
19110 roots = options.roots;
19111 } else if (array(options.roots)) {
19112 var rootsArray = [];
19113
19114 for (var i = 0; i < options.roots.length; i++) {
19115 var id = options.roots[i];
19116 var ele = cy.getElementById(id);
19117 rootsArray.push(ele);
19118 }
19119
19120 roots = cy.collection(rootsArray);
19121 } else if (string(options.roots)) {
19122 roots = cy.$(options.roots);
19123 } else {
19124 if (directed) {
19125 roots = nodes.roots();
19126 } else {
19127 var components = eles.components();
19128 roots = cy.collection();
19129
19130 var _loop = function _loop(_i) {
19131 var comp = components[_i];
19132 var maxDegree = comp.maxDegree(false);
19133 var compRoots = comp.filter(function (ele) {
19134 return ele.degree(false) === maxDegree;
19135 });
19136 roots = roots.add(compRoots);
19137 };
19138
19139 for (var _i = 0; _i < components.length; _i++) {
19140 _loop(_i);
19141 }
19142 }
19143 }
19144
19145 var depths = [];
19146 var foundByBfs = {};
19147
19148 var addToDepth = function addToDepth(ele, d) {
19149 if (depths[d] == null) {
19150 depths[d] = [];
19151 }
19152
19153 var i = depths[d].length;
19154 depths[d].push(ele);
19155 setInfo(ele, {
19156 index: i,
19157 depth: d
19158 });
19159 };
19160
19161 var changeDepth = function changeDepth(ele, newDepth) {
19162 var _getInfo = getInfo(ele),
19163 depth = _getInfo.depth,
19164 index = _getInfo.index;
19165
19166 depths[depth][index] = null;
19167 addToDepth(ele, newDepth);
19168 }; // find the depths of the nodes
19169
19170
19171 graph.bfs({
19172 roots: roots,
19173 directed: options.directed,
19174 visit: function visit(node, edge, pNode, i, depth) {
19175 var ele = node[0];
19176 var id = ele.id();
19177 addToDepth(ele, depth);
19178 foundByBfs[id] = true;
19179 }
19180 }); // check for nodes not found by bfs
19181
19182 var orphanNodes = [];
19183
19184 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19185 var _ele = nodes[_i2];
19186
19187 if (foundByBfs[_ele.id()]) {
19188 continue;
19189 } else {
19190 orphanNodes.push(_ele);
19191 }
19192 } // assign the nodes a depth and index
19193
19194
19195 var assignDepthsAt = function assignDepthsAt(i) {
19196 var eles = depths[i];
19197
19198 for (var j = 0; j < eles.length; j++) {
19199 var _ele2 = eles[j];
19200
19201 if (_ele2 == null) {
19202 eles.splice(j, 1);
19203 j--;
19204 continue;
19205 }
19206
19207 setInfo(_ele2, {
19208 depth: i,
19209 index: j
19210 });
19211 }
19212 };
19213
19214 var assignDepths = function assignDepths() {
19215 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19216 assignDepthsAt(_i3);
19217 }
19218 };
19219
19220 var adjustMaximally = function adjustMaximally(ele, shifted) {
19221 var eInfo = getInfo(ele);
19222 var incomers = ele.incomers().filter(function (el) {
19223 return el.isNode() && eles.has(el);
19224 });
19225 var maxDepth = -1;
19226 var id = ele.id();
19227
19228 for (var k = 0; k < incomers.length; k++) {
19229 var incmr = incomers[k];
19230 var iInfo = getInfo(incmr);
19231 maxDepth = Math.max(maxDepth, iInfo.depth);
19232 }
19233
19234 if (eInfo.depth <= maxDepth) {
19235 if (shifted[id]) {
19236 return null;
19237 }
19238
19239 changeDepth(ele, maxDepth + 1);
19240 shifted[id] = true;
19241 return true;
19242 }
19243
19244 return false;
19245 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19246
19247
19248 if (directed && maximal) {
19249 var Q = [];
19250 var shifted = {};
19251
19252 var enqueue = function enqueue(n) {
19253 return Q.push(n);
19254 };
19255
19256 var dequeue = function dequeue() {
19257 return Q.shift();
19258 };
19259
19260 nodes.forEach(function (n) {
19261 return Q.push(n);
19262 });
19263
19264 while (Q.length > 0) {
19265 var _ele3 = dequeue();
19266
19267 var didShift = adjustMaximally(_ele3, shifted);
19268
19269 if (didShift) {
19270 _ele3.outgoers().filter(function (el) {
19271 return el.isNode() && eles.has(el);
19272 }).forEach(enqueue);
19273 } else if (didShift === null) {
19274 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19275 break; // exit on failure
19276 }
19277 }
19278 }
19279
19280 assignDepths(); // clear holes
19281 // find min distance we need to leave between nodes
19282
19283 var minDistance = 0;
19284
19285 if (options.avoidOverlap) {
19286 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19287 var n = nodes[_i4];
19288 var nbb = n.layoutDimensions(options);
19289 var w = nbb.w;
19290 var h = nbb.h;
19291 minDistance = Math.max(minDistance, w, h);
19292 }
19293 } // get the weighted percent for an element based on its connectivity to other levels
19294
19295
19296 var cachedWeightedPercent = {};
19297
19298 var getWeightedPercent = function getWeightedPercent(ele) {
19299 if (cachedWeightedPercent[ele.id()]) {
19300 return cachedWeightedPercent[ele.id()];
19301 }
19302
19303 var eleDepth = getInfo(ele).depth;
19304 var neighbors = ele.neighborhood();
19305 var percent = 0;
19306 var samples = 0;
19307
19308 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19309 var neighbor = neighbors[_i5];
19310
19311 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19312 continue;
19313 }
19314
19315 var bf = getInfo(neighbor);
19316
19317 if (bf == null) {
19318 continue;
19319 }
19320
19321 var index = bf.index;
19322 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19323
19324 if (index == null || depth == null) {
19325 continue;
19326 }
19327
19328 var nDepth = depths[depth].length;
19329
19330 if (depth < eleDepth) {
19331 // only get influenced by elements above
19332 percent += index / nDepth;
19333 samples++;
19334 }
19335 }
19336
19337 samples = Math.max(1, samples);
19338 percent = percent / samples;
19339
19340 if (samples === 0) {
19341 // put lone nodes at the start
19342 percent = 0;
19343 }
19344
19345 cachedWeightedPercent[ele.id()] = percent;
19346 return percent;
19347 }; // rearrange the indices in each depth level based on connectivity
19348
19349
19350 var sortFn = function sortFn(a, b) {
19351 var apct = getWeightedPercent(a);
19352 var bpct = getWeightedPercent(b);
19353 var diff = apct - bpct;
19354
19355 if (diff === 0) {
19356 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19357 } else {
19358 return diff;
19359 }
19360 };
19361
19362 if (options.depthSort !== undefined) {
19363 sortFn = options.depthSort;
19364 } // sort each level to make connected nodes closer
19365
19366
19367 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19368 depths[_i6].sort(sortFn);
19369
19370 assignDepthsAt(_i6);
19371 } // assign orphan nodes to a new top-level depth
19372
19373
19374 var orphanDepth = [];
19375
19376 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19377 orphanDepth.push(orphanNodes[_i7]);
19378 }
19379
19380 depths.unshift(orphanDepth);
19381 assignDepths();
19382 var biggestDepthSize = 0;
19383
19384 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19385 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19386 }
19387
19388 var center = {
19389 x: bb.x1 + bb.w / 2,
19390 y: bb.x1 + bb.h / 2
19391 };
19392 var maxDepthSize = depths.reduce(function (max, eles) {
19393 return Math.max(max, eles.length);
19394 }, 0);
19395
19396 var getPosition = function getPosition(ele) {
19397 var _getInfo2 = getInfo(ele),
19398 depth = _getInfo2.depth,
19399 index = _getInfo2.index;
19400
19401 var depthSize = depths[depth].length;
19402 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19403 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19404 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19405 radiusStepSize = Math.max(radiusStepSize, minDistance);
19406
19407 if (!options.circle) {
19408 var epos = {
19409 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19410 y: (depth + 1) * distanceY
19411 };
19412 return epos;
19413 } else {
19414 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19415 var theta = 2 * Math.PI / depths[depth].length * index;
19416
19417 if (depth === 0 && depths[0].length === 1) {
19418 radius = 1;
19419 }
19420
19421 return {
19422 x: center.x + radius * Math.cos(theta),
19423 y: center.y + radius * Math.sin(theta)
19424 };
19425 }
19426 };
19427
19428 eles.nodes().layoutPositions(this, options, getPosition);
19429 return this; // chaining
19430};
19431
19432var defaults$6 = {
19433 fit: true,
19434 // whether to fit the viewport to the graph
19435 padding: 30,
19436 // the padding on fit
19437 boundingBox: undefined,
19438 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19439 avoidOverlap: true,
19440 // prevents node overlap, may overflow boundingBox and radius if not enough space
19441 nodeDimensionsIncludeLabels: false,
19442 // Excludes the label when calculating node bounding boxes for the layout algorithm
19443 spacingFactor: undefined,
19444 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19445 radius: undefined,
19446 // the radius of the circle
19447 startAngle: 3 / 2 * Math.PI,
19448 // where nodes start in radians
19449 sweep: undefined,
19450 // how many radians should be between the first and last node (defaults to full circle)
19451 clockwise: true,
19452 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19453 sort: undefined,
19454 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19455 animate: false,
19456 // whether to transition the node positions
19457 animationDuration: 500,
19458 // duration of animation in ms if enabled
19459 animationEasing: undefined,
19460 // easing of animation if enabled
19461 animateFilter: function animateFilter(node, i) {
19462 return true;
19463 },
19464 // 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
19465 ready: undefined,
19466 // callback on layoutready
19467 stop: undefined,
19468 // callback on layoutstop
19469 transform: function transform(node, position) {
19470 return position;
19471 } // transform a given node position. Useful for changing flow direction in discrete layouts
19472
19473};
19474
19475function CircleLayout(options) {
19476 this.options = extend({}, defaults$6, options);
19477}
19478
19479CircleLayout.prototype.run = function () {
19480 var params = this.options;
19481 var options = params;
19482 var cy = params.cy;
19483 var eles = options.eles;
19484 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19485 var nodes = eles.nodes().not(':parent');
19486
19487 if (options.sort) {
19488 nodes = nodes.sort(options.sort);
19489 }
19490
19491 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19492 x1: 0,
19493 y1: 0,
19494 w: cy.width(),
19495 h: cy.height()
19496 });
19497 var center = {
19498 x: bb.x1 + bb.w / 2,
19499 y: bb.y1 + bb.h / 2
19500 };
19501 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19502 var dTheta = sweep / Math.max(1, nodes.length - 1);
19503 var r;
19504 var minDistance = 0;
19505
19506 for (var i = 0; i < nodes.length; i++) {
19507 var n = nodes[i];
19508 var nbb = n.layoutDimensions(options);
19509 var w = nbb.w;
19510 var h = nbb.h;
19511 minDistance = Math.max(minDistance, w, h);
19512 }
19513
19514 if (number$1(options.radius)) {
19515 r = options.radius;
19516 } else if (nodes.length <= 1) {
19517 r = 0;
19518 } else {
19519 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19520 } // calculate the radius
19521
19522
19523 if (nodes.length > 1 && options.avoidOverlap) {
19524 // but only if more than one node (can't overlap)
19525 minDistance *= 1.75; // just to have some nice spacing
19526
19527 var dcos = Math.cos(dTheta) - Math.cos(0);
19528 var dsin = Math.sin(dTheta) - Math.sin(0);
19529 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19530
19531 r = Math.max(rMin, r);
19532 }
19533
19534 var getPos = function getPos(ele, i) {
19535 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19536 var rx = r * Math.cos(theta);
19537 var ry = r * Math.sin(theta);
19538 var pos = {
19539 x: center.x + rx,
19540 y: center.y + ry
19541 };
19542 return pos;
19543 };
19544
19545 eles.nodes().layoutPositions(this, options, getPos);
19546 return this; // chaining
19547};
19548
19549var defaults$5 = {
19550 fit: true,
19551 // whether to fit the viewport to the graph
19552 padding: 30,
19553 // the padding on fit
19554 startAngle: 3 / 2 * Math.PI,
19555 // where nodes start in radians
19556 sweep: undefined,
19557 // how many radians should be between the first and last node (defaults to full circle)
19558 clockwise: true,
19559 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19560 equidistant: false,
19561 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19562 minNodeSpacing: 10,
19563 // min spacing between outside of nodes (used for radius adjustment)
19564 boundingBox: undefined,
19565 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19566 avoidOverlap: true,
19567 // prevents node overlap, may overflow boundingBox if not enough space
19568 nodeDimensionsIncludeLabels: false,
19569 // Excludes the label when calculating node bounding boxes for the layout algorithm
19570 height: undefined,
19571 // height of layout area (overrides container height)
19572 width: undefined,
19573 // width of layout area (overrides container width)
19574 spacingFactor: undefined,
19575 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19576 concentric: function concentric(node) {
19577 // returns numeric value for each node, placing higher nodes in levels towards the centre
19578 return node.degree();
19579 },
19580 levelWidth: function levelWidth(nodes) {
19581 // the variation of concentric values in each level
19582 return nodes.maxDegree() / 4;
19583 },
19584 animate: false,
19585 // whether to transition the node positions
19586 animationDuration: 500,
19587 // duration of animation in ms if enabled
19588 animationEasing: undefined,
19589 // easing of animation if enabled
19590 animateFilter: function animateFilter(node, i) {
19591 return true;
19592 },
19593 // 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
19594 ready: undefined,
19595 // callback on layoutready
19596 stop: undefined,
19597 // callback on layoutstop
19598 transform: function transform(node, position) {
19599 return position;
19600 } // transform a given node position. Useful for changing flow direction in discrete layouts
19601
19602};
19603
19604function ConcentricLayout(options) {
19605 this.options = extend({}, defaults$5, options);
19606}
19607
19608ConcentricLayout.prototype.run = function () {
19609 var params = this.options;
19610 var options = params;
19611 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19612 var cy = params.cy;
19613 var eles = options.eles;
19614 var nodes = eles.nodes().not(':parent');
19615 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19616 x1: 0,
19617 y1: 0,
19618 w: cy.width(),
19619 h: cy.height()
19620 });
19621 var center = {
19622 x: bb.x1 + bb.w / 2,
19623 y: bb.y1 + bb.h / 2
19624 };
19625 var nodeValues = []; // { node, value }
19626
19627 var maxNodeSize = 0;
19628
19629 for (var i = 0; i < nodes.length; i++) {
19630 var node = nodes[i];
19631 var value = void 0; // calculate the node value
19632
19633 value = options.concentric(node);
19634 nodeValues.push({
19635 value: value,
19636 node: node
19637 }); // for style mapping
19638
19639 node._private.scratch.concentric = value;
19640 } // in case we used the `concentric` in style
19641
19642
19643 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19644
19645 for (var _i = 0; _i < nodes.length; _i++) {
19646 var _node = nodes[_i];
19647
19648 var nbb = _node.layoutDimensions(options);
19649
19650 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19651 } // sort node values in descreasing order
19652
19653
19654 nodeValues.sort(function (a, b) {
19655 return b.value - a.value;
19656 });
19657 var levelWidth = options.levelWidth(nodes); // put the values into levels
19658
19659 var levels = [[]];
19660 var currentLevel = levels[0];
19661
19662 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19663 var val = nodeValues[_i2];
19664
19665 if (currentLevel.length > 0) {
19666 var diff = Math.abs(currentLevel[0].value - val.value);
19667
19668 if (diff >= levelWidth) {
19669 currentLevel = [];
19670 levels.push(currentLevel);
19671 }
19672 }
19673
19674 currentLevel.push(val);
19675 } // create positions from levels
19676
19677
19678 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19679
19680 if (!options.avoidOverlap) {
19681 // then strictly constrain to bb
19682 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19683 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19684 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19685 minDist = Math.min(minDist, rStep);
19686 } // find the metrics for each level
19687
19688
19689 var r = 0;
19690
19691 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19692 var level = levels[_i3];
19693 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19694 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19695
19696 if (level.length > 1 && options.avoidOverlap) {
19697 // but only if more than one node (can't overlap)
19698 var dcos = Math.cos(dTheta) - Math.cos(0);
19699 var dsin = Math.sin(dTheta) - Math.sin(0);
19700 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19701
19702 r = Math.max(rMin, r);
19703 }
19704
19705 level.r = r;
19706 r += minDist;
19707 }
19708
19709 if (options.equidistant) {
19710 var rDeltaMax = 0;
19711 var _r = 0;
19712
19713 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19714 var _level = levels[_i4];
19715 var rDelta = _level.r - _r;
19716 rDeltaMax = Math.max(rDeltaMax, rDelta);
19717 }
19718
19719 _r = 0;
19720
19721 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19722 var _level2 = levels[_i5];
19723
19724 if (_i5 === 0) {
19725 _r = _level2.r;
19726 }
19727
19728 _level2.r = _r;
19729 _r += rDeltaMax;
19730 }
19731 } // calculate the node positions
19732
19733
19734 var pos = {}; // id => position
19735
19736 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19737 var _level3 = levels[_i6];
19738 var _dTheta = _level3.dTheta;
19739 var _r2 = _level3.r;
19740
19741 for (var j = 0; j < _level3.length; j++) {
19742 var _val = _level3[j];
19743 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19744 var p = {
19745 x: center.x + _r2 * Math.cos(theta),
19746 y: center.y + _r2 * Math.sin(theta)
19747 };
19748 pos[_val.node.id()] = p;
19749 }
19750 } // position the nodes
19751
19752
19753 eles.nodes().layoutPositions(this, options, function (ele) {
19754 var id = ele.id();
19755 return pos[id];
19756 });
19757 return this; // chaining
19758};
19759
19760/*
19761The CoSE layout was written by Gerardo Huck.
19762https://www.linkedin.com/in/gerardohuck/
19763
19764Based on the following article:
19765http://dl.acm.org/citation.cfm?id=1498047
19766
19767Modifications tracked on Github.
19768*/
19769var DEBUG;
19770/**
19771 * @brief : default layout options
19772 */
19773
19774var defaults$4 = {
19775 // Called on `layoutready`
19776 ready: function ready() {},
19777 // Called on `layoutstop`
19778 stop: function stop() {},
19779 // Whether to animate while running the layout
19780 // true : Animate continuously as the layout is running
19781 // false : Just show the end result
19782 // 'end' : Animate with the end result, from the initial positions to the end positions
19783 animate: true,
19784 // Easing of the animation for animate:'end'
19785 animationEasing: undefined,
19786 // The duration of the animation for animate:'end'
19787 animationDuration: undefined,
19788 // A function that determines whether the node should be animated
19789 // All nodes animated by default on animate enabled
19790 // Non-animated nodes are positioned immediately when the layout starts
19791 animateFilter: function animateFilter(node, i) {
19792 return true;
19793 },
19794 // The layout animates only after this many milliseconds for animate:true
19795 // (prevents flashing on fast runs)
19796 animationThreshold: 250,
19797 // Number of iterations between consecutive screen positions update
19798 refresh: 20,
19799 // Whether to fit the network view after when done
19800 fit: true,
19801 // Padding on fit
19802 padding: 30,
19803 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19804 boundingBox: undefined,
19805 // Excludes the label when calculating node bounding boxes for the layout algorithm
19806 nodeDimensionsIncludeLabels: false,
19807 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19808 randomize: false,
19809 // Extra spacing between components in non-compound graphs
19810 componentSpacing: 40,
19811 // Node repulsion (non overlapping) multiplier
19812 nodeRepulsion: function nodeRepulsion(node) {
19813 return 2048;
19814 },
19815 // Node repulsion (overlapping) multiplier
19816 nodeOverlap: 4,
19817 // Ideal edge (non nested) length
19818 idealEdgeLength: function idealEdgeLength(edge) {
19819 return 32;
19820 },
19821 // Divisor to compute edge forces
19822 edgeElasticity: function edgeElasticity(edge) {
19823 return 32;
19824 },
19825 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19826 nestingFactor: 1.2,
19827 // Gravity force (constant)
19828 gravity: 1,
19829 // Maximum number of iterations to perform
19830 numIter: 1000,
19831 // Initial temperature (maximum node displacement)
19832 initialTemp: 1000,
19833 // Cooling factor (how the temperature is reduced between consecutive iterations
19834 coolingFactor: 0.99,
19835 // Lower temperature threshold (below this point the layout will end)
19836 minTemp: 1.0
19837};
19838/**
19839 * @brief : constructor
19840 * @arg options : object containing layout options
19841 */
19842
19843function CoseLayout(options) {
19844 this.options = extend({}, defaults$4, options);
19845 this.options.layout = this;
19846}
19847/**
19848 * @brief : runs the layout
19849 */
19850
19851
19852CoseLayout.prototype.run = function () {
19853 var options = this.options;
19854 var cy = options.cy;
19855 var layout = this;
19856 layout.stopped = false;
19857
19858 if (options.animate === true || options.animate === false) {
19859 layout.emit({
19860 type: 'layoutstart',
19861 layout: layout
19862 });
19863 } // Set DEBUG - Global variable
19864
19865
19866 if (true === options.debug) {
19867 DEBUG = true;
19868 } else {
19869 DEBUG = false;
19870 } // Initialize layout info
19871
19872
19873 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19874
19875 if (DEBUG) {
19876 printLayoutInfo(layoutInfo);
19877 } // If required, randomize node positions
19878
19879
19880 if (options.randomize) {
19881 randomizePositions(layoutInfo);
19882 }
19883
19884 var startTime = performanceNow();
19885
19886 var refresh = function refresh() {
19887 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19888
19889 if (true === options.fit) {
19890 cy.fit(options.padding);
19891 }
19892 };
19893
19894 var mainLoop = function mainLoop(i) {
19895 if (layout.stopped || i >= options.numIter) {
19896 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19897 return false;
19898 } // Do one step in the phisical simulation
19899
19900
19901 step(layoutInfo, options); // Update temperature
19902
19903 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19904
19905 if (layoutInfo.temperature < options.minTemp) {
19906 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19907 return false;
19908 }
19909
19910 return true;
19911 };
19912
19913 var done = function done() {
19914 if (options.animate === true || options.animate === false) {
19915 refresh(); // Layout has finished
19916
19917 layout.one('layoutstop', options.stop);
19918 layout.emit({
19919 type: 'layoutstop',
19920 layout: layout
19921 });
19922 } else {
19923 var nodes = options.eles.nodes();
19924 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19925 nodes.layoutPositions(layout, options, getScaledPos);
19926 }
19927 };
19928
19929 var i = 0;
19930 var loopRet = true;
19931
19932 if (options.animate === true) {
19933 var frame = function frame() {
19934 var f = 0;
19935
19936 while (loopRet && f < options.refresh) {
19937 loopRet = mainLoop(i);
19938 i++;
19939 f++;
19940 }
19941
19942 if (!loopRet) {
19943 // it's done
19944 separateComponents(layoutInfo, options);
19945 done();
19946 } else {
19947 var now = performanceNow();
19948
19949 if (now - startTime >= options.animationThreshold) {
19950 refresh();
19951 }
19952
19953 requestAnimationFrame(frame);
19954 }
19955 };
19956
19957 frame();
19958 } else {
19959 while (loopRet) {
19960 loopRet = mainLoop(i);
19961 i++;
19962 }
19963
19964 separateComponents(layoutInfo, options);
19965 done();
19966 }
19967
19968 return this; // chaining
19969};
19970/**
19971 * @brief : called on continuous layouts to stop them before they finish
19972 */
19973
19974
19975CoseLayout.prototype.stop = function () {
19976 this.stopped = true;
19977
19978 if (this.thread) {
19979 this.thread.stop();
19980 }
19981
19982 this.emit('layoutstop');
19983 return this; // chaining
19984};
19985
19986CoseLayout.prototype.destroy = function () {
19987 if (this.thread) {
19988 this.thread.stop();
19989 }
19990
19991 return this; // chaining
19992};
19993/**
19994 * @brief : Creates an object which is contains all the data
19995 * used in the layout process
19996 * @arg cy : cytoscape.js object
19997 * @return : layoutInfo object initialized
19998 */
19999
20000
20001var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
20002 // Shortcut
20003 var edges = options.eles.edges();
20004 var nodes = options.eles.nodes();
20005 var layoutInfo = {
20006 isCompound: cy.hasCompoundNodes(),
20007 layoutNodes: [],
20008 idToIndex: {},
20009 nodeSize: nodes.size(),
20010 graphSet: [],
20011 indexToGraph: [],
20012 layoutEdges: [],
20013 edgeSize: edges.size(),
20014 temperature: options.initialTemp,
20015 clientWidth: cy.width(),
20016 clientHeight: cy.width(),
20017 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
20018 x1: 0,
20019 y1: 0,
20020 w: cy.width(),
20021 h: cy.height()
20022 })
20023 };
20024 var components = options.eles.components();
20025 var id2cmptId = {};
20026
20027 for (var i = 0; i < components.length; i++) {
20028 var component = components[i];
20029
20030 for (var j = 0; j < component.length; j++) {
20031 var node = component[j];
20032 id2cmptId[node.id()] = i;
20033 }
20034 } // Iterate over all nodes, creating layout nodes
20035
20036
20037 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20038 var n = nodes[i];
20039 var nbb = n.layoutDimensions(options);
20040 var tempNode = {};
20041 tempNode.isLocked = n.locked();
20042 tempNode.id = n.data('id');
20043 tempNode.parentId = n.data('parent');
20044 tempNode.cmptId = id2cmptId[n.id()];
20045 tempNode.children = [];
20046 tempNode.positionX = n.position('x');
20047 tempNode.positionY = n.position('y');
20048 tempNode.offsetX = 0;
20049 tempNode.offsetY = 0;
20050 tempNode.height = nbb.w;
20051 tempNode.width = nbb.h;
20052 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20053 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20054 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20055 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20056 tempNode.padLeft = parseFloat(n.style('padding'));
20057 tempNode.padRight = parseFloat(n.style('padding'));
20058 tempNode.padTop = parseFloat(n.style('padding'));
20059 tempNode.padBottom = parseFloat(n.style('padding')); // forces
20060
20061 tempNode.nodeRepulsion = fn$6(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
20062
20063 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
20064
20065 layoutInfo.idToIndex[tempNode.id] = i;
20066 } // Inline implementation of a queue, used for traversing the graph in BFS order
20067
20068
20069 var queue = [];
20070 var start = 0; // Points to the start the queue
20071
20072 var end = -1; // Points to the end of the queue
20073
20074 var tempGraph = []; // Second pass to add child information and
20075 // initialize queue for hierarchical traversal
20076
20077 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20078 var n = layoutInfo.layoutNodes[i];
20079 var p_id = n.parentId; // Check if node n has a parent node
20080
20081 if (null != p_id) {
20082 // Add node Id to parent's list of children
20083 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20084 } else {
20085 // If a node doesn't have a parent, then it's in the root graph
20086 queue[++end] = n.id;
20087 tempGraph.push(n.id);
20088 }
20089 } // Add root graph to graphSet
20090
20091
20092 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
20093
20094 while (start <= end) {
20095 // Get the node to visit and remove it from queue
20096 var node_id = queue[start++];
20097 var node_ix = layoutInfo.idToIndex[node_id];
20098 var node = layoutInfo.layoutNodes[node_ix];
20099 var children = node.children;
20100
20101 if (children.length > 0) {
20102 // Add children nodes as a new graph to graph set
20103 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
20104
20105 for (var i = 0; i < children.length; i++) {
20106 queue[++end] = children[i];
20107 }
20108 }
20109 } // Create indexToGraph map
20110
20111
20112 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20113 var graph = layoutInfo.graphSet[i];
20114
20115 for (var j = 0; j < graph.length; j++) {
20116 var index = layoutInfo.idToIndex[graph[j]];
20117 layoutInfo.indexToGraph[index] = i;
20118 }
20119 } // Iterate over all edges, creating Layout Edges
20120
20121
20122 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20123 var e = edges[i];
20124 var tempEdge = {};
20125 tempEdge.id = e.data('id');
20126 tempEdge.sourceId = e.data('source');
20127 tempEdge.targetId = e.data('target'); // Compute ideal length
20128
20129 var idealLength = fn$6(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20130 var elasticity = fn$6(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
20131
20132 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20133 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20134 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20135 var targetGraph = layoutInfo.indexToGraph[targetIx];
20136
20137 if (sourceGraph != targetGraph) {
20138 // Find lowest common graph ancestor
20139 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
20140
20141 var lcaGraph = layoutInfo.graphSet[lca];
20142 var depth = 0; // Source depth
20143
20144 var tempNode = layoutInfo.layoutNodes[sourceIx];
20145
20146 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20147 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20148 depth++;
20149 } // Target depth
20150
20151
20152 tempNode = layoutInfo.layoutNodes[targetIx];
20153
20154 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20155 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20156 depth++;
20157 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20158 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20159 // ". Depth: " + depth);
20160 // Update idealLength
20161
20162
20163 idealLength *= depth * options.nestingFactor;
20164 }
20165
20166 tempEdge.idealLength = idealLength;
20167 tempEdge.elasticity = elasticity;
20168 layoutInfo.layoutEdges.push(tempEdge);
20169 } // Finally, return layoutInfo object
20170
20171
20172 return layoutInfo;
20173};
20174/**
20175 * @brief : This function finds the index of the lowest common
20176 * graph ancestor between 2 nodes in the subtree
20177 * (from the graph hierarchy induced tree) whose
20178 * root is graphIx
20179 *
20180 * @arg node1: node1's ID
20181 * @arg node2: node2's ID
20182 * @arg layoutInfo: layoutInfo object
20183 *
20184 */
20185
20186
20187var findLCA = function findLCA(node1, node2, layoutInfo) {
20188 // Find their common ancester, starting from the root graph
20189 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20190
20191 if (2 > res.count) {
20192 // If aux function couldn't find the common ancester,
20193 // then it is the root graph
20194 return 0;
20195 } else {
20196 return res.graph;
20197 }
20198};
20199/**
20200 * @brief : Auxiliary function used for LCA computation
20201 *
20202 * @arg node1 : node1's ID
20203 * @arg node2 : node2's ID
20204 * @arg graphIx : subgraph index
20205 * @arg layoutInfo : layoutInfo object
20206 *
20207 * @return : object of the form {count: X, graph: Y}, where:
20208 * X is the number of ancesters (max: 2) found in
20209 * graphIx (and it's subgraphs),
20210 * Y is the graph index of the lowest graph containing
20211 * all X nodes
20212 */
20213
20214
20215var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20216 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20217
20218 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20219 return {
20220 count: 2,
20221 graph: graphIx
20222 };
20223 } // Make recursive calls for all subgraphs
20224
20225
20226 var c = 0;
20227
20228 for (var i = 0; i < graph.length; i++) {
20229 var nodeId = graph[i];
20230 var nodeIx = layoutInfo.idToIndex[nodeId];
20231 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20232
20233 if (0 === children.length) {
20234 continue;
20235 }
20236
20237 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20238 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20239
20240 if (0 === result.count) {
20241 // Neither node1 nor node2 are present in this subgraph
20242 continue;
20243 } else if (1 === result.count) {
20244 // One of (node1, node2) is present in this subgraph
20245 c++;
20246
20247 if (2 === c) {
20248 // We've already found both nodes, no need to keep searching
20249 break;
20250 }
20251 } else {
20252 // Both nodes are present in this subgraph
20253 return result;
20254 }
20255 }
20256
20257 return {
20258 count: c,
20259 graph: graphIx
20260 };
20261};
20262/**
20263 * @brief: printsLayoutInfo into js console
20264 * Only used for debbuging
20265 */
20266
20267
20268var printLayoutInfo;
20269/**
20270 * @brief : Randomizes the position of all nodes
20271 */
20272
20273
20274var randomizePositions = function randomizePositions(layoutInfo, cy) {
20275 var width = layoutInfo.clientWidth;
20276 var height = layoutInfo.clientHeight;
20277
20278 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20279 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20280
20281 if (0 === n.children.length && !n.isLocked) {
20282 n.positionX = Math.random() * width;
20283 n.positionY = Math.random() * height;
20284 }
20285 }
20286};
20287
20288var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20289 var bb = layoutInfo.boundingBox;
20290 var coseBB = {
20291 x1: Infinity,
20292 x2: -Infinity,
20293 y1: Infinity,
20294 y2: -Infinity
20295 };
20296
20297 if (options.boundingBox) {
20298 nodes.forEach(function (node) {
20299 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20300 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20301 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20302 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20303 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20304 });
20305 coseBB.w = coseBB.x2 - coseBB.x1;
20306 coseBB.h = coseBB.y2 - coseBB.y1;
20307 }
20308
20309 return function (ele, i) {
20310 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20311
20312 if (options.boundingBox) {
20313 // then add extra bounding box constraint
20314 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20315 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20316 return {
20317 x: bb.x1 + pctX * bb.w,
20318 y: bb.y1 + pctY * bb.h
20319 };
20320 } else {
20321 return {
20322 x: lnode.positionX,
20323 y: lnode.positionY
20324 };
20325 }
20326 };
20327};
20328/**
20329 * @brief : Updates the positions of nodes in the network
20330 * @arg layoutInfo : LayoutInfo object
20331 * @arg cy : Cytoscape object
20332 * @arg options : Layout options
20333 */
20334
20335
20336var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20337 // var s = 'Refreshing positions';
20338 // logDebug(s);
20339 var layout = options.layout;
20340 var nodes = options.eles.nodes();
20341 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20342 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20343
20344 if (true !== layoutInfo.ready) {
20345 // s = 'Triggering layoutready';
20346 // logDebug(s);
20347 layoutInfo.ready = true;
20348 layout.one('layoutready', options.ready);
20349 layout.emit({
20350 type: 'layoutready',
20351 layout: this
20352 });
20353 }
20354};
20355/**
20356 * @brief : Logs a debug message in JS console, if DEBUG is ON
20357 */
20358// var logDebug = function(text) {
20359// if (DEBUG) {
20360// console.debug(text);
20361// }
20362// };
20363
20364/**
20365 * @brief : Performs one iteration of the physical simulation
20366 * @arg layoutInfo : LayoutInfo object already initialized
20367 * @arg cy : Cytoscape object
20368 * @arg options : Layout options
20369 */
20370
20371
20372var step = function step(layoutInfo, options, _step) {
20373 // var s = "\n\n###############################";
20374 // s += "\nSTEP: " + step;
20375 // s += "\n###############################\n";
20376 // logDebug(s);
20377 // Calculate node repulsions
20378 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20379
20380 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20381
20382 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20383
20384 propagateForces(layoutInfo); // Update positions based on calculated forces
20385
20386 updatePositions(layoutInfo);
20387};
20388/**
20389 * @brief : Computes the node repulsion forces
20390 */
20391
20392
20393var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20394 // Go through each of the graphs in graphSet
20395 // Nodes only repel each other if they belong to the same graph
20396 // var s = 'calculateNodeForces';
20397 // logDebug(s);
20398 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20399 var graph = layoutInfo.graphSet[i];
20400 var numNodes = graph.length; // s = "Set: " + graph.toString();
20401 // logDebug(s);
20402 // Now get all the pairs of nodes
20403 // Only get each pair once, (A, B) = (B, A)
20404
20405 for (var j = 0; j < numNodes; j++) {
20406 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20407
20408 for (var k = j + 1; k < numNodes; k++) {
20409 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20410 nodeRepulsion(node1, node2, layoutInfo, options);
20411 }
20412 }
20413 }
20414};
20415
20416var randomDistance = function randomDistance(max) {
20417 return -max + 2 * max * Math.random();
20418};
20419/**
20420 * @brief : Compute the node repulsion forces between a pair of nodes
20421 */
20422
20423
20424var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20425 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20426 var cmptId1 = node1.cmptId;
20427 var cmptId2 = node2.cmptId;
20428
20429 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20430 return;
20431 } // Get direction of line connecting both node centers
20432
20433
20434 var directionX = node2.positionX - node1.positionX;
20435 var directionY = node2.positionY - node1.positionY;
20436 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20437 // If both centers are the same, apply a random force
20438
20439 if (0 === directionX && 0 === directionY) {
20440 directionX = randomDistance(maxRandDist);
20441 directionY = randomDistance(maxRandDist);
20442 }
20443
20444 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20445
20446 if (overlap > 0) {
20447 // s += "\nNodes DO overlap.";
20448 // s += "\nOverlap: " + overlap;
20449 // If nodes overlap, repulsion force is proportional
20450 // to the overlap
20451 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20452
20453 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20454
20455 var forceX = force * directionX / distance;
20456 var forceY = force * directionY / distance;
20457 } else {
20458 // s += "\nNodes do NOT overlap.";
20459 // If there's no overlap, force is inversely proportional
20460 // to squared distance
20461 // Get clipping points for both nodes
20462 var point1 = findClippingPoint(node1, directionX, directionY);
20463 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20464
20465 var distanceX = point2.x - point1.x;
20466 var distanceY = point2.y - point1.y;
20467 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20468 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20469 // Compute the module and components of the force vector
20470
20471 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20472 var forceX = force * distanceX / distance;
20473 var forceY = force * distanceY / distance;
20474 } // Apply force
20475
20476
20477 if (!node1.isLocked) {
20478 node1.offsetX -= forceX;
20479 node1.offsetY -= forceY;
20480 }
20481
20482 if (!node2.isLocked) {
20483 node2.offsetX += forceX;
20484 node2.offsetY += forceY;
20485 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20486 // logDebug(s);
20487
20488
20489 return;
20490};
20491/**
20492 * @brief : Determines whether two nodes overlap or not
20493 * @return : Amount of overlapping (0 => no overlap)
20494 */
20495
20496
20497var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20498 if (dX > 0) {
20499 var overlapX = node1.maxX - node2.minX;
20500 } else {
20501 var overlapX = node2.maxX - node1.minX;
20502 }
20503
20504 if (dY > 0) {
20505 var overlapY = node1.maxY - node2.minY;
20506 } else {
20507 var overlapY = node2.maxY - node1.minY;
20508 }
20509
20510 if (overlapX >= 0 && overlapY >= 0) {
20511 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20512 } else {
20513 return 0;
20514 }
20515};
20516/**
20517 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20518 * the rectangular bounding box of it's source/target node
20519 */
20520
20521
20522var findClippingPoint = function findClippingPoint(node, dX, dY) {
20523 // Shorcuts
20524 var X = node.positionX;
20525 var Y = node.positionY;
20526 var H = node.height || 1;
20527 var W = node.width || 1;
20528 var dirSlope = dY / dX;
20529 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20530 // " . Height: " + H + ", Width: " + W +
20531 // "\nDirection " + dX + ", " + dY;
20532 //
20533 // Compute intersection
20534
20535 var res = {}; // Case: Vertical direction (up)
20536
20537 if (0 === dX && 0 < dY) {
20538 res.x = X; // s += "\nUp direction";
20539
20540 res.y = Y + H / 2;
20541 return res;
20542 } // Case: Vertical direction (down)
20543
20544
20545 if (0 === dX && 0 > dY) {
20546 res.x = X;
20547 res.y = Y + H / 2; // s += "\nDown direction";
20548
20549 return res;
20550 } // Case: Intersects the right border
20551
20552
20553 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20554 res.x = X + W / 2;
20555 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20556
20557 return res;
20558 } // Case: Intersects the left border
20559
20560
20561 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20562 res.x = X - W / 2;
20563 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20564
20565 return res;
20566 } // Case: Intersects the top border
20567
20568
20569 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20570 res.x = X + H * dX / 2 / dY;
20571 res.y = Y + H / 2; // s += "\nTop border";
20572
20573 return res;
20574 } // Case: Intersects the bottom border
20575
20576
20577 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20578 res.x = X - H * dX / 2 / dY;
20579 res.y = Y - H / 2; // s += "\nBottom border";
20580
20581 return res;
20582 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20583 // logDebug(s);
20584
20585
20586 return res;
20587};
20588/**
20589 * @brief : Calculates all edge forces
20590 */
20591
20592
20593var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20594 // Iterate over all edges
20595 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20596 // Get edge, source & target nodes
20597 var edge = layoutInfo.layoutEdges[i];
20598 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20599 var source = layoutInfo.layoutNodes[sourceIx];
20600 var targetIx = layoutInfo.idToIndex[edge.targetId];
20601 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20602
20603 var directionX = target.positionX - source.positionX;
20604 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20605 // A random force has already been applied as node repulsion
20606
20607 if (0 === directionX && 0 === directionY) {
20608 continue;
20609 } // Get clipping points for both nodes
20610
20611
20612 var point1 = findClippingPoint(source, directionX, directionY);
20613 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20614 var lx = point2.x - point1.x;
20615 var ly = point2.y - point1.y;
20616 var l = Math.sqrt(lx * lx + ly * ly);
20617 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20618
20619 if (0 !== l) {
20620 var forceX = force * lx / l;
20621 var forceY = force * ly / l;
20622 } else {
20623 var forceX = 0;
20624 var forceY = 0;
20625 } // Add this force to target and source nodes
20626
20627
20628 if (!source.isLocked) {
20629 source.offsetX += forceX;
20630 source.offsetY += forceY;
20631 }
20632
20633 if (!target.isLocked) {
20634 target.offsetX -= forceX;
20635 target.offsetY -= forceY;
20636 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20637 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20638 // logDebug(s);
20639
20640 }
20641};
20642/**
20643 * @brief : Computes gravity forces for all nodes
20644 */
20645
20646
20647var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20648 if (options.gravity === 0) {
20649 return;
20650 }
20651
20652 var distThreshold = 1; // var s = 'calculateGravityForces';
20653 // logDebug(s);
20654
20655 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20656 var graph = layoutInfo.graphSet[i];
20657 var numNodes = graph.length; // s = "Set: " + graph.toString();
20658 // logDebug(s);
20659 // Compute graph center
20660
20661 if (0 === i) {
20662 var centerX = layoutInfo.clientHeight / 2;
20663 var centerY = layoutInfo.clientWidth / 2;
20664 } else {
20665 // Get Parent node for this graph, and use its position as center
20666 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20667 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20668 var centerX = parent.positionX;
20669 var centerY = parent.positionY;
20670 } // s = "Center found at: " + centerX + ", " + centerY;
20671 // logDebug(s);
20672 // Apply force to all nodes in graph
20673
20674
20675 for (var j = 0; j < numNodes; j++) {
20676 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20677
20678 if (node.isLocked) {
20679 continue;
20680 }
20681
20682 var dx = centerX - node.positionX;
20683 var dy = centerY - node.positionY;
20684 var d = Math.sqrt(dx * dx + dy * dy);
20685
20686 if (d > distThreshold) {
20687 var fx = options.gravity * dx / d;
20688 var fy = options.gravity * dy / d;
20689 node.offsetX += fx;
20690 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20691 } // logDebug(s);
20692
20693 }
20694 }
20695};
20696/**
20697 * @brief : This function propagates the existing offsets from
20698 * parent nodes to its descendents.
20699 * @arg layoutInfo : layoutInfo Object
20700 * @arg cy : cytoscape Object
20701 * @arg options : Layout options
20702 */
20703
20704
20705var propagateForces = function propagateForces(layoutInfo, options) {
20706 // Inline implementation of a queue, used for traversing the graph in BFS order
20707 var queue = [];
20708 var start = 0; // Points to the start the queue
20709
20710 var end = -1; // Points to the end of the queue
20711 // logDebug('propagateForces');
20712 // Start by visiting the nodes in the root graph
20713
20714 queue.push.apply(queue, layoutInfo.graphSet[0]);
20715 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20716
20717 while (start <= end) {
20718 // Get the node to visit and remove it from queue
20719 var nodeId = queue[start++];
20720 var nodeIndex = layoutInfo.idToIndex[nodeId];
20721 var node = layoutInfo.layoutNodes[nodeIndex];
20722 var children = node.children; // We only need to process the node if it's compound
20723
20724 if (0 < children.length && !node.isLocked) {
20725 var offX = node.offsetX;
20726 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20727 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20728 // s += "\n Children: " + children.toString();
20729 // logDebug(s);
20730
20731 for (var i = 0; i < children.length; i++) {
20732 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20733
20734 childNode.offsetX += offX;
20735 childNode.offsetY += offY; // Add children to queue to be visited
20736
20737 queue[++end] = children[i];
20738 } // Reset parent offsets
20739
20740
20741 node.offsetX = 0;
20742 node.offsetY = 0;
20743 }
20744 }
20745};
20746/**
20747 * @brief : Updates the layout model positions, based on
20748 * the accumulated forces
20749 */
20750
20751
20752var updatePositions = function updatePositions(layoutInfo, options) {
20753 // var s = 'Updating positions';
20754 // logDebug(s);
20755 // Reset boundaries for compound nodes
20756 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20757 var n = layoutInfo.layoutNodes[i];
20758
20759 if (0 < n.children.length) {
20760 // logDebug("Resetting boundaries of compound node: " + n.id);
20761 n.maxX = undefined;
20762 n.minX = undefined;
20763 n.maxY = undefined;
20764 n.minY = undefined;
20765 }
20766 }
20767
20768 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20769 var n = layoutInfo.layoutNodes[i];
20770
20771 if (0 < n.children.length || n.isLocked) {
20772 // No need to set compound or locked node position
20773 // logDebug("Skipping position update of node: " + n.id);
20774 continue;
20775 } // s = "Node: " + n.id + " Previous position: (" +
20776 // n.positionX + ", " + n.positionY + ").";
20777 // Limit displacement in order to improve stability
20778
20779
20780 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20781 n.positionX += tempForce.x;
20782 n.positionY += tempForce.y;
20783 n.offsetX = 0;
20784 n.offsetY = 0;
20785 n.minX = n.positionX - n.width;
20786 n.maxX = n.positionX + n.width;
20787 n.minY = n.positionY - n.height;
20788 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20789 // logDebug(s);
20790 // Update ancestry boudaries
20791
20792 updateAncestryBoundaries(n, layoutInfo);
20793 } // Update size, position of compund nodes
20794
20795
20796 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20797 var n = layoutInfo.layoutNodes[i];
20798
20799 if (0 < n.children.length && !n.isLocked) {
20800 n.positionX = (n.maxX + n.minX) / 2;
20801 n.positionY = (n.maxY + n.minY) / 2;
20802 n.width = n.maxX - n.minX;
20803 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20804 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20805 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20806 // logDebug(s);
20807 }
20808 }
20809};
20810/**
20811 * @brief : Limits a force (forceX, forceY) to be not
20812 * greater (in modulo) than max.
20813 8 Preserves force direction.
20814 */
20815
20816
20817var limitForce = function limitForce(forceX, forceY, max) {
20818 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20819 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20820
20821 if (force > max) {
20822 var res = {
20823 x: max * forceX / force,
20824 y: max * forceY / force
20825 };
20826 } else {
20827 var res = {
20828 x: forceX,
20829 y: forceY
20830 };
20831 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20832 // logDebug(s);
20833
20834
20835 return res;
20836};
20837/**
20838 * @brief : Function used for keeping track of compound node
20839 * sizes, since they should bound all their subnodes.
20840 */
20841
20842
20843var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20844 // var s = "Propagating new position/size of node " + node.id;
20845 var parentId = node.parentId;
20846
20847 if (null == parentId) {
20848 // If there's no parent, we are done
20849 // s += ". No parent node.";
20850 // logDebug(s);
20851 return;
20852 } // Get Parent Node
20853
20854
20855 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20856 var flag = false; // MaxX
20857
20858 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20859 p.maxX = node.maxX + p.padRight;
20860 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20861 } // MinX
20862
20863
20864 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20865 p.minX = node.minX - p.padLeft;
20866 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20867 } // MaxY
20868
20869
20870 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20871 p.maxY = node.maxY + p.padBottom;
20872 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20873 } // MinY
20874
20875
20876 if (null == p.minY || node.minY - p.padTop < p.minY) {
20877 p.minY = node.minY - p.padTop;
20878 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20879 } // If updated boundaries, propagate changes upward
20880
20881
20882 if (flag) {
20883 // logDebug(s);
20884 return updateAncestryBoundaries(p, layoutInfo);
20885 } // s += ". No changes in boundaries/position of parent node " + p.id;
20886 // logDebug(s);
20887
20888
20889 return;
20890};
20891
20892var separateComponents = function separateComponents(layoutInfo, options) {
20893 var nodes = layoutInfo.layoutNodes;
20894 var components = [];
20895
20896 for (var i = 0; i < nodes.length; i++) {
20897 var node = nodes[i];
20898 var cid = node.cmptId;
20899 var component = components[cid] = components[cid] || [];
20900 component.push(node);
20901 }
20902
20903 var totalA = 0;
20904
20905 for (var i = 0; i < components.length; i++) {
20906 var c = components[i];
20907
20908 if (!c) {
20909 continue;
20910 }
20911
20912 c.x1 = Infinity;
20913 c.x2 = -Infinity;
20914 c.y1 = Infinity;
20915 c.y2 = -Infinity;
20916
20917 for (var j = 0; j < c.length; j++) {
20918 var n = c[j];
20919 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20920 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20921 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20922 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20923 }
20924
20925 c.w = c.x2 - c.x1;
20926 c.h = c.y2 - c.y1;
20927 totalA += c.w * c.h;
20928 }
20929
20930 components.sort(function (c1, c2) {
20931 return c2.w * c2.h - c1.w * c1.h;
20932 });
20933 var x = 0;
20934 var y = 0;
20935 var usedW = 0;
20936 var rowH = 0;
20937 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20938
20939 for (var i = 0; i < components.length; i++) {
20940 var c = components[i];
20941
20942 if (!c) {
20943 continue;
20944 }
20945
20946 for (var j = 0; j < c.length; j++) {
20947 var n = c[j];
20948
20949 if (!n.isLocked) {
20950 n.positionX += x - c.x1;
20951 n.positionY += y - c.y1;
20952 }
20953 }
20954
20955 x += c.w + options.componentSpacing;
20956 usedW += c.w + options.componentSpacing;
20957 rowH = Math.max(rowH, c.h);
20958
20959 if (usedW > maxRowW) {
20960 y += rowH + options.componentSpacing;
20961 x = 0;
20962 usedW = 0;
20963 rowH = 0;
20964 }
20965 }
20966};
20967
20968var defaults$3 = {
20969 fit: true,
20970 // whether to fit the viewport to the graph
20971 padding: 30,
20972 // padding used on fit
20973 boundingBox: undefined,
20974 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20975 avoidOverlap: true,
20976 // prevents node overlap, may overflow boundingBox if not enough space
20977 avoidOverlapPadding: 10,
20978 // extra spacing around nodes when avoidOverlap: true
20979 nodeDimensionsIncludeLabels: false,
20980 // Excludes the label when calculating node bounding boxes for the layout algorithm
20981 spacingFactor: undefined,
20982 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20983 condense: false,
20984 // uses all available space on false, uses minimal space on true
20985 rows: undefined,
20986 // force num of rows in the grid
20987 cols: undefined,
20988 // force num of columns in the grid
20989 position: function position(node) {},
20990 // returns { row, col } for element
20991 sort: undefined,
20992 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20993 animate: false,
20994 // whether to transition the node positions
20995 animationDuration: 500,
20996 // duration of animation in ms if enabled
20997 animationEasing: undefined,
20998 // easing of animation if enabled
20999 animateFilter: function animateFilter(node, i) {
21000 return true;
21001 },
21002 // 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
21003 ready: undefined,
21004 // callback on layoutready
21005 stop: undefined,
21006 // callback on layoutstop
21007 transform: function transform(node, position) {
21008 return position;
21009 } // transform a given node position. Useful for changing flow direction in discrete layouts
21010
21011};
21012
21013function GridLayout(options) {
21014 this.options = extend({}, defaults$3, options);
21015}
21016
21017GridLayout.prototype.run = function () {
21018 var params = this.options;
21019 var options = params;
21020 var cy = params.cy;
21021 var eles = options.eles;
21022 var nodes = eles.nodes().not(':parent');
21023
21024 if (options.sort) {
21025 nodes = nodes.sort(options.sort);
21026 }
21027
21028 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21029 x1: 0,
21030 y1: 0,
21031 w: cy.width(),
21032 h: cy.height()
21033 });
21034
21035 if (bb.h === 0 || bb.w === 0) {
21036 eles.nodes().layoutPositions(this, options, function (ele) {
21037 return {
21038 x: bb.x1,
21039 y: bb.y1
21040 };
21041 });
21042 } else {
21043 // width/height * splits^2 = cells where splits is number of times to split width
21044 var cells = nodes.size();
21045 var splits = Math.sqrt(cells * bb.h / bb.w);
21046 var rows = Math.round(splits);
21047 var cols = Math.round(bb.w / bb.h * splits);
21048
21049 var small = function small(val) {
21050 if (val == null) {
21051 return Math.min(rows, cols);
21052 } else {
21053 var min = Math.min(rows, cols);
21054
21055 if (min == rows) {
21056 rows = val;
21057 } else {
21058 cols = val;
21059 }
21060 }
21061 };
21062
21063 var large = function large(val) {
21064 if (val == null) {
21065 return Math.max(rows, cols);
21066 } else {
21067 var max = Math.max(rows, cols);
21068
21069 if (max == rows) {
21070 rows = val;
21071 } else {
21072 cols = val;
21073 }
21074 }
21075 };
21076
21077 var oRows = options.rows;
21078 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
21079
21080 if (oRows != null && oCols != null) {
21081 rows = oRows;
21082 cols = oCols;
21083 } else if (oRows != null && oCols == null) {
21084 rows = oRows;
21085 cols = Math.ceil(cells / rows);
21086 } else if (oRows == null && oCols != null) {
21087 cols = oCols;
21088 rows = Math.ceil(cells / cols);
21089 } // otherwise use the automatic values and adjust accordingly
21090 // if rounding was up, see if we can reduce rows or columns
21091 else if (cols * rows > cells) {
21092 var sm = small();
21093 var lg = large(); // reducing the small side takes away the most cells, so try it first
21094
21095 if ((sm - 1) * lg >= cells) {
21096 small(sm - 1);
21097 } else if ((lg - 1) * sm >= cells) {
21098 large(lg - 1);
21099 }
21100 } else {
21101 // if rounding was too low, add rows or columns
21102 while (cols * rows < cells) {
21103 var _sm = small();
21104
21105 var _lg = large(); // try to add to larger side first (adds less in multiplication)
21106
21107
21108 if ((_lg + 1) * _sm >= cells) {
21109 large(_lg + 1);
21110 } else {
21111 small(_sm + 1);
21112 }
21113 }
21114 }
21115
21116 var cellWidth = bb.w / cols;
21117 var cellHeight = bb.h / rows;
21118
21119 if (options.condense) {
21120 cellWidth = 0;
21121 cellHeight = 0;
21122 }
21123
21124 if (options.avoidOverlap) {
21125 for (var i = 0; i < nodes.length; i++) {
21126 var node = nodes[i];
21127 var pos = node._private.position;
21128
21129 if (pos.x == null || pos.y == null) {
21130 // for bb
21131 pos.x = 0;
21132 pos.y = 0;
21133 }
21134
21135 var nbb = node.layoutDimensions(options);
21136 var p = options.avoidOverlapPadding;
21137 var w = nbb.w + p;
21138 var h = nbb.h + p;
21139 cellWidth = Math.max(cellWidth, w);
21140 cellHeight = Math.max(cellHeight, h);
21141 }
21142 }
21143
21144 var cellUsed = {}; // e.g. 'c-0-2' => true
21145
21146 var used = function used(row, col) {
21147 return cellUsed['c-' + row + '-' + col] ? true : false;
21148 };
21149
21150 var use = function use(row, col) {
21151 cellUsed['c-' + row + '-' + col] = true;
21152 }; // to keep track of current cell position
21153
21154
21155 var row = 0;
21156 var col = 0;
21157
21158 var moveToNextCell = function moveToNextCell() {
21159 col++;
21160
21161 if (col >= cols) {
21162 col = 0;
21163 row++;
21164 }
21165 }; // get a cache of all the manual positions
21166
21167
21168 var id2manPos = {};
21169
21170 for (var _i = 0; _i < nodes.length; _i++) {
21171 var _node = nodes[_i];
21172 var rcPos = options.position(_node);
21173
21174 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21175 // must have at least row or col def'd
21176 var _pos = {
21177 row: rcPos.row,
21178 col: rcPos.col
21179 };
21180
21181 if (_pos.col === undefined) {
21182 // find unused col
21183 _pos.col = 0;
21184
21185 while (used(_pos.row, _pos.col)) {
21186 _pos.col++;
21187 }
21188 } else if (_pos.row === undefined) {
21189 // find unused row
21190 _pos.row = 0;
21191
21192 while (used(_pos.row, _pos.col)) {
21193 _pos.row++;
21194 }
21195 }
21196
21197 id2manPos[_node.id()] = _pos;
21198 use(_pos.row, _pos.col);
21199 }
21200 }
21201
21202 var getPos = function getPos(element, i) {
21203 var x, y;
21204
21205 if (element.locked() || element.isParent()) {
21206 return false;
21207 } // see if we have a manual position set
21208
21209
21210 var rcPos = id2manPos[element.id()];
21211
21212 if (rcPos) {
21213 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21214 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21215 } else {
21216 // otherwise set automatically
21217 while (used(row, col)) {
21218 moveToNextCell();
21219 }
21220
21221 x = col * cellWidth + cellWidth / 2 + bb.x1;
21222 y = row * cellHeight + cellHeight / 2 + bb.y1;
21223 use(row, col);
21224 moveToNextCell();
21225 }
21226
21227 return {
21228 x: x,
21229 y: y
21230 };
21231 };
21232
21233 nodes.layoutPositions(this, options, getPos);
21234 }
21235
21236 return this; // chaining
21237};
21238
21239var defaults$2 = {
21240 ready: function ready() {},
21241 // on layoutready
21242 stop: function stop() {} // on layoutstop
21243
21244}; // constructor
21245// options : object containing layout options
21246
21247function NullLayout(options) {
21248 this.options = extend({}, defaults$2, options);
21249} // runs the layout
21250
21251
21252NullLayout.prototype.run = function () {
21253 var options = this.options;
21254 var eles = options.eles; // elements to consider in the layout
21255
21256 var layout = this; // cy is automatically populated for us in the constructor
21257 // (disable eslint for next line as this serves as example layout code to external developers)
21258 // eslint-disable-next-line no-unused-vars
21259
21260 options.cy;
21261 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21262 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21263
21264 eles.nodes().positions(function () {
21265 return {
21266 x: 0,
21267 y: 0
21268 };
21269 }); // trigger layoutready when each node has had its position set at least once
21270
21271 layout.one('layoutready', options.ready);
21272 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21273
21274 layout.one('layoutstop', options.stop);
21275 layout.emit('layoutstop');
21276 return this; // chaining
21277}; // called on continuous layouts to stop them before they finish
21278
21279
21280NullLayout.prototype.stop = function () {
21281 return this; // chaining
21282};
21283
21284var defaults$1 = {
21285 positions: undefined,
21286 // map of (node id) => (position obj); or function(node){ return somPos; }
21287 zoom: undefined,
21288 // the zoom level to set (prob want fit = false if set)
21289 pan: undefined,
21290 // the pan level to set (prob want fit = false if set)
21291 fit: true,
21292 // whether to fit to viewport
21293 padding: 30,
21294 // padding on fit
21295 animate: false,
21296 // whether to transition the node positions
21297 animationDuration: 500,
21298 // duration of animation in ms if enabled
21299 animationEasing: undefined,
21300 // easing of animation if enabled
21301 animateFilter: function animateFilter(node, i) {
21302 return true;
21303 },
21304 // 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
21305 ready: undefined,
21306 // callback on layoutready
21307 stop: undefined,
21308 // callback on layoutstop
21309 transform: function transform(node, position) {
21310 return position;
21311 } // transform a given node position. Useful for changing flow direction in discrete layouts
21312
21313};
21314
21315function PresetLayout(options) {
21316 this.options = extend({}, defaults$1, options);
21317}
21318
21319PresetLayout.prototype.run = function () {
21320 var options = this.options;
21321 var eles = options.eles;
21322 var nodes = eles.nodes();
21323 var posIsFn = fn$6(options.positions);
21324
21325 function getPosition(node) {
21326 if (options.positions == null) {
21327 return copyPosition(node.position());
21328 }
21329
21330 if (posIsFn) {
21331 return options.positions(node);
21332 }
21333
21334 var pos = options.positions[node._private.data.id];
21335
21336 if (pos == null) {
21337 return null;
21338 }
21339
21340 return pos;
21341 }
21342
21343 nodes.layoutPositions(this, options, function (node, i) {
21344 var position = getPosition(node);
21345
21346 if (node.locked() || position == null) {
21347 return false;
21348 }
21349
21350 return position;
21351 });
21352 return this; // chaining
21353};
21354
21355var defaults = {
21356 fit: true,
21357 // whether to fit to viewport
21358 padding: 30,
21359 // fit padding
21360 boundingBox: undefined,
21361 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21362 animate: false,
21363 // whether to transition the node positions
21364 animationDuration: 500,
21365 // duration of animation in ms if enabled
21366 animationEasing: undefined,
21367 // easing of animation if enabled
21368 animateFilter: function animateFilter(node, i) {
21369 return true;
21370 },
21371 // 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
21372 ready: undefined,
21373 // callback on layoutready
21374 stop: undefined,
21375 // callback on layoutstop
21376 transform: function transform(node, position) {
21377 return position;
21378 } // transform a given node position. Useful for changing flow direction in discrete layouts
21379
21380};
21381
21382function RandomLayout(options) {
21383 this.options = extend({}, defaults, options);
21384}
21385
21386RandomLayout.prototype.run = function () {
21387 var options = this.options;
21388 var cy = options.cy;
21389 var eles = options.eles;
21390 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21391 x1: 0,
21392 y1: 0,
21393 w: cy.width(),
21394 h: cy.height()
21395 });
21396
21397 var getPos = function getPos(node, i) {
21398 return {
21399 x: bb.x1 + Math.round(Math.random() * bb.w),
21400 y: bb.y1 + Math.round(Math.random() * bb.h)
21401 };
21402 };
21403
21404 eles.nodes().layoutPositions(this, options, getPos);
21405 return this; // chaining
21406};
21407
21408var layout = [{
21409 name: 'breadthfirst',
21410 impl: BreadthFirstLayout
21411}, {
21412 name: 'circle',
21413 impl: CircleLayout
21414}, {
21415 name: 'concentric',
21416 impl: ConcentricLayout
21417}, {
21418 name: 'cose',
21419 impl: CoseLayout
21420}, {
21421 name: 'grid',
21422 impl: GridLayout
21423}, {
21424 name: 'null',
21425 impl: NullLayout
21426}, {
21427 name: 'preset',
21428 impl: PresetLayout
21429}, {
21430 name: 'random',
21431 impl: RandomLayout
21432}];
21433
21434function NullRenderer(options) {
21435 this.options = options;
21436 this.notifications = 0; // for testing
21437}
21438
21439var noop = function noop() {};
21440
21441var throwImgErr = function throwImgErr() {
21442 throw new Error('A headless instance can not render images');
21443};
21444
21445NullRenderer.prototype = {
21446 recalculateRenderedStyle: noop,
21447 notify: function notify() {
21448 this.notifications++;
21449 },
21450 init: noop,
21451 isHeadless: function isHeadless() {
21452 return true;
21453 },
21454 png: throwImgErr,
21455 jpg: throwImgErr
21456};
21457
21458var BRp$f = {};
21459BRp$f.arrowShapeWidth = 0.3;
21460
21461BRp$f.registerArrowShapes = function () {
21462 var arrowShapes = this.arrowShapes = {};
21463 var renderer = this; // Contract for arrow shapes:
21464 // 0, 0 is arrow tip
21465 // (0, 1) is direction towards node
21466 // (1, 0) is right
21467 //
21468 // functional api:
21469 // collide: check x, y in shape
21470 // roughCollide: called before collide, no false negatives
21471 // draw: draw
21472 // spacing: dist(arrowTip, nodeBoundary)
21473 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21474
21475 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21476 var x1 = translation.x - size / 2 - padding;
21477 var x2 = translation.x + size / 2 + padding;
21478 var y1 = translation.y - size / 2 - padding;
21479 var y2 = translation.y + size / 2 + padding;
21480 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21481 return inside;
21482 };
21483
21484 var transform = function transform(x, y, size, angle, translation) {
21485 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21486 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21487 var xScaled = xRotated * size;
21488 var yScaled = yRotated * size;
21489 var xTranslated = xScaled + translation.x;
21490 var yTranslated = yScaled + translation.y;
21491 return {
21492 x: xTranslated,
21493 y: yTranslated
21494 };
21495 };
21496
21497 var transformPoints = function transformPoints(pts, size, angle, translation) {
21498 var retPts = [];
21499
21500 for (var i = 0; i < pts.length; i += 2) {
21501 var x = pts[i];
21502 var y = pts[i + 1];
21503 retPts.push(transform(x, y, size, angle, translation));
21504 }
21505
21506 return retPts;
21507 };
21508
21509 var pointsToArr = function pointsToArr(pts) {
21510 var ret = [];
21511
21512 for (var i = 0; i < pts.length; i++) {
21513 var p = pts[i];
21514 ret.push(p.x, p.y);
21515 }
21516
21517 return ret;
21518 };
21519
21520 var standardGap = function standardGap(edge) {
21521 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21522 };
21523
21524 var defineArrowShape = function defineArrowShape(name, defn) {
21525 if (string(defn)) {
21526 defn = arrowShapes[defn];
21527 }
21528
21529 arrowShapes[name] = extend({
21530 name: name,
21531 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21532 collide: function collide(x, y, size, angle, translation, padding) {
21533 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21534 var inside = pointInsidePolygonPoints(x, y, points);
21535 return inside;
21536 },
21537 roughCollide: bbCollide,
21538 draw: function draw(context, size, angle, translation) {
21539 var points = transformPoints(this.points, size, angle, translation);
21540 renderer.arrowShapeImpl('polygon')(context, points);
21541 },
21542 spacing: function spacing(edge) {
21543 return 0;
21544 },
21545 gap: standardGap
21546 }, defn);
21547 };
21548
21549 defineArrowShape('none', {
21550 collide: falsify,
21551 roughCollide: falsify,
21552 draw: noop$1,
21553 spacing: zeroify,
21554 gap: zeroify
21555 });
21556 defineArrowShape('triangle', {
21557 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21558 });
21559 defineArrowShape('arrow', 'triangle');
21560 defineArrowShape('triangle-backcurve', {
21561 points: arrowShapes['triangle'].points,
21562 controlPoint: [0, -0.15],
21563 roughCollide: bbCollide,
21564 draw: function draw(context, size, angle, translation, edgeWidth) {
21565 var ptsTrans = transformPoints(this.points, size, angle, translation);
21566 var ctrlPt = this.controlPoint;
21567 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21568 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21569 },
21570 gap: function gap(edge) {
21571 return standardGap(edge) * 0.8;
21572 }
21573 });
21574 defineArrowShape('triangle-tee', {
21575 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21576 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21577 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21578 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21579 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21580 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21581 return inside;
21582 },
21583 draw: function draw(context, size, angle, translation, edgeWidth) {
21584 var triPts = transformPoints(this.points, size, angle, translation);
21585 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21586 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21587 }
21588 });
21589 defineArrowShape('circle-triangle', {
21590 radius: 0.15,
21591 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21592 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21593 var t = translation;
21594 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21595 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21596 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21597 },
21598 draw: function draw(context, size, angle, translation, edgeWidth) {
21599 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21600 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21601 },
21602 spacing: function spacing(edge) {
21603 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21604 }
21605 });
21606 defineArrowShape('triangle-cross', {
21607 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21608 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21609 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21610 0.15, -0.4],
21611 crossLinePts: function crossLinePts(size, edgeWidth) {
21612 // shift points so that the distance between the cross points matches edge width
21613 var p = this.baseCrossLinePts.slice();
21614 var shiftFactor = edgeWidth / size;
21615 var y0 = 3;
21616 var y1 = 5;
21617 p[y0] = p[y0] - shiftFactor;
21618 p[y1] = p[y1] - shiftFactor;
21619 return p;
21620 },
21621 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21622 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21623 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21624 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21625 return inside;
21626 },
21627 draw: function draw(context, size, angle, translation, edgeWidth) {
21628 var triPts = transformPoints(this.points, size, angle, translation);
21629 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21630 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21631 }
21632 });
21633 defineArrowShape('vee', {
21634 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21635 gap: function gap(edge) {
21636 return standardGap(edge) * 0.525;
21637 }
21638 });
21639 defineArrowShape('circle', {
21640 radius: 0.15,
21641 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21642 var t = translation;
21643 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21644 return inside;
21645 },
21646 draw: function draw(context, size, angle, translation, edgeWidth) {
21647 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21648 },
21649 spacing: function spacing(edge) {
21650 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21651 }
21652 });
21653 defineArrowShape('tee', {
21654 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21655 spacing: function spacing(edge) {
21656 return 1;
21657 },
21658 gap: function gap(edge) {
21659 return 1;
21660 }
21661 });
21662 defineArrowShape('square', {
21663 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21664 });
21665 defineArrowShape('diamond', {
21666 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21667 gap: function gap(edge) {
21668 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21669 }
21670 });
21671 defineArrowShape('chevron', {
21672 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21673 gap: function gap(edge) {
21674 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21675 }
21676 });
21677};
21678
21679var BRp$e = {}; // Project mouse
21680
21681BRp$e.projectIntoViewport = function (clientX, clientY) {
21682 var cy = this.cy;
21683 var offsets = this.findContainerClientCoords();
21684 var offsetLeft = offsets[0];
21685 var offsetTop = offsets[1];
21686 var scale = offsets[4];
21687 var pan = cy.pan();
21688 var zoom = cy.zoom();
21689 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21690 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21691 return [x, y];
21692};
21693
21694BRp$e.findContainerClientCoords = function () {
21695 if (this.containerBB) {
21696 return this.containerBB;
21697 }
21698
21699 var container = this.container;
21700 var rect = container.getBoundingClientRect();
21701 var style = window$1.getComputedStyle(container);
21702
21703 var styleValue = function styleValue(name) {
21704 return parseFloat(style.getPropertyValue(name));
21705 };
21706
21707 var padding = {
21708 left: styleValue('padding-left'),
21709 right: styleValue('padding-right'),
21710 top: styleValue('padding-top'),
21711 bottom: styleValue('padding-bottom')
21712 };
21713 var border = {
21714 left: styleValue('border-left-width'),
21715 right: styleValue('border-right-width'),
21716 top: styleValue('border-top-width'),
21717 bottom: styleValue('border-bottom-width')
21718 };
21719 var clientWidth = container.clientWidth;
21720 var clientHeight = container.clientHeight;
21721 var paddingHor = padding.left + padding.right;
21722 var paddingVer = padding.top + padding.bottom;
21723 var borderHor = border.left + border.right;
21724 var scale = rect.width / (clientWidth + borderHor);
21725 var unscaledW = clientWidth - paddingHor;
21726 var unscaledH = clientHeight - paddingVer;
21727 var left = rect.left + padding.left + border.left;
21728 var top = rect.top + padding.top + border.top;
21729 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21730};
21731
21732BRp$e.invalidateContainerClientCoordsCache = function () {
21733 this.containerBB = null;
21734};
21735
21736BRp$e.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21737 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21738};
21739
21740BRp$e.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21741 var self = this;
21742 var r = this;
21743 var eles = r.getCachedZSortedEles();
21744 var near = []; // 1 node max, 1 edge max
21745
21746 var zoom = r.cy.zoom();
21747 var hasCompounds = r.cy.hasCompoundNodes();
21748 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21749 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21750 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21751 var minSqDist = Infinity;
21752 var nearEdge;
21753 var nearNode;
21754
21755 if (interactiveElementsOnly) {
21756 eles = eles.interactive;
21757 }
21758
21759 function addEle(ele, sqDist) {
21760 if (ele.isNode()) {
21761 if (nearNode) {
21762 return; // can't replace node
21763 } else {
21764 nearNode = ele;
21765 near.push(ele);
21766 }
21767 }
21768
21769 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21770 if (nearEdge) {
21771 // then replace existing edge
21772 // can replace only if same z-index
21773 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) {
21774 for (var i = 0; i < near.length; i++) {
21775 if (near[i].isEdge()) {
21776 near[i] = ele;
21777 nearEdge = ele;
21778 minSqDist = sqDist != null ? sqDist : minSqDist;
21779 break;
21780 }
21781 }
21782 }
21783 } else {
21784 near.push(ele);
21785 nearEdge = ele;
21786 minSqDist = sqDist != null ? sqDist : minSqDist;
21787 }
21788 }
21789 }
21790
21791 function checkNode(node) {
21792 var width = node.outerWidth() + 2 * nodeThreshold;
21793 var height = node.outerHeight() + 2 * nodeThreshold;
21794 var hw = width / 2;
21795 var hh = height / 2;
21796 var pos = node.position();
21797
21798 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21799 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21800 ) {
21801 var shape = r.nodeShapes[self.getNodeShape(node)];
21802
21803 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21804 addEle(node, 0);
21805 return true;
21806 }
21807 }
21808 }
21809
21810 function checkEdge(edge) {
21811 var _p = edge._private;
21812 var rs = _p.rscratch;
21813 var styleWidth = edge.pstyle('width').pfValue;
21814 var scale = edge.pstyle('arrow-scale').value;
21815 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21816
21817 var widthSq = width * width;
21818 var width2 = width * 2;
21819 var src = _p.source;
21820 var tgt = _p.target;
21821 var sqDist;
21822
21823 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21824 var pts = rs.allpts;
21825
21826 for (var i = 0; i + 3 < pts.length; i += 2) {
21827 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]))) {
21828 addEle(edge, sqDist);
21829 return true;
21830 }
21831 }
21832 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21833 var pts = rs.allpts;
21834
21835 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21836 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]))) {
21837 addEle(edge, sqDist);
21838 return true;
21839 }
21840 }
21841 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21842
21843
21844 var src = src || _p.source;
21845 var tgt = tgt || _p.target;
21846 var arSize = self.getArrowWidth(styleWidth, scale);
21847 var arrows = [{
21848 name: 'source',
21849 x: rs.arrowStartX,
21850 y: rs.arrowStartY,
21851 angle: rs.srcArrowAngle
21852 }, {
21853 name: 'target',
21854 x: rs.arrowEndX,
21855 y: rs.arrowEndY,
21856 angle: rs.tgtArrowAngle
21857 }, {
21858 name: 'mid-source',
21859 x: rs.midX,
21860 y: rs.midY,
21861 angle: rs.midsrcArrowAngle
21862 }, {
21863 name: 'mid-target',
21864 x: rs.midX,
21865 y: rs.midY,
21866 angle: rs.midtgtArrowAngle
21867 }];
21868
21869 for (var i = 0; i < arrows.length; i++) {
21870 var ar = arrows[i];
21871 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21872 var edgeWidth = edge.pstyle('width').pfValue;
21873
21874 if (shape.roughCollide(x, y, arSize, ar.angle, {
21875 x: ar.x,
21876 y: ar.y
21877 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21878 x: ar.x,
21879 y: ar.y
21880 }, edgeWidth, edgeThreshold)) {
21881 addEle(edge);
21882 return true;
21883 }
21884 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21885
21886
21887 if (hasCompounds && near.length > 0) {
21888 checkNode(src);
21889 checkNode(tgt);
21890 }
21891 }
21892
21893 function preprop(obj, name, pre) {
21894 return getPrefixedProperty(obj, name, pre);
21895 }
21896
21897 function checkLabel(ele, prefix) {
21898 var _p = ele._private;
21899 var th = labelThreshold;
21900 var prefixDash;
21901
21902 if (prefix) {
21903 prefixDash = prefix + '-';
21904 } else {
21905 prefixDash = '';
21906 }
21907
21908 ele.boundingBox();
21909 var bb = _p.labelBounds[prefix || 'main'];
21910 var text = ele.pstyle(prefixDash + 'label').value;
21911 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21912
21913 if (!eventsEnabled || !text) {
21914 return;
21915 }
21916
21917 var lx = preprop(_p.rscratch, 'labelX', prefix);
21918 var ly = preprop(_p.rscratch, 'labelY', prefix);
21919 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21920 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21921 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21922 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21923
21924 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21925
21926 var ly1 = bb.y1 - th - oy;
21927 var ly2 = bb.y2 + th - oy;
21928
21929 if (theta) {
21930 var cos = Math.cos(theta);
21931 var sin = Math.sin(theta);
21932
21933 var rotate = function rotate(x, y) {
21934 x = x - lx;
21935 y = y - ly;
21936 return {
21937 x: x * cos - y * sin + lx,
21938 y: x * sin + y * cos + ly
21939 };
21940 };
21941
21942 var px1y1 = rotate(lx1, ly1);
21943 var px1y2 = rotate(lx1, ly2);
21944 var px2y1 = rotate(lx2, ly1);
21945 var px2y2 = rotate(lx2, ly2);
21946 var points = [// with the margin added after the rotation is applied
21947 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21948
21949 if (pointInsidePolygonPoints(x, y, points)) {
21950 addEle(ele);
21951 return true;
21952 }
21953 } else {
21954 // do a cheaper bb check
21955 if (inBoundingBox(bb, x, y)) {
21956 addEle(ele);
21957 return true;
21958 }
21959 }
21960 }
21961
21962 for (var i = eles.length - 1; i >= 0; i--) {
21963 // reverse order for precedence
21964 var ele = eles[i];
21965
21966 if (ele.isNode()) {
21967 checkNode(ele) || checkLabel(ele);
21968 } else {
21969 // then edge
21970 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21971 }
21972 }
21973
21974 return near;
21975}; // 'Give me everything from this box'
21976
21977
21978BRp$e.getAllInBox = function (x1, y1, x2, y2) {
21979 var eles = this.getCachedZSortedEles().interactive;
21980 var box = [];
21981 var x1c = Math.min(x1, x2);
21982 var x2c = Math.max(x1, x2);
21983 var y1c = Math.min(y1, y2);
21984 var y2c = Math.max(y1, y2);
21985 x1 = x1c;
21986 x2 = x2c;
21987 y1 = y1c;
21988 y2 = y2c;
21989 var boxBb = makeBoundingBox({
21990 x1: x1,
21991 y1: y1,
21992 x2: x2,
21993 y2: y2
21994 });
21995
21996 for (var e = 0; e < eles.length; e++) {
21997 var ele = eles[e];
21998
21999 if (ele.isNode()) {
22000 var node = ele;
22001 var nodeBb = node.boundingBox({
22002 includeNodes: true,
22003 includeEdges: false,
22004 includeLabels: false
22005 });
22006
22007 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
22008 box.push(node);
22009 }
22010 } else {
22011 var edge = ele;
22012 var _p = edge._private;
22013 var rs = _p.rscratch;
22014
22015 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
22016 continue;
22017 }
22018
22019 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
22020 continue;
22021 }
22022
22023 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
22024 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
22025 var allInside = true;
22026
22027 for (var i = 0; i < pts.length; i++) {
22028 if (!pointInBoundingBox(boxBb, pts[i])) {
22029 allInside = false;
22030 break;
22031 }
22032 }
22033
22034 if (allInside) {
22035 box.push(edge);
22036 }
22037 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22038 box.push(edge);
22039 }
22040 }
22041 }
22042
22043 return box;
22044};
22045
22046var BRp$d = {};
22047
22048BRp$d.calculateArrowAngles = function (edge) {
22049 var rs = edge._private.rscratch;
22050 var isHaystack = rs.edgeType === 'haystack';
22051 var isBezier = rs.edgeType === 'bezier';
22052 var isMultibezier = rs.edgeType === 'multibezier';
22053 var isSegments = rs.edgeType === 'segments';
22054 var isCompound = rs.edgeType === 'compound';
22055 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
22056
22057 var dispX, dispY;
22058 var startX, startY, endX, endY, midX, midY;
22059
22060 if (isHaystack) {
22061 startX = rs.haystackPts[0];
22062 startY = rs.haystackPts[1];
22063 endX = rs.haystackPts[2];
22064 endY = rs.haystackPts[3];
22065 } else {
22066 startX = rs.arrowStartX;
22067 startY = rs.arrowStartY;
22068 endX = rs.arrowEndX;
22069 endY = rs.arrowEndY;
22070 }
22071
22072 midX = rs.midX;
22073 midY = rs.midY; // source
22074 //
22075
22076 if (isSegments) {
22077 dispX = startX - rs.segpts[0];
22078 dispY = startY - rs.segpts[1];
22079 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22080 var pts = rs.allpts;
22081 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22082 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22083 dispX = startX - bX;
22084 dispY = startY - bY;
22085 } else {
22086 dispX = startX - midX;
22087 dispY = startY - midY;
22088 }
22089
22090 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
22091 //
22092
22093 var midX = rs.midX;
22094 var midY = rs.midY;
22095
22096 if (isHaystack) {
22097 midX = (startX + endX) / 2;
22098 midY = (startY + endY) / 2;
22099 }
22100
22101 dispX = endX - startX;
22102 dispY = endY - startY;
22103
22104 if (isSegments) {
22105 var pts = rs.allpts;
22106
22107 if (pts.length / 2 % 2 === 0) {
22108 var i2 = pts.length / 2;
22109 var i1 = i2 - 2;
22110 dispX = pts[i2] - pts[i1];
22111 dispY = pts[i2 + 1] - pts[i1 + 1];
22112 } else {
22113 var i2 = pts.length / 2 - 1;
22114 var i1 = i2 - 2;
22115 var i3 = i2 + 2;
22116 dispX = pts[i2] - pts[i1];
22117 dispY = pts[i2 + 1] - pts[i1 + 1];
22118 }
22119 } else if (isMultibezier || isCompound || isSelf) {
22120 var pts = rs.allpts;
22121 var cpts = rs.ctrlpts;
22122 var bp0x, bp0y;
22123 var bp1x, bp1y;
22124
22125 if (cpts.length / 2 % 2 === 0) {
22126 var p0 = pts.length / 2 - 1; // startpt
22127
22128 var ic = p0 + 2;
22129 var p1 = ic + 2;
22130 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22131 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22132 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22133 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22134 } else {
22135 var ic = pts.length / 2 - 1; // ctrpt
22136
22137 var p0 = ic - 2; // startpt
22138
22139 var p1 = ic + 2; // endpt
22140
22141 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22142 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22143 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22144 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22145 }
22146
22147 dispX = bp1x - bp0x;
22148 dispY = bp1y - bp0y;
22149 }
22150
22151 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22152 rs.midDispX = dispX;
22153 rs.midDispY = dispY; // mid source
22154 //
22155
22156 dispX *= -1;
22157 dispY *= -1;
22158
22159 if (isSegments) {
22160 var pts = rs.allpts;
22161
22162 if (pts.length / 2 % 2 === 0) ; else {
22163 var i2 = pts.length / 2 - 1;
22164 var i3 = i2 + 2;
22165 dispX = -(pts[i3] - pts[i2]);
22166 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22167 }
22168 }
22169
22170 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22171 //
22172
22173 if (isSegments) {
22174 dispX = endX - rs.segpts[rs.segpts.length - 2];
22175 dispY = endY - rs.segpts[rs.segpts.length - 1];
22176 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22177 var pts = rs.allpts;
22178 var l = pts.length;
22179 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22180 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22181 dispX = endX - bX;
22182 dispY = endY - bY;
22183 } else {
22184 dispX = endX - midX;
22185 dispY = endY - midY;
22186 }
22187
22188 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22189};
22190
22191BRp$d.getArrowWidth = BRp$d.getArrowHeight = function (edgeWidth, scale) {
22192 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22193 var cachedVal = cache[edgeWidth + ', ' + scale];
22194
22195 if (cachedVal) {
22196 return cachedVal;
22197 }
22198
22199 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22200 cache[edgeWidth + ', ' + scale] = cachedVal;
22201 return cachedVal;
22202};
22203
22204var BRp$c = {};
22205
22206BRp$c.findHaystackPoints = function (edges) {
22207 for (var i = 0; i < edges.length; i++) {
22208 var edge = edges[i];
22209 var _p = edge._private;
22210 var rs = _p.rscratch;
22211
22212 if (!rs.haystack) {
22213 var angle = Math.random() * 2 * Math.PI;
22214 rs.source = {
22215 x: Math.cos(angle),
22216 y: Math.sin(angle)
22217 };
22218 angle = Math.random() * 2 * Math.PI;
22219 rs.target = {
22220 x: Math.cos(angle),
22221 y: Math.sin(angle)
22222 };
22223 }
22224
22225 var src = _p.source;
22226 var tgt = _p.target;
22227 var srcPos = src.position();
22228 var tgtPos = tgt.position();
22229 var srcW = src.width();
22230 var tgtW = tgt.width();
22231 var srcH = src.height();
22232 var tgtH = tgt.height();
22233 var radius = edge.pstyle('haystack-radius').value;
22234 var halfRadius = radius / 2; // b/c have to half width/height
22235
22236 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];
22237 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22238 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22239
22240 rs.edgeType = 'haystack';
22241 rs.haystack = true;
22242 this.storeEdgeProjections(edge);
22243 this.calculateArrowAngles(edge);
22244 this.recalculateEdgeLabelProjections(edge);
22245 this.calculateLabelAngles(edge);
22246 }
22247};
22248
22249BRp$c.findSegmentsPoints = function (edge, pairInfo) {
22250 // Segments (multiple straight lines)
22251 var rs = edge._private.rscratch;
22252 var posPts = pairInfo.posPts,
22253 intersectionPts = pairInfo.intersectionPts,
22254 vectorNormInverse = pairInfo.vectorNormInverse;
22255 var edgeDistances = edge.pstyle('edge-distances').value;
22256 var segmentWs = edge.pstyle('segment-weights');
22257 var segmentDs = edge.pstyle('segment-distances');
22258 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22259 rs.edgeType = 'segments';
22260 rs.segpts = [];
22261
22262 for (var s = 0; s < segmentsN; s++) {
22263 var w = segmentWs.pfValue[s];
22264 var d = segmentDs.pfValue[s];
22265 var w1 = 1 - w;
22266 var w2 = w;
22267 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22268 var adjustedMidpt = {
22269 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22270 y: midptPts.y1 * w1 + midptPts.y2 * w2
22271 };
22272 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22273 }
22274};
22275
22276BRp$c.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22277 // Self-edge
22278 var rs = edge._private.rscratch;
22279 var dirCounts = pairInfo.dirCounts,
22280 srcPos = pairInfo.srcPos;
22281 var ctrlptDists = edge.pstyle('control-point-distances');
22282 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22283 var loopDir = edge.pstyle('loop-direction').pfValue;
22284 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22285 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22286 rs.edgeType = 'self';
22287 var j = i;
22288 var loopDist = stepSize;
22289
22290 if (edgeIsUnbundled) {
22291 j = 0;
22292 loopDist = ctrlptDist;
22293 }
22294
22295 var loopAngle = loopDir - Math.PI / 2;
22296 var outAngle = loopAngle - loopSwp / 2;
22297 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22298
22299 var dc = String(loopDir + '_' + loopSwp);
22300 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22301 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)];
22302};
22303
22304BRp$c.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22305 // Compound edge
22306 var rs = edge._private.rscratch;
22307 rs.edgeType = 'compound';
22308 var srcPos = pairInfo.srcPos,
22309 tgtPos = pairInfo.tgtPos,
22310 srcW = pairInfo.srcW,
22311 srcH = pairInfo.srcH,
22312 tgtW = pairInfo.tgtW,
22313 tgtH = pairInfo.tgtH;
22314 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22315 var ctrlptDists = edge.pstyle('control-point-distances');
22316 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22317 var j = i;
22318 var loopDist = stepSize;
22319
22320 if (edgeIsUnbundled) {
22321 j = 0;
22322 loopDist = ctrlptDist;
22323 }
22324
22325 var loopW = 50;
22326 var loopaPos = {
22327 x: srcPos.x - srcW / 2,
22328 y: srcPos.y - srcH / 2
22329 };
22330 var loopbPos = {
22331 x: tgtPos.x - tgtW / 2,
22332 y: tgtPos.y - tgtH / 2
22333 };
22334 var loopPos = {
22335 x: Math.min(loopaPos.x, loopbPos.x),
22336 y: Math.min(loopaPos.y, loopbPos.y)
22337 }; // avoids cases with impossible beziers
22338
22339 var minCompoundStretch = 0.5;
22340 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22341 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22342 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];
22343};
22344
22345BRp$c.findStraightEdgePoints = function (edge) {
22346 // Straight edge within bundle
22347 edge._private.rscratch.edgeType = 'straight';
22348};
22349
22350BRp$c.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22351 var rs = edge._private.rscratch;
22352 var vectorNormInverse = pairInfo.vectorNormInverse,
22353 posPts = pairInfo.posPts,
22354 intersectionPts = pairInfo.intersectionPts;
22355 var edgeDistances = edge.pstyle('edge-distances').value;
22356 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22357 var ctrlptDists = edge.pstyle('control-point-distances');
22358 var ctrlptWs = edge.pstyle('control-point-weights');
22359 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22360 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22361 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22362
22363 var multi = edgeIsUnbundled;
22364 rs.edgeType = multi ? 'multibezier' : 'bezier';
22365 rs.ctrlpts = [];
22366
22367 for (var b = 0; b < bezierN; b++) {
22368 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22369 var manctrlptDist = void 0;
22370 var sign = signum(normctrlptDist);
22371
22372 if (multi) {
22373 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22374
22375 ctrlptWeight = ctrlptWs.value[b];
22376 }
22377
22378 if (edgeIsUnbundled) {
22379 // multi or single unbundled
22380 manctrlptDist = ctrlptDist;
22381 } else {
22382 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22383 }
22384
22385 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22386 var w1 = 1 - ctrlptWeight;
22387 var w2 = ctrlptWeight;
22388 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22389 var adjustedMidpt = {
22390 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22391 y: midptPts.y1 * w1 + midptPts.y2 * w2
22392 };
22393 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22394 }
22395};
22396
22397BRp$c.findTaxiPoints = function (edge, pairInfo) {
22398 // Taxicab geometry with two turns maximum
22399 var rs = edge._private.rscratch;
22400 rs.edgeType = 'segments';
22401 var VERTICAL = 'vertical';
22402 var HORIZONTAL = 'horizontal';
22403 var LEFTWARD = 'leftward';
22404 var RIGHTWARD = 'rightward';
22405 var DOWNWARD = 'downward';
22406 var UPWARD = 'upward';
22407 var AUTO = 'auto';
22408 var posPts = pairInfo.posPts,
22409 srcW = pairInfo.srcW,
22410 srcH = pairInfo.srcH,
22411 tgtW = pairInfo.tgtW,
22412 tgtH = pairInfo.tgtH;
22413 var edgeDistances = edge.pstyle('edge-distances').value;
22414 var dIncludesNodeBody = edgeDistances !== 'node-position';
22415 var taxiDir = edge.pstyle('taxi-direction').value;
22416 var rawTaxiDir = taxiDir; // unprocessed value
22417
22418 var taxiTurn = edge.pstyle('taxi-turn');
22419 var turnIsPercent = taxiTurn.units === '%';
22420 var taxiTurnPfVal = taxiTurn.pfValue;
22421 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22422
22423 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22424 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22425 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22426 var pdx = posPts.x2 - posPts.x1;
22427 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22428
22429 var subDWH = function subDWH(dxy, dwh) {
22430 if (dxy > 0) {
22431 return Math.max(dxy - dwh, 0);
22432 } else {
22433 return Math.min(dxy + dwh, 0);
22434 }
22435 };
22436
22437 var dx = subDWH(pdx, dw);
22438 var dy = subDWH(pdy, dh);
22439 var isExplicitDir = false;
22440
22441 if (rawTaxiDir === AUTO) {
22442 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22443 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22444 taxiDir = VERTICAL;
22445 isExplicitDir = true;
22446 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22447 taxiDir = HORIZONTAL;
22448 isExplicitDir = true;
22449 }
22450
22451 var isVert = taxiDir === VERTICAL;
22452 var l = isVert ? dy : dx;
22453 var pl = isVert ? pdy : pdx;
22454 var sgnL = signum(pl);
22455 var forcedDir = false;
22456
22457 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22458 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22459 sgnL *= -1;
22460 l = sgnL * Math.abs(l);
22461 forcedDir = true;
22462 }
22463
22464 var d;
22465
22466 if (turnIsPercent) {
22467 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22468 d = p * l;
22469 } else {
22470 var k = taxiTurnPfVal < 0 ? l : 0;
22471 d = k + taxiTurnPfVal * sgnL;
22472 }
22473
22474 var getIsTooClose = function getIsTooClose(d) {
22475 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22476 };
22477
22478 var isTooCloseSrc = getIsTooClose(d);
22479 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22480 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22481
22482 if (isTooClose && !forcedDir) {
22483 // non-ideal routing
22484 if (isVert) {
22485 // vertical fallbacks
22486 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22487 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22488
22489 if (lShapeInsideSrc) {
22490 // horizontal Z-shape (direction not respected)
22491 var x = (posPts.x1 + posPts.x2) / 2;
22492 var y1 = posPts.y1,
22493 y2 = posPts.y2;
22494 rs.segpts = [x, y1, x, y2];
22495 } else if (lShapeInsideTgt) {
22496 // vertical Z-shape (distance not respected)
22497 var y = (posPts.y1 + posPts.y2) / 2;
22498 var x1 = posPts.x1,
22499 x2 = posPts.x2;
22500 rs.segpts = [x1, y, x2, y];
22501 } else {
22502 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22503 rs.segpts = [posPts.x1, posPts.y2];
22504 }
22505 } else {
22506 // horizontal fallbacks
22507 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22508
22509 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22510
22511 if (_lShapeInsideSrc) {
22512 // vertical Z-shape (direction not respected)
22513 var _y = (posPts.y1 + posPts.y2) / 2;
22514
22515 var _x = posPts.x1,
22516 _x2 = posPts.x2;
22517 rs.segpts = [_x, _y, _x2, _y];
22518 } else if (_lShapeInsideTgt) {
22519 // horizontal Z-shape (turn distance not respected)
22520 var _x3 = (posPts.x1 + posPts.x2) / 2;
22521
22522 var _y2 = posPts.y1,
22523 _y3 = posPts.y2;
22524 rs.segpts = [_x3, _y2, _x3, _y3];
22525 } else {
22526 // L-shape (turn distance not respected, but works well for tree siblings)
22527 rs.segpts = [posPts.x2, posPts.y1];
22528 }
22529 }
22530 } else {
22531 // ideal routing
22532 if (isVert) {
22533 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22534
22535 var _x4 = posPts.x1,
22536 _x5 = posPts.x2;
22537 rs.segpts = [_x4, _y4, _x5, _y4];
22538 } else {
22539 // horizontal
22540 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22541
22542 var _y5 = posPts.y1,
22543 _y6 = posPts.y2;
22544 rs.segpts = [_x6, _y5, _x6, _y6];
22545 }
22546 }
22547};
22548
22549BRp$c.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22550 var rs = edge._private.rscratch; // can only correct beziers for now...
22551
22552 if (rs.edgeType === 'bezier') {
22553 var srcPos = pairInfo.srcPos,
22554 tgtPos = pairInfo.tgtPos,
22555 srcW = pairInfo.srcW,
22556 srcH = pairInfo.srcH,
22557 tgtW = pairInfo.tgtW,
22558 tgtH = pairInfo.tgtH,
22559 srcShape = pairInfo.srcShape,
22560 tgtShape = pairInfo.tgtShape;
22561 var badStart = !number$1(rs.startX) || !number$1(rs.startY);
22562 var badAStart = !number$1(rs.arrowStartX) || !number$1(rs.arrowStartY);
22563 var badEnd = !number$1(rs.endX) || !number$1(rs.endY);
22564 var badAEnd = !number$1(rs.arrowEndX) || !number$1(rs.arrowEndY);
22565 var minCpADistFactor = 3;
22566 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22567 var minCpADist = minCpADistFactor * arrowW;
22568 var startACpDist = dist({
22569 x: rs.ctrlpts[0],
22570 y: rs.ctrlpts[1]
22571 }, {
22572 x: rs.startX,
22573 y: rs.startY
22574 });
22575 var closeStartACp = startACpDist < minCpADist;
22576 var endACpDist = dist({
22577 x: rs.ctrlpts[0],
22578 y: rs.ctrlpts[1]
22579 }, {
22580 x: rs.endX,
22581 y: rs.endY
22582 });
22583 var closeEndACp = endACpDist < minCpADist;
22584 var overlapping = false;
22585
22586 if (badStart || badAStart || closeStartACp) {
22587 overlapping = true; // project control point along line from src centre to outside the src shape
22588 // (otherwise intersection will yield nothing)
22589
22590 var cpD = {
22591 // delta
22592 x: rs.ctrlpts[0] - srcPos.x,
22593 y: rs.ctrlpts[1] - srcPos.y
22594 };
22595 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22596
22597 var cpM = {
22598 // normalised delta
22599 x: cpD.x / cpL,
22600 y: cpD.y / cpL
22601 };
22602 var radius = Math.max(srcW, srcH);
22603 var cpProj = {
22604 // *2 radius guarantees outside shape
22605 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22606 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22607 };
22608 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22609
22610 if (closeStartACp) {
22611 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22612 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22613 } else {
22614 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22615 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22616 }
22617 }
22618
22619 if (badEnd || badAEnd || closeEndACp) {
22620 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22621 // (otherwise intersection will yield nothing)
22622
22623 var _cpD = {
22624 // delta
22625 x: rs.ctrlpts[0] - tgtPos.x,
22626 y: rs.ctrlpts[1] - tgtPos.y
22627 };
22628
22629 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22630
22631
22632 var _cpM = {
22633 // normalised delta
22634 x: _cpD.x / _cpL,
22635 y: _cpD.y / _cpL
22636 };
22637
22638 var _radius = Math.max(srcW, srcH);
22639
22640 var _cpProj = {
22641 // *2 radius guarantees outside shape
22642 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22643 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22644 };
22645 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22646
22647 if (closeEndACp) {
22648 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22649 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22650 } else {
22651 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22652 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22653 }
22654 }
22655
22656 if (overlapping) {
22657 // recalc endpts
22658 this.findEndpoints(edge);
22659 }
22660 }
22661};
22662
22663BRp$c.storeAllpts = function (edge) {
22664 var rs = edge._private.rscratch;
22665
22666 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22667 rs.allpts = [];
22668 rs.allpts.push(rs.startX, rs.startY);
22669
22670 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22671 // ctrl pt itself
22672 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22673
22674 if (b + 3 < rs.ctrlpts.length) {
22675 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22676 }
22677 }
22678
22679 rs.allpts.push(rs.endX, rs.endY);
22680 var m, mt;
22681
22682 if (rs.ctrlpts.length / 2 % 2 === 0) {
22683 m = rs.allpts.length / 2 - 1;
22684 rs.midX = rs.allpts[m];
22685 rs.midY = rs.allpts[m + 1];
22686 } else {
22687 m = rs.allpts.length / 2 - 3;
22688 mt = 0.5;
22689 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22690 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22691 }
22692 } else if (rs.edgeType === 'straight') {
22693 // need to calc these after endpts
22694 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22695
22696 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22697 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22698 } else if (rs.edgeType === 'segments') {
22699 rs.allpts = [];
22700 rs.allpts.push(rs.startX, rs.startY);
22701 rs.allpts.push.apply(rs.allpts, rs.segpts);
22702 rs.allpts.push(rs.endX, rs.endY);
22703
22704 if (rs.segpts.length % 4 === 0) {
22705 var i2 = rs.segpts.length / 2;
22706 var i1 = i2 - 2;
22707 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22708 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22709 } else {
22710 var _i = rs.segpts.length / 2 - 1;
22711
22712 rs.midX = rs.segpts[_i];
22713 rs.midY = rs.segpts[_i + 1];
22714 }
22715 }
22716};
22717
22718BRp$c.checkForInvalidEdgeWarning = function (edge) {
22719 var rs = edge[0]._private.rscratch;
22720
22721 if (rs.nodesOverlap || number$1(rs.startX) && number$1(rs.startY) && number$1(rs.endX) && number$1(rs.endY)) {
22722 rs.loggedErr = false;
22723 } else {
22724 if (!rs.loggedErr) {
22725 rs.loggedErr = true;
22726 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.');
22727 }
22728 }
22729};
22730
22731BRp$c.findEdgeControlPoints = function (edges) {
22732 var _this = this;
22733
22734 if (!edges || edges.length === 0) {
22735 return;
22736 }
22737
22738 var r = this;
22739 var cy = r.cy;
22740 var hasCompounds = cy.hasCompoundNodes();
22741 var hashTable = {
22742 map: new Map$1(),
22743 get: function get(pairId) {
22744 var map2 = this.map.get(pairId[0]);
22745
22746 if (map2 != null) {
22747 return map2.get(pairId[1]);
22748 } else {
22749 return null;
22750 }
22751 },
22752 set: function set(pairId, val) {
22753 var map2 = this.map.get(pairId[0]);
22754
22755 if (map2 == null) {
22756 map2 = new Map$1();
22757 this.map.set(pairId[0], map2);
22758 }
22759
22760 map2.set(pairId[1], val);
22761 }
22762 };
22763 var pairIds = [];
22764 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22765
22766 for (var i = 0; i < edges.length; i++) {
22767 var edge = edges[i];
22768 var _p = edge._private;
22769 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22770 // they shouldn't take up space
22771
22772 if (edge.removed() || !edge.takesUpSpace()) {
22773 continue;
22774 }
22775
22776 if (curveStyle === 'haystack') {
22777 haystackEdges.push(edge);
22778 continue;
22779 }
22780
22781 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'straight-triangle' || curveStyle === 'taxi';
22782 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22783 var src = _p.source;
22784 var tgt = _p.target;
22785 var srcIndex = src.poolIndex();
22786 var tgtIndex = tgt.poolIndex();
22787 var pairId = [srcIndex, tgtIndex].sort();
22788 var tableEntry = hashTable.get(pairId);
22789
22790 if (tableEntry == null) {
22791 tableEntry = {
22792 eles: []
22793 };
22794 hashTable.set(pairId, tableEntry);
22795 pairIds.push(pairId);
22796 }
22797
22798 tableEntry.eles.push(edge);
22799
22800 if (edgeIsUnbundled) {
22801 tableEntry.hasUnbundled = true;
22802 }
22803
22804 if (edgeIsBezier) {
22805 tableEntry.hasBezier = true;
22806 }
22807 } // for each pair (src, tgt), create the ctrl pts
22808 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22809
22810
22811 var _loop = function _loop(p) {
22812 var pairId = pairIds[p];
22813 var pairInfo = hashTable.get(pairId);
22814 var swappedpairInfo = void 0;
22815
22816 if (!pairInfo.hasUnbundled) {
22817 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22818 return e.isBundledBezier();
22819 });
22820 clearArray(pairInfo.eles);
22821 pllEdges.forEach(function (edge) {
22822 return pairInfo.eles.push(edge);
22823 }); // for each pair id, the edges should be sorted by index
22824
22825 pairInfo.eles.sort(function (edge1, edge2) {
22826 return edge1.poolIndex() - edge2.poolIndex();
22827 });
22828 }
22829
22830 var firstEdge = pairInfo.eles[0];
22831 var src = firstEdge.source();
22832 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22833
22834 if (src.poolIndex() > tgt.poolIndex()) {
22835 var temp = src;
22836 src = tgt;
22837 tgt = temp;
22838 }
22839
22840 var srcPos = pairInfo.srcPos = src.position();
22841 var tgtPos = pairInfo.tgtPos = tgt.position();
22842 var srcW = pairInfo.srcW = src.outerWidth();
22843 var srcH = pairInfo.srcH = src.outerHeight();
22844 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22845 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22846
22847 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22848
22849 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22850
22851 pairInfo.dirCounts = {
22852 'north': 0,
22853 'west': 0,
22854 'south': 0,
22855 'east': 0,
22856 'northwest': 0,
22857 'southwest': 0,
22858 'northeast': 0,
22859 'southeast': 0
22860 };
22861
22862 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22863 var _edge = pairInfo.eles[_i2];
22864 var rs = _edge[0]._private.rscratch;
22865
22866 var _curveStyle = _edge.pstyle('curve-style').value;
22867
22868 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22869
22870
22871 var edgeIsSwapped = !src.same(_edge.source());
22872
22873 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22874 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22875
22876 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22877 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22878
22879 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22880 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22881 var intersectionPts = pairInfo.intersectionPts = {
22882 x1: srcOutside[0],
22883 x2: tgtOutside[0],
22884 y1: srcOutside[1],
22885 y2: tgtOutside[1]
22886 };
22887 var posPts = pairInfo.posPts = {
22888 x1: srcPos.x,
22889 x2: tgtPos.x,
22890 y1: srcPos.y,
22891 y2: tgtPos.y
22892 };
22893 var dy = tgtOutside[1] - srcOutside[1];
22894 var dx = tgtOutside[0] - srcOutside[0];
22895 var l = Math.sqrt(dx * dx + dy * dy);
22896 var vector = pairInfo.vector = {
22897 x: dx,
22898 y: dy
22899 };
22900 var vectorNorm = pairInfo.vectorNorm = {
22901 x: vector.x / l,
22902 y: vector.y / l
22903 };
22904 var vectorNormInverse = {
22905 x: -vectorNorm.y,
22906 y: vectorNorm.x
22907 }; // if node shapes overlap, then no ctrl pts to draw
22908
22909 pairInfo.nodesOverlap = !number$1(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);
22910 pairInfo.vectorNormInverse = vectorNormInverse;
22911 swappedpairInfo = {
22912 nodesOverlap: pairInfo.nodesOverlap,
22913 dirCounts: pairInfo.dirCounts,
22914 calculatedIntersection: true,
22915 hasBezier: pairInfo.hasBezier,
22916 hasUnbundled: pairInfo.hasUnbundled,
22917 eles: pairInfo.eles,
22918 srcPos: tgtPos,
22919 tgtPos: srcPos,
22920 srcW: tgtW,
22921 srcH: tgtH,
22922 tgtW: srcW,
22923 tgtH: srcH,
22924 srcIntn: tgtIntn,
22925 tgtIntn: srcIntn,
22926 srcShape: tgtShape,
22927 tgtShape: srcShape,
22928 posPts: {
22929 x1: posPts.x2,
22930 y1: posPts.y2,
22931 x2: posPts.x1,
22932 y2: posPts.y1
22933 },
22934 intersectionPts: {
22935 x1: intersectionPts.x2,
22936 y1: intersectionPts.y2,
22937 x2: intersectionPts.x1,
22938 y2: intersectionPts.y1
22939 },
22940 vector: {
22941 x: -vector.x,
22942 y: -vector.y
22943 },
22944 vectorNorm: {
22945 x: -vectorNorm.x,
22946 y: -vectorNorm.y
22947 },
22948 vectorNormInverse: {
22949 x: -vectorNormInverse.x,
22950 y: -vectorNormInverse.y
22951 }
22952 };
22953 }
22954
22955 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22956 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22957 rs.srcIntn = passedPairInfo.srcIntn;
22958 rs.tgtIntn = passedPairInfo.tgtIntn;
22959
22960 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22961 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22962 } else if (src === tgt) {
22963 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22964 } else if (_curveStyle === 'segments') {
22965 _this.findSegmentsPoints(_edge, passedPairInfo);
22966 } else if (_curveStyle === 'taxi') {
22967 _this.findTaxiPoints(_edge, passedPairInfo);
22968 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22969 _this.findStraightEdgePoints(_edge);
22970 } else {
22971 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22972 }
22973
22974 _this.findEndpoints(_edge);
22975
22976 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22977
22978 _this.checkForInvalidEdgeWarning(_edge);
22979
22980 _this.storeAllpts(_edge);
22981
22982 _this.storeEdgeProjections(_edge);
22983
22984 _this.calculateArrowAngles(_edge);
22985
22986 _this.recalculateEdgeLabelProjections(_edge);
22987
22988 _this.calculateLabelAngles(_edge);
22989 } // for pair edges
22990
22991 };
22992
22993 for (var p = 0; p < pairIds.length; p++) {
22994 _loop(p);
22995 } // for pair ids
22996 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22997
22998
22999 this.findHaystackPoints(haystackEdges);
23000};
23001
23002function getPts(pts) {
23003 var retPts = [];
23004
23005 if (pts == null) {
23006 return;
23007 }
23008
23009 for (var i = 0; i < pts.length; i += 2) {
23010 var x = pts[i];
23011 var y = pts[i + 1];
23012 retPts.push({
23013 x: x,
23014 y: y
23015 });
23016 }
23017
23018 return retPts;
23019}
23020
23021BRp$c.getSegmentPoints = function (edge) {
23022 var rs = edge[0]._private.rscratch;
23023 var type = rs.edgeType;
23024
23025 if (type === 'segments') {
23026 this.recalculateRenderedStyle(edge);
23027 return getPts(rs.segpts);
23028 }
23029};
23030
23031BRp$c.getControlPoints = function (edge) {
23032 var rs = edge[0]._private.rscratch;
23033 var type = rs.edgeType;
23034
23035 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23036 this.recalculateRenderedStyle(edge);
23037 return getPts(rs.ctrlpts);
23038 }
23039};
23040
23041BRp$c.getEdgeMidpoint = function (edge) {
23042 var rs = edge[0]._private.rscratch;
23043 this.recalculateRenderedStyle(edge);
23044 return {
23045 x: rs.midX,
23046 y: rs.midY
23047 };
23048};
23049
23050var BRp$b = {};
23051
23052BRp$b.manualEndptToPx = function (node, prop) {
23053 var r = this;
23054 var npos = node.position();
23055 var w = node.outerWidth();
23056 var h = node.outerHeight();
23057
23058 if (prop.value.length === 2) {
23059 var p = [prop.pfValue[0], prop.pfValue[1]];
23060
23061 if (prop.units[0] === '%') {
23062 p[0] = p[0] * w;
23063 }
23064
23065 if (prop.units[1] === '%') {
23066 p[1] = p[1] * h;
23067 }
23068
23069 p[0] += npos.x;
23070 p[1] += npos.y;
23071 return p;
23072 } else {
23073 var angle = prop.pfValue[0];
23074 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23075
23076 var l = 2 * Math.max(w, h);
23077 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23078 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
23079 }
23080};
23081
23082BRp$b.findEndpoints = function (edge) {
23083 var r = this;
23084 var intersect;
23085 var source = edge.source()[0];
23086 var target = edge.target()[0];
23087 var srcPos = source.position();
23088 var tgtPos = target.position();
23089 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23090 var srcArShape = edge.pstyle('source-arrow-shape').value;
23091 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23092 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23093 var curveStyle = edge.pstyle('curve-style').value;
23094 var rs = edge._private.rscratch;
23095 var et = rs.edgeType;
23096 var taxi = curveStyle === 'taxi';
23097 var self = et === 'self' || et === 'compound';
23098 var bezier = et === 'bezier' || et === 'multibezier' || self;
23099 var multi = et !== 'bezier';
23100 var lines = et === 'straight' || et === 'segments';
23101 var segments = et === 'segments';
23102 var hasEndpts = bezier || multi || lines;
23103 var overrideEndpts = self || taxi;
23104 var srcManEndpt = edge.pstyle('source-endpoint');
23105 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23106 var tgtManEndpt = edge.pstyle('target-endpoint');
23107 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23108 rs.srcManEndpt = srcManEndpt;
23109 rs.tgtManEndpt = tgtManEndpt;
23110 var p1; // last known point of edge on target side
23111
23112 var p2; // last known point of edge on source side
23113
23114 var p1_i; // point to intersect with target shape
23115
23116 var p2_i; // point to intersect with source shape
23117
23118 if (bezier) {
23119 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23120 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23121 p1 = cpEnd;
23122 p2 = cpStart;
23123 } else if (lines) {
23124 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23125 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23126 p1 = tgtArrowFromPt;
23127 p2 = srcArrowFromPt;
23128 }
23129
23130 if (tgtManEndptVal === 'inside-to-node') {
23131 intersect = [tgtPos.x, tgtPos.y];
23132 } else if (tgtManEndpt.units) {
23133 intersect = this.manualEndptToPx(target, tgtManEndpt);
23134 } else if (tgtManEndptVal === 'outside-to-line') {
23135 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23136 } else {
23137 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23138 p1_i = p1;
23139 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23140 p1_i = [srcPos.x, srcPos.y];
23141 }
23142
23143 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
23144
23145 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23146 var trs = target._private.rscratch;
23147 var lw = trs.labelWidth;
23148 var lh = trs.labelHeight;
23149 var lx = trs.labelX;
23150 var ly = trs.labelY;
23151 var lw2 = lw / 2;
23152 var lh2 = lh / 2;
23153 var va = target.pstyle('text-valign').value;
23154
23155 if (va === 'top') {
23156 ly -= lh2;
23157 } else if (va === 'bottom') {
23158 ly += lh2;
23159 }
23160
23161 var ha = target.pstyle('text-halign').value;
23162
23163 if (ha === 'left') {
23164 lx -= lw2;
23165 } else if (ha === 'right') {
23166 lx += lw2;
23167 }
23168
23169 var labelIntersect = polygonIntersectLine(p1_i[0], p1_i[1], [lx - lw2, ly - lh2, lx + lw2, ly - lh2, lx + lw2, ly + lh2, lx - lw2, ly + lh2], tgtPos.x, tgtPos.y);
23170
23171 if (labelIntersect.length > 0) {
23172 var refPt = srcPos;
23173 var intSqdist = sqdist(refPt, array2point(intersect));
23174 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23175 var minSqDist = intSqdist;
23176
23177 if (labIntSqdist < intSqdist) {
23178 intersect = labelIntersect;
23179 minSqDist = labIntSqdist;
23180 }
23181
23182 if (labelIntersect.length > 2) {
23183 var labInt2SqDist = sqdist(refPt, {
23184 x: labelIntersect[2],
23185 y: labelIntersect[3]
23186 });
23187
23188 if (labInt2SqDist < minSqDist) {
23189 intersect = [labelIntersect[2], labelIntersect[3]];
23190 }
23191 }
23192 }
23193 }
23194 }
23195
23196 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23197 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23198 rs.endX = edgeEnd[0];
23199 rs.endY = edgeEnd[1];
23200 rs.arrowEndX = arrowEnd[0];
23201 rs.arrowEndY = arrowEnd[1];
23202
23203 if (srcManEndptVal === 'inside-to-node') {
23204 intersect = [srcPos.x, srcPos.y];
23205 } else if (srcManEndpt.units) {
23206 intersect = this.manualEndptToPx(source, srcManEndpt);
23207 } else if (srcManEndptVal === 'outside-to-line') {
23208 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23209 } else {
23210 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23211 p2_i = p2;
23212 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23213 p2_i = [tgtPos.x, tgtPos.y];
23214 }
23215
23216 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23217
23218 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23219 var srs = source._private.rscratch;
23220 var _lw = srs.labelWidth;
23221 var _lh = srs.labelHeight;
23222 var _lx = srs.labelX;
23223 var _ly = srs.labelY;
23224
23225 var _lw2 = _lw / 2;
23226
23227 var _lh2 = _lh / 2;
23228
23229 var _va = source.pstyle('text-valign').value;
23230
23231 if (_va === 'top') {
23232 _ly -= _lh2;
23233 } else if (_va === 'bottom') {
23234 _ly += _lh2;
23235 }
23236
23237 var _ha = source.pstyle('text-halign').value;
23238
23239 if (_ha === 'left') {
23240 _lx -= _lw2;
23241 } else if (_ha === 'right') {
23242 _lx += _lw2;
23243 }
23244
23245 var _labelIntersect = polygonIntersectLine(p2_i[0], p2_i[1], [_lx - _lw2, _ly - _lh2, _lx + _lw2, _ly - _lh2, _lx + _lw2, _ly + _lh2, _lx - _lw2, _ly + _lh2], srcPos.x, srcPos.y);
23246
23247 if (_labelIntersect.length > 0) {
23248 var _refPt = tgtPos;
23249
23250 var _intSqdist = sqdist(_refPt, array2point(intersect));
23251
23252 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23253
23254 var _minSqDist = _intSqdist;
23255
23256 if (_labIntSqdist < _intSqdist) {
23257 intersect = [_labelIntersect[0], _labelIntersect[1]];
23258 _minSqDist = _labIntSqdist;
23259 }
23260
23261 if (_labelIntersect.length > 2) {
23262 var _labInt2SqDist = sqdist(_refPt, {
23263 x: _labelIntersect[2],
23264 y: _labelIntersect[3]
23265 });
23266
23267 if (_labInt2SqDist < _minSqDist) {
23268 intersect = [_labelIntersect[2], _labelIntersect[3]];
23269 }
23270 }
23271 }
23272 }
23273 }
23274
23275 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23276 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23277 rs.startX = edgeStart[0];
23278 rs.startY = edgeStart[1];
23279 rs.arrowStartX = arrowStart[0];
23280 rs.arrowStartY = arrowStart[1];
23281
23282 if (hasEndpts) {
23283 if (!number$1(rs.startX) || !number$1(rs.startY) || !number$1(rs.endX) || !number$1(rs.endY)) {
23284 rs.badLine = true;
23285 } else {
23286 rs.badLine = false;
23287 }
23288 }
23289};
23290
23291BRp$b.getSourceEndpoint = function (edge) {
23292 var rs = edge[0]._private.rscratch;
23293 this.recalculateRenderedStyle(edge);
23294
23295 switch (rs.edgeType) {
23296 case 'haystack':
23297 return {
23298 x: rs.haystackPts[0],
23299 y: rs.haystackPts[1]
23300 };
23301
23302 default:
23303 return {
23304 x: rs.arrowStartX,
23305 y: rs.arrowStartY
23306 };
23307 }
23308};
23309
23310BRp$b.getTargetEndpoint = function (edge) {
23311 var rs = edge[0]._private.rscratch;
23312 this.recalculateRenderedStyle(edge);
23313
23314 switch (rs.edgeType) {
23315 case 'haystack':
23316 return {
23317 x: rs.haystackPts[2],
23318 y: rs.haystackPts[3]
23319 };
23320
23321 default:
23322 return {
23323 x: rs.arrowEndX,
23324 y: rs.arrowEndY
23325 };
23326 }
23327};
23328
23329var BRp$a = {};
23330
23331function pushBezierPts(r, edge, pts) {
23332 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23333 return qbezierAt(p1, p2, p3, t);
23334 };
23335
23336 var _p = edge._private;
23337 var bpts = _p.rstyle.bezierPts;
23338
23339 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23340 var p = r.bezierProjPcts[i];
23341 bpts.push({
23342 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23343 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23344 });
23345 }
23346}
23347
23348BRp$a.storeEdgeProjections = function (edge) {
23349 var _p = edge._private;
23350 var rs = _p.rscratch;
23351 var et = rs.edgeType; // clear the cached points state
23352
23353 _p.rstyle.bezierPts = null;
23354 _p.rstyle.linePts = null;
23355 _p.rstyle.haystackPts = null;
23356
23357 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23358 _p.rstyle.bezierPts = [];
23359
23360 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23361 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23362 }
23363 } else if (et === 'segments') {
23364 var lpts = _p.rstyle.linePts = [];
23365
23366 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23367 lpts.push({
23368 x: rs.allpts[i],
23369 y: rs.allpts[i + 1]
23370 });
23371 }
23372 } else if (et === 'haystack') {
23373 var hpts = rs.haystackPts;
23374 _p.rstyle.haystackPts = [{
23375 x: hpts[0],
23376 y: hpts[1]
23377 }, {
23378 x: hpts[2],
23379 y: hpts[3]
23380 }];
23381 }
23382
23383 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23384};
23385
23386BRp$a.recalculateEdgeProjections = function (edges) {
23387 this.findEdgeControlPoints(edges);
23388};
23389
23390/* global document */
23391
23392var BRp$9 = {};
23393
23394BRp$9.recalculateNodeLabelProjection = function (node) {
23395 var content = node.pstyle('label').strValue;
23396
23397 if (emptyString(content)) {
23398 return;
23399 }
23400
23401 var textX, textY;
23402 var _p = node._private;
23403 var nodeWidth = node.width();
23404 var nodeHeight = node.height();
23405 var padding = node.padding();
23406 var nodePos = node.position();
23407 var textHalign = node.pstyle('text-halign').strValue;
23408 var textValign = node.pstyle('text-valign').strValue;
23409 var rs = _p.rscratch;
23410 var rstyle = _p.rstyle;
23411
23412 switch (textHalign) {
23413 case 'left':
23414 textX = nodePos.x - nodeWidth / 2 - padding;
23415 break;
23416
23417 case 'right':
23418 textX = nodePos.x + nodeWidth / 2 + padding;
23419 break;
23420
23421 default:
23422 // e.g. center
23423 textX = nodePos.x;
23424 }
23425
23426 switch (textValign) {
23427 case 'top':
23428 textY = nodePos.y - nodeHeight / 2 - padding;
23429 break;
23430
23431 case 'bottom':
23432 textY = nodePos.y + nodeHeight / 2 + padding;
23433 break;
23434
23435 default:
23436 // e.g. middle
23437 textY = nodePos.y;
23438 }
23439
23440 rs.labelX = textX;
23441 rs.labelY = textY;
23442 rstyle.labelX = textX;
23443 rstyle.labelY = textY;
23444 this.calculateLabelAngles(node);
23445 this.applyLabelDimensions(node);
23446};
23447
23448var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23449 var angle = Math.atan(dy / dx);
23450
23451 if (dx === 0 && angle < 0) {
23452 angle = angle * -1;
23453 }
23454
23455 return angle;
23456};
23457
23458var lineAngle = function lineAngle(p0, p1) {
23459 var dx = p1.x - p0.x;
23460 var dy = p1.y - p0.y;
23461 return lineAngleFromDelta(dx, dy);
23462};
23463
23464var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23465 var t0 = bound(0, t - 0.001, 1);
23466 var t1 = bound(0, t + 0.001, 1);
23467 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23468 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23469 return lineAngle(lp0, lp1);
23470};
23471
23472BRp$9.recalculateEdgeLabelProjections = function (edge) {
23473 var p;
23474 var _p = edge._private;
23475 var rs = _p.rscratch;
23476 var r = this;
23477 var content = {
23478 mid: edge.pstyle('label').strValue,
23479 source: edge.pstyle('source-label').strValue,
23480 target: edge.pstyle('target-label').strValue
23481 };
23482
23483 if (content.mid || content.source || content.target) ; else {
23484 return; // no labels => no calcs
23485 } // add center point to style so bounding box calculations can use it
23486 //
23487
23488
23489 p = {
23490 x: rs.midX,
23491 y: rs.midY
23492 };
23493
23494 var setRs = function setRs(propName, prefix, value) {
23495 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23496 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23497 };
23498
23499 setRs('labelX', null, p.x);
23500 setRs('labelY', null, p.y);
23501 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23502 setRs('labelAutoAngle', null, midAngle);
23503
23504 var createControlPointInfo = function createControlPointInfo() {
23505 if (createControlPointInfo.cache) {
23506 return createControlPointInfo.cache;
23507 } // use cache so only 1x per edge
23508
23509
23510 var ctrlpts = []; // store each ctrlpt info init
23511
23512 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23513 var p0 = {
23514 x: rs.allpts[i],
23515 y: rs.allpts[i + 1]
23516 };
23517 var p1 = {
23518 x: rs.allpts[i + 2],
23519 y: rs.allpts[i + 3]
23520 }; // ctrlpt
23521
23522 var p2 = {
23523 x: rs.allpts[i + 4],
23524 y: rs.allpts[i + 5]
23525 };
23526 ctrlpts.push({
23527 p0: p0,
23528 p1: p1,
23529 p2: p2,
23530 startDist: 0,
23531 length: 0,
23532 segments: []
23533 });
23534 }
23535
23536 var bpts = _p.rstyle.bezierPts;
23537 var nProjs = r.bezierProjPcts.length;
23538
23539 function addSegment(cp, p0, p1, t0, t1) {
23540 var length = dist(p0, p1);
23541 var prevSegment = cp.segments[cp.segments.length - 1];
23542 var segment = {
23543 p0: p0,
23544 p1: p1,
23545 t0: t0,
23546 t1: t1,
23547 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23548 length: length
23549 };
23550 cp.segments.push(segment);
23551 cp.length += length;
23552 } // update each ctrlpt with segment info
23553
23554
23555 for (var _i = 0; _i < ctrlpts.length; _i++) {
23556 var cp = ctrlpts[_i];
23557 var prevCp = ctrlpts[_i - 1];
23558
23559 if (prevCp) {
23560 cp.startDist = prevCp.startDist + prevCp.length;
23561 }
23562
23563 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23564
23565 for (var j = 0; j < nProjs - 1; j++) {
23566 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23567 }
23568
23569 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23570 }
23571
23572 return createControlPointInfo.cache = ctrlpts;
23573 };
23574
23575 var calculateEndProjection = function calculateEndProjection(prefix) {
23576 var angle;
23577 var isSrc = prefix === 'source';
23578
23579 if (!content[prefix]) {
23580 return;
23581 }
23582
23583 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23584
23585 switch (rs.edgeType) {
23586 case 'self':
23587 case 'compound':
23588 case 'bezier':
23589 case 'multibezier':
23590 {
23591 var cps = createControlPointInfo();
23592 var selected;
23593 var startDist = 0;
23594 var totalDist = 0; // find the segment we're on
23595
23596 for (var i = 0; i < cps.length; i++) {
23597 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23598
23599 for (var j = 0; j < _cp.segments.length; j++) {
23600 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23601 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23602 startDist = totalDist;
23603 totalDist += _seg.length;
23604
23605 if (totalDist >= offset || lastSeg) {
23606 selected = {
23607 cp: _cp,
23608 segment: _seg
23609 };
23610 break;
23611 }
23612 }
23613
23614 if (selected) {
23615 break;
23616 }
23617 }
23618
23619 var cp = selected.cp;
23620 var seg = selected.segment;
23621 var tSegment = (offset - startDist) / seg.length;
23622 var segDt = seg.t1 - seg.t0;
23623 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23624 t = bound(0, t, 1);
23625 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23626 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23627 break;
23628 }
23629
23630 case 'straight':
23631 case 'segments':
23632 case 'haystack':
23633 {
23634 var d = 0,
23635 di,
23636 d0;
23637 var p0, p1;
23638 var l = rs.allpts.length;
23639
23640 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23641 if (isSrc) {
23642 p0 = {
23643 x: rs.allpts[_i2],
23644 y: rs.allpts[_i2 + 1]
23645 };
23646 p1 = {
23647 x: rs.allpts[_i2 + 2],
23648 y: rs.allpts[_i2 + 3]
23649 };
23650 } else {
23651 p0 = {
23652 x: rs.allpts[l - 2 - _i2],
23653 y: rs.allpts[l - 1 - _i2]
23654 };
23655 p1 = {
23656 x: rs.allpts[l - 4 - _i2],
23657 y: rs.allpts[l - 3 - _i2]
23658 };
23659 }
23660
23661 di = dist(p0, p1);
23662 d0 = d;
23663 d += di;
23664
23665 if (d >= offset) {
23666 break;
23667 }
23668 }
23669
23670 var pD = offset - d0;
23671
23672 var _t = pD / di;
23673
23674 _t = bound(0, _t, 1);
23675 p = lineAt(p0, p1, _t);
23676 angle = lineAngle(p0, p1);
23677 break;
23678 }
23679 }
23680
23681 setRs('labelX', prefix, p.x);
23682 setRs('labelY', prefix, p.y);
23683 setRs('labelAutoAngle', prefix, angle);
23684 };
23685
23686 calculateEndProjection('source');
23687 calculateEndProjection('target');
23688 this.applyLabelDimensions(edge);
23689};
23690
23691BRp$9.applyLabelDimensions = function (ele) {
23692 this.applyPrefixedLabelDimensions(ele);
23693
23694 if (ele.isEdge()) {
23695 this.applyPrefixedLabelDimensions(ele, 'source');
23696 this.applyPrefixedLabelDimensions(ele, 'target');
23697 }
23698};
23699
23700BRp$9.applyPrefixedLabelDimensions = function (ele, prefix) {
23701 var _p = ele._private;
23702 var text = this.getLabelText(ele, prefix);
23703 var labelDims = this.calculateLabelDimensions(ele, text);
23704 var lineHeight = ele.pstyle('line-height').pfValue;
23705 var textWrap = ele.pstyle('text-wrap').strValue;
23706 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23707 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23708 var normPerLineHeight = labelDims.height / numLines;
23709 var labelLineHeight = normPerLineHeight * lineHeight;
23710 var width = labelDims.width;
23711 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23712 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23713 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23714 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23715 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23716 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23717};
23718
23719BRp$9.getLabelText = function (ele, prefix) {
23720 var _p = ele._private;
23721 var pfd = prefix ? prefix + '-' : '';
23722 var text = ele.pstyle(pfd + 'label').strValue;
23723 var textTransform = ele.pstyle('text-transform').value;
23724
23725 var rscratch = function rscratch(propName, value) {
23726 if (value) {
23727 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23728 return value;
23729 } else {
23730 return getPrefixedProperty(_p.rscratch, propName, prefix);
23731 }
23732 }; // for empty text, skip all processing
23733
23734
23735 if (!text) {
23736 return '';
23737 }
23738
23739 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23740 text = text.toUpperCase();
23741 } else if (textTransform == 'lowercase') {
23742 text = text.toLowerCase();
23743 }
23744
23745 var wrapStyle = ele.pstyle('text-wrap').value;
23746
23747 if (wrapStyle === 'wrap') {
23748 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23749
23750 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23751 return rscratch('labelWrapCachedText');
23752 }
23753
23754 var zwsp = "\u200B";
23755 var lines = text.split('\n');
23756 var maxW = ele.pstyle('text-max-width').pfValue;
23757 var overflow = ele.pstyle('text-overflow-wrap').value;
23758 var overflowAny = overflow === 'anywhere';
23759 var wrappedLines = [];
23760 var wordsRegex = /[\s\u200b]+/;
23761 var wordSeparator = overflowAny ? '' : ' ';
23762
23763 for (var l = 0; l < lines.length; l++) {
23764 var line = lines[l];
23765 var lineDims = this.calculateLabelDimensions(ele, line);
23766 var lineW = lineDims.width;
23767
23768 if (overflowAny) {
23769 var processedLine = line.split('').join(zwsp);
23770 line = processedLine;
23771 }
23772
23773 if (lineW > maxW) {
23774 // line is too long
23775 var words = line.split(wordsRegex);
23776 var subline = '';
23777
23778 for (var w = 0; w < words.length; w++) {
23779 var word = words[w];
23780 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23781 var testDims = this.calculateLabelDimensions(ele, testLine);
23782 var testW = testDims.width;
23783
23784 if (testW <= maxW) {
23785 // word fits on current line
23786 subline += word + wordSeparator;
23787 } else {
23788 // word starts new line
23789 if (subline) {
23790 wrappedLines.push(subline);
23791 }
23792
23793 subline = word + wordSeparator;
23794 }
23795 } // if there's remaining text, put it in a wrapped line
23796
23797
23798 if (!subline.match(/^[\s\u200b]+$/)) {
23799 wrappedLines.push(subline);
23800 }
23801 } else {
23802 // line is already short enough
23803 wrappedLines.push(line);
23804 }
23805 } // for
23806
23807
23808 rscratch('labelWrapCachedLines', wrappedLines);
23809 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23810 rscratch('labelWrapKey', labelKey);
23811 } else if (wrapStyle === 'ellipsis') {
23812 var _maxW = ele.pstyle('text-max-width').pfValue;
23813 var ellipsized = '';
23814 var ellipsis = "\u2026";
23815 var incLastCh = false;
23816
23817 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23818 // the label already fits
23819 return text;
23820 }
23821
23822 for (var i = 0; i < text.length; i++) {
23823 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23824
23825 if (widthWithNextCh > _maxW) {
23826 break;
23827 }
23828
23829 ellipsized += text[i];
23830
23831 if (i === text.length - 1) {
23832 incLastCh = true;
23833 }
23834 }
23835
23836 if (!incLastCh) {
23837 ellipsized += ellipsis;
23838 }
23839
23840 return ellipsized;
23841 } // if ellipsize
23842
23843
23844 return text;
23845};
23846
23847BRp$9.getLabelJustification = function (ele) {
23848 var justification = ele.pstyle('text-justification').strValue;
23849 var textHalign = ele.pstyle('text-halign').strValue;
23850
23851 if (justification === 'auto') {
23852 if (ele.isNode()) {
23853 switch (textHalign) {
23854 case 'left':
23855 return 'right';
23856
23857 case 'right':
23858 return 'left';
23859
23860 default:
23861 return 'center';
23862 }
23863 } else {
23864 return 'center';
23865 }
23866 } else {
23867 return justification;
23868 }
23869};
23870
23871BRp$9.calculateLabelDimensions = function (ele, text) {
23872 var r = this;
23873 var cacheKey = hashString(text, ele._private.labelDimsKey);
23874 var cache = r.labelDimCache || (r.labelDimCache = []);
23875 var existingVal = cache[cacheKey];
23876
23877 if (existingVal != null) {
23878 return existingVal;
23879 }
23880
23881 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23882
23883 var fStyle = ele.pstyle('font-style').strValue;
23884 var size = ele.pstyle('font-size').pfValue;
23885 var family = ele.pstyle('font-family').strValue;
23886 var weight = ele.pstyle('font-weight').strValue;
23887 var canvas = this.labelCalcCanvas;
23888 var c2d = this.labelCalcCanvasContext;
23889
23890 if (!canvas) {
23891 canvas = this.labelCalcCanvas = document.createElement('canvas');
23892 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23893 var ds = canvas.style;
23894 ds.position = 'absolute';
23895 ds.left = '-9999px';
23896 ds.top = '-9999px';
23897 ds.zIndex = '-1';
23898 ds.visibility = 'hidden';
23899 ds.pointerEvents = 'none';
23900 }
23901
23902 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23903 var width = 0;
23904 var height = 0;
23905 var lines = text.split('\n');
23906
23907 for (var i = 0; i < lines.length; i++) {
23908 var line = lines[i];
23909 var metrics = c2d.measureText(line);
23910 var w = Math.ceil(metrics.width);
23911 var h = size;
23912 width = Math.max(w, width);
23913 height += h;
23914 }
23915
23916 width += padding;
23917 height += padding;
23918 return cache[cacheKey] = {
23919 width: width,
23920 height: height
23921 };
23922};
23923
23924BRp$9.calculateLabelAngle = function (ele, prefix) {
23925 var _p = ele._private;
23926 var rs = _p.rscratch;
23927 var isEdge = ele.isEdge();
23928 var prefixDash = prefix ? prefix + '-' : '';
23929 var rot = ele.pstyle(prefixDash + 'text-rotation');
23930 var rotStr = rot.strValue;
23931
23932 if (rotStr === 'none') {
23933 return 0;
23934 } else if (isEdge && rotStr === 'autorotate') {
23935 return rs.labelAutoAngle;
23936 } else if (rotStr === 'autorotate') {
23937 return 0;
23938 } else {
23939 return rot.pfValue;
23940 }
23941};
23942
23943BRp$9.calculateLabelAngles = function (ele) {
23944 var r = this;
23945 var isEdge = ele.isEdge();
23946 var _p = ele._private;
23947 var rs = _p.rscratch;
23948 rs.labelAngle = r.calculateLabelAngle(ele);
23949
23950 if (isEdge) {
23951 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23952 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23953 }
23954};
23955
23956var BRp$8 = {};
23957var TOO_SMALL_CUT_RECT = 28;
23958var warnedCutRect = false;
23959
23960BRp$8.getNodeShape = function (node) {
23961 var r = this;
23962 var shape = node.pstyle('shape').value;
23963
23964 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23965 if (!warnedCutRect) {
23966 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23967 warnedCutRect = true;
23968 }
23969
23970 return 'rectangle';
23971 }
23972
23973 if (node.isParent()) {
23974 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23975 return shape;
23976 } else {
23977 return 'rectangle';
23978 }
23979 }
23980
23981 if (shape === 'polygon') {
23982 var points = node.pstyle('shape-polygon-points').value;
23983 return r.nodeShapes.makePolygon(points).name;
23984 }
23985
23986 return shape;
23987};
23988
23989var BRp$7 = {};
23990
23991BRp$7.registerCalculationListeners = function () {
23992 var cy = this.cy;
23993 var elesToUpdate = cy.collection();
23994 var r = this;
23995
23996 var enqueue = function enqueue(eles) {
23997 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23998 elesToUpdate.merge(eles);
23999
24000 if (dirtyStyleCaches) {
24001 for (var i = 0; i < eles.length; i++) {
24002 var ele = eles[i];
24003 var _p = ele._private;
24004 var rstyle = _p.rstyle;
24005 rstyle.clean = false;
24006 rstyle.cleanConnected = false;
24007 }
24008 }
24009 };
24010
24011 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
24012 var ele = e.target;
24013 enqueue(ele);
24014 }).on('style.* background.*', function onDirtyStyle(e) {
24015 var ele = e.target;
24016 enqueue(ele, false);
24017 });
24018
24019 var updateEleCalcs = function updateEleCalcs(willDraw) {
24020 if (willDraw) {
24021 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
24022 // before calculating rendered style (and pstyle might not be called yet)
24023
24024 elesToUpdate.cleanStyle();
24025
24026 for (var i = 0; i < elesToUpdate.length; i++) {
24027 var ele = elesToUpdate[i];
24028 var rstyle = ele._private.rstyle;
24029
24030 if (ele.isNode() && !rstyle.cleanConnected) {
24031 enqueue(ele.connectedEdges());
24032 rstyle.cleanConnected = true;
24033 }
24034 }
24035
24036 if (fns) {
24037 for (var _i = 0; _i < fns.length; _i++) {
24038 var fn = fns[_i];
24039 fn(willDraw, elesToUpdate);
24040 }
24041 }
24042
24043 r.recalculateRenderedStyle(elesToUpdate);
24044 elesToUpdate = cy.collection();
24045 }
24046 };
24047
24048 r.flushRenderedStyleQueue = function () {
24049 updateEleCalcs(true);
24050 };
24051
24052 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24053};
24054
24055BRp$7.onUpdateEleCalcs = function (fn) {
24056 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24057 fns.push(fn);
24058};
24059
24060BRp$7.recalculateRenderedStyle = function (eles, useCache) {
24061 var isCleanConnected = function isCleanConnected(ele) {
24062 return ele._private.rstyle.cleanConnected;
24063 };
24064
24065 var edges = [];
24066 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24067
24068 if (this.destroyed) {
24069 return;
24070 } // use cache by default for perf
24071
24072
24073 if (useCache === undefined) {
24074 useCache = true;
24075 }
24076
24077 for (var i = 0; i < eles.length; i++) {
24078 var ele = eles[i];
24079 var _p = ele._private;
24080 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
24081 // (and a request for recalc may come in between frames)
24082
24083 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24084 rstyle.clean = false;
24085 } // only update if dirty and in graph
24086
24087
24088 if (useCache && rstyle.clean || ele.removed()) {
24089 continue;
24090 } // only update if not display: none
24091
24092
24093 if (ele.pstyle('display').value === 'none') {
24094 continue;
24095 }
24096
24097 if (_p.group === 'nodes') {
24098 nodes.push(ele);
24099 } else {
24100 // edges
24101 edges.push(ele);
24102 }
24103
24104 rstyle.clean = true;
24105 } // update node data from projections
24106
24107
24108 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24109 var _ele = nodes[_i2];
24110 var _p2 = _ele._private;
24111 var _rstyle = _p2.rstyle;
24112
24113 var pos = _ele.position();
24114
24115 this.recalculateNodeLabelProjection(_ele);
24116 _rstyle.nodeX = pos.x;
24117 _rstyle.nodeY = pos.y;
24118 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24119 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24120 }
24121
24122 this.recalculateEdgeProjections(edges); // update edge data from projections
24123
24124 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24125 var _ele2 = edges[_i3];
24126 var _p3 = _ele2._private;
24127 var _rstyle2 = _p3.rstyle;
24128 var rs = _p3.rscratch; // update rstyle positions
24129
24130 _rstyle2.srcX = rs.arrowStartX;
24131 _rstyle2.srcY = rs.arrowStartY;
24132 _rstyle2.tgtX = rs.arrowEndX;
24133 _rstyle2.tgtY = rs.arrowEndY;
24134 _rstyle2.midX = rs.midX;
24135 _rstyle2.midY = rs.midY;
24136 _rstyle2.labelAngle = rs.labelAngle;
24137 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24138 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24139 }
24140};
24141
24142var BRp$6 = {};
24143
24144BRp$6.updateCachedGrabbedEles = function () {
24145 var eles = this.cachedZSortedEles;
24146
24147 if (!eles) {
24148 // just let this be recalculated on the next z sort tick
24149 return;
24150 }
24151
24152 eles.drag = [];
24153 eles.nondrag = [];
24154 var grabTargets = [];
24155
24156 for (var i = 0; i < eles.length; i++) {
24157 var ele = eles[i];
24158 var rs = ele._private.rscratch;
24159
24160 if (ele.grabbed() && !ele.isParent()) {
24161 grabTargets.push(ele);
24162 } else if (rs.inDragLayer) {
24163 eles.drag.push(ele);
24164 } else {
24165 eles.nondrag.push(ele);
24166 }
24167 } // put the grab target nodes last so it's on top of its neighbourhood
24168
24169
24170 for (var i = 0; i < grabTargets.length; i++) {
24171 var ele = grabTargets[i];
24172 eles.drag.push(ele);
24173 }
24174};
24175
24176BRp$6.invalidateCachedZSortedEles = function () {
24177 this.cachedZSortedEles = null;
24178};
24179
24180BRp$6.getCachedZSortedEles = function (forceRecalc) {
24181 if (forceRecalc || !this.cachedZSortedEles) {
24182 var eles = this.cy.mutableElements().toArray();
24183 eles.sort(zIndexSort);
24184 eles.interactive = eles.filter(function (ele) {
24185 return ele.interactive();
24186 });
24187 this.cachedZSortedEles = eles;
24188 this.updateCachedGrabbedEles();
24189 } else {
24190 eles = this.cachedZSortedEles;
24191 }
24192
24193 return eles;
24194};
24195
24196var BRp$5 = {};
24197[BRp$e, BRp$d, BRp$c, BRp$b, BRp$a, BRp$9, BRp$8, BRp$7, BRp$6].forEach(function (props) {
24198 extend(BRp$5, props);
24199});
24200
24201var BRp$4 = {};
24202
24203BRp$4.getCachedImage = function (url, crossOrigin, onLoad) {
24204 var r = this;
24205 var imageCache = r.imageCache = r.imageCache || {};
24206 var cache = imageCache[url];
24207
24208 if (cache) {
24209 if (!cache.image.complete) {
24210 cache.image.addEventListener('load', onLoad);
24211 }
24212
24213 return cache.image;
24214 } else {
24215 cache = imageCache[url] = imageCache[url] || {};
24216 var image = cache.image = new Image(); // eslint-disable-line no-undef
24217
24218 image.addEventListener('load', onLoad);
24219 image.addEventListener('error', function () {
24220 image.error = true;
24221 }); // #1582 safari doesn't load data uris with crossOrigin properly
24222 // https://bugs.webkit.org/show_bug.cgi?id=123978
24223
24224 var dataUriPrefix = 'data:';
24225 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24226
24227 if (!isDataUri) {
24228 image.crossOrigin = crossOrigin; // prevent tainted canvas
24229 }
24230
24231 image.src = url;
24232 return image;
24233 }
24234};
24235
24236var BRp$3 = {};
24237/* global document, window, ResizeObserver, MutationObserver */
24238
24239BRp$3.registerBinding = function (target, event, handler, useCapture) {
24240 // eslint-disable-line no-unused-vars
24241 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24242
24243 var b = this.binder(target);
24244 return b.on.apply(b, args);
24245};
24246
24247BRp$3.binder = function (tgt) {
24248 var r = this;
24249 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24250
24251 if (r.supportsPassiveEvents == null) {
24252 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24253 var supportsPassive = false;
24254
24255 try {
24256 var opts = Object.defineProperty({}, 'passive', {
24257 get: function get() {
24258 supportsPassive = true;
24259 return true;
24260 }
24261 });
24262 window.addEventListener('test', null, opts);
24263 } catch (err) {// not supported
24264 }
24265
24266 r.supportsPassiveEvents = supportsPassive;
24267 }
24268
24269 var on = function on(event, handler, useCapture) {
24270 var args = Array.prototype.slice.call(arguments);
24271
24272 if (tgtIsDom && r.supportsPassiveEvents) {
24273 // replace useCapture w/ opts obj
24274 args[2] = {
24275 capture: useCapture != null ? useCapture : false,
24276 passive: false,
24277 once: false
24278 };
24279 }
24280
24281 r.bindings.push({
24282 target: tgt,
24283 args: args
24284 });
24285 (tgt.addEventListener || tgt.on).apply(tgt, args);
24286 return this;
24287 };
24288
24289 return {
24290 on: on,
24291 addEventListener: on,
24292 addListener: on,
24293 bind: on
24294 };
24295};
24296
24297BRp$3.nodeIsDraggable = function (node) {
24298 return node && node.isNode() && !node.locked() && node.grabbable();
24299};
24300
24301BRp$3.nodeIsGrabbable = function (node) {
24302 return this.nodeIsDraggable(node) && node.interactive();
24303};
24304
24305BRp$3.load = function () {
24306 var r = this;
24307
24308 var isSelected = function isSelected(ele) {
24309 return ele.selected();
24310 };
24311
24312 var triggerEvents = function triggerEvents(target, names, e, position) {
24313 if (target == null) {
24314 target = r.cy;
24315 }
24316
24317 for (var i = 0; i < names.length; i++) {
24318 var name = names[i];
24319 target.emit({
24320 originalEvent: e,
24321 type: name,
24322 position: position
24323 });
24324 }
24325 };
24326
24327 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24328 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24329 };
24330
24331 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24332 var allowPassthrough = true;
24333
24334 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24335 // a grabbable compound node below the ele => no passthrough panning
24336 for (var i = 0; downs && i < downs.length; i++) {
24337 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
24338
24339 if (down.isNode() && down.isParent() && !down.pannable()) {
24340 allowPassthrough = false;
24341 break;
24342 }
24343 }
24344 } else {
24345 allowPassthrough = true;
24346 }
24347
24348 return allowPassthrough;
24349 };
24350
24351 var setGrabbed = function setGrabbed(ele) {
24352 ele[0]._private.grabbed = true;
24353 };
24354
24355 var setFreed = function setFreed(ele) {
24356 ele[0]._private.grabbed = false;
24357 };
24358
24359 var setInDragLayer = function setInDragLayer(ele) {
24360 ele[0]._private.rscratch.inDragLayer = true;
24361 };
24362
24363 var setOutDragLayer = function setOutDragLayer(ele) {
24364 ele[0]._private.rscratch.inDragLayer = false;
24365 };
24366
24367 var setGrabTarget = function setGrabTarget(ele) {
24368 ele[0]._private.rscratch.isGrabTarget = true;
24369 };
24370
24371 var removeGrabTarget = function removeGrabTarget(ele) {
24372 ele[0]._private.rscratch.isGrabTarget = false;
24373 };
24374
24375 var addToDragList = function addToDragList(ele, opts) {
24376 var list = opts.addToList;
24377 var listHasEle = list.has(ele);
24378
24379 if (!listHasEle && ele.grabbable() && !ele.locked()) {
24380 list.merge(ele);
24381 setGrabbed(ele);
24382 }
24383 }; // helper function to determine which child nodes and inner edges
24384 // of a compound node to be dragged as well as the grabbed and selected nodes
24385
24386
24387 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24388 if (!node.cy().hasCompoundNodes()) {
24389 return;
24390 }
24391
24392 if (opts.inDragLayer == null && opts.addToList == null) {
24393 return;
24394 } // nothing to do
24395
24396
24397 var innerNodes = node.descendants();
24398
24399 if (opts.inDragLayer) {
24400 innerNodes.forEach(setInDragLayer);
24401 innerNodes.connectedEdges().forEach(setInDragLayer);
24402 }
24403
24404 if (opts.addToList) {
24405 addToDragList(innerNodes, opts);
24406 }
24407 }; // adds the given nodes and its neighbourhood to the drag layer
24408
24409
24410 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24411 opts = opts || {};
24412 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24413
24414 if (opts.inDragLayer) {
24415 nodes.forEach(setInDragLayer);
24416 nodes.neighborhood().stdFilter(function (ele) {
24417 return !hasCompoundNodes || ele.isEdge();
24418 }).forEach(setInDragLayer);
24419 }
24420
24421 if (opts.addToList) {
24422 nodes.forEach(function (ele) {
24423 addToDragList(ele, opts);
24424 });
24425 }
24426
24427 addDescendantsToDrag(nodes, opts); // always add to drag
24428 // also add nodes and edges related to the topmost ancestor
24429
24430 updateAncestorsInDragLayer(nodes, {
24431 inDragLayer: opts.inDragLayer
24432 });
24433 r.updateCachedGrabbedEles();
24434 };
24435
24436 var addNodeToDrag = addNodesToDrag;
24437
24438 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24439 if (!grabbedEles) {
24440 return;
24441 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24442
24443
24444 r.getCachedZSortedEles().forEach(function (ele) {
24445 setFreed(ele);
24446 setOutDragLayer(ele);
24447 removeGrabTarget(ele);
24448 });
24449 r.updateCachedGrabbedEles();
24450 }; // helper function to determine which ancestor nodes and edges should go
24451 // to the drag layer (or should be removed from drag layer).
24452
24453
24454 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24455 if (opts.inDragLayer == null && opts.addToList == null) {
24456 return;
24457 } // nothing to do
24458
24459
24460 if (!node.cy().hasCompoundNodes()) {
24461 return;
24462 } // find top-level parent
24463
24464
24465 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24466
24467 if (parent.same(node)) {
24468 return;
24469 }
24470
24471 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24472 var edges = nodes.connectedEdges();
24473
24474 if (opts.inDragLayer) {
24475 edges.forEach(setInDragLayer);
24476 nodes.forEach(setInDragLayer);
24477 }
24478
24479 if (opts.addToList) {
24480 nodes.forEach(function (ele) {
24481 addToDragList(ele, opts);
24482 });
24483 }
24484 };
24485
24486 var blurActiveDomElement = function blurActiveDomElement() {
24487 if (document.activeElement != null && document.activeElement.blur != null) {
24488 document.activeElement.blur();
24489 }
24490 };
24491
24492 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24493 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24494
24495 if (haveMutationsApi) {
24496 r.removeObserver = new MutationObserver(function (mutns) {
24497 // eslint-disable-line no-undef
24498 for (var i = 0; i < mutns.length; i++) {
24499 var mutn = mutns[i];
24500 var rNodes = mutn.removedNodes;
24501
24502 if (rNodes) {
24503 for (var j = 0; j < rNodes.length; j++) {
24504 var rNode = rNodes[j];
24505
24506 if (rNode === r.container) {
24507 r.destroy();
24508 break;
24509 }
24510 }
24511 }
24512 }
24513 });
24514
24515 if (r.container.parentNode) {
24516 r.removeObserver.observe(r.container.parentNode, {
24517 childList: true
24518 });
24519 }
24520 } else {
24521 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24522 // eslint-disable-line no-unused-vars
24523 r.destroy();
24524 });
24525 }
24526
24527 var onResize = debounce__default["default"](function () {
24528 r.cy.resize();
24529 }, 100);
24530
24531 if (haveMutationsApi) {
24532 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24533
24534 r.styleObserver.observe(r.container, {
24535 attributes: true
24536 });
24537 } // auto resize
24538
24539
24540 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24541
24542 if (haveResizeObserverApi) {
24543 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24544
24545 r.resizeObserver.observe(r.container);
24546 }
24547
24548 var forEachUp = function forEachUp(domEle, fn) {
24549 while (domEle != null) {
24550 fn(domEle);
24551 domEle = domEle.parentNode;
24552 }
24553 };
24554
24555 var invalidateCoords = function invalidateCoords() {
24556 r.invalidateContainerClientCoordsCache();
24557 };
24558
24559 forEachUp(r.container, function (domEle) {
24560 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24561 r.registerBinding(domEle, 'animationend', invalidateCoords);
24562 r.registerBinding(domEle, 'scroll', invalidateCoords);
24563 }); // stop right click menu from appearing on cy
24564
24565 r.registerBinding(r.container, 'contextmenu', function (e) {
24566 e.preventDefault();
24567 });
24568
24569 var inBoxSelection = function inBoxSelection() {
24570 return r.selection[4] !== 0;
24571 };
24572
24573 var eventInContainer = function eventInContainer(e) {
24574 // save cycles if mouse events aren't to be captured
24575 var containerPageCoords = r.findContainerClientCoords();
24576 var x = containerPageCoords[0];
24577 var y = containerPageCoords[1];
24578 var width = containerPageCoords[2];
24579 var height = containerPageCoords[3];
24580 var positions = e.touches ? e.touches : [e];
24581 var atLeastOnePosInside = false;
24582
24583 for (var i = 0; i < positions.length; i++) {
24584 var p = positions[i];
24585
24586 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24587 atLeastOnePosInside = true;
24588 break;
24589 }
24590 }
24591
24592 if (!atLeastOnePosInside) {
24593 return false;
24594 }
24595
24596 var container = r.container;
24597 var target = e.target;
24598 var tParent = target.parentNode;
24599 var containerIsTarget = false;
24600
24601 while (tParent) {
24602 if (tParent === container) {
24603 containerIsTarget = true;
24604 break;
24605 }
24606
24607 tParent = tParent.parentNode;
24608 }
24609
24610 if (!containerIsTarget) {
24611 return false;
24612 } // if target is outisde cy container, then this event is not for us
24613
24614
24615 return true;
24616 }; // Primary key
24617
24618
24619 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24620 if (!eventInContainer(e)) {
24621 return;
24622 }
24623
24624 e.preventDefault();
24625 blurActiveDomElement();
24626 r.hoverData.capture = true;
24627 r.hoverData.which = e.which;
24628 var cy = r.cy;
24629 var gpos = [e.clientX, e.clientY];
24630 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24631 var select = r.selection;
24632 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24633 var near = nears[0];
24634 var draggedElements = r.dragData.possibleDragElements;
24635 r.hoverData.mdownPos = pos;
24636 r.hoverData.mdownGPos = gpos;
24637
24638 var checkForTaphold = function checkForTaphold() {
24639 r.hoverData.tapholdCancelled = false;
24640 clearTimeout(r.hoverData.tapholdTimeout);
24641 r.hoverData.tapholdTimeout = setTimeout(function () {
24642 if (r.hoverData.tapholdCancelled) {
24643 return;
24644 } else {
24645 var ele = r.hoverData.down;
24646
24647 if (ele) {
24648 ele.emit({
24649 originalEvent: e,
24650 type: 'taphold',
24651 position: {
24652 x: pos[0],
24653 y: pos[1]
24654 }
24655 });
24656 } else {
24657 cy.emit({
24658 originalEvent: e,
24659 type: 'taphold',
24660 position: {
24661 x: pos[0],
24662 y: pos[1]
24663 }
24664 });
24665 }
24666 }
24667 }, r.tapholdDuration);
24668 }; // Right click button
24669
24670
24671 if (e.which == 3) {
24672 r.hoverData.cxtStarted = true;
24673 var cxtEvt = {
24674 originalEvent: e,
24675 type: 'cxttapstart',
24676 position: {
24677 x: pos[0],
24678 y: pos[1]
24679 }
24680 };
24681
24682 if (near) {
24683 near.activate();
24684 near.emit(cxtEvt);
24685 r.hoverData.down = near;
24686 } else {
24687 cy.emit(cxtEvt);
24688 }
24689
24690 r.hoverData.downTime = new Date().getTime();
24691 r.hoverData.cxtDragged = false; // Primary button
24692 } else if (e.which == 1) {
24693 if (near) {
24694 near.activate();
24695 } // Element dragging
24696
24697
24698 {
24699 // If something is under the cursor and it is draggable, prepare to grab it
24700 if (near != null) {
24701 if (r.nodeIsGrabbable(near)) {
24702 var makeEvent = function makeEvent(type) {
24703 return {
24704 originalEvent: e,
24705 type: type,
24706 position: {
24707 x: pos[0],
24708 y: pos[1]
24709 }
24710 };
24711 };
24712
24713 var triggerGrab = function triggerGrab(ele) {
24714 ele.emit(makeEvent('grab'));
24715 };
24716
24717 setGrabTarget(near);
24718
24719 if (!near.selected()) {
24720 draggedElements = r.dragData.possibleDragElements = cy.collection();
24721 addNodeToDrag(near, {
24722 addToList: draggedElements
24723 });
24724 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24725 } else {
24726 draggedElements = r.dragData.possibleDragElements = cy.collection();
24727 var selectedNodes = cy.$(function (ele) {
24728 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24729 });
24730 addNodesToDrag(selectedNodes, {
24731 addToList: draggedElements
24732 });
24733 near.emit(makeEvent('grabon'));
24734 selectedNodes.forEach(triggerGrab);
24735 }
24736
24737 r.redrawHint('eles', true);
24738 r.redrawHint('drag', true);
24739 }
24740 }
24741
24742 r.hoverData.down = near;
24743 r.hoverData.downs = nears;
24744 r.hoverData.downTime = new Date().getTime();
24745 }
24746 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24747 x: pos[0],
24748 y: pos[1]
24749 });
24750
24751 if (near == null) {
24752 select[4] = 1;
24753 r.data.bgActivePosistion = {
24754 x: pos[0],
24755 y: pos[1]
24756 };
24757 r.redrawHint('select', true);
24758 r.redraw();
24759 } else if (near.pannable()) {
24760 select[4] = 1; // for future pan
24761 }
24762
24763 checkForTaphold();
24764 } // Initialize selection box coordinates
24765
24766
24767 select[0] = select[2] = pos[0];
24768 select[1] = select[3] = pos[1];
24769 }, false);
24770 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24771 // eslint-disable-line no-undef
24772 var capture = r.hoverData.capture;
24773
24774 if (!capture && !eventInContainer(e)) {
24775 return;
24776 }
24777
24778 var preventDefault = false;
24779 var cy = r.cy;
24780 var zoom = cy.zoom();
24781 var gpos = [e.clientX, e.clientY];
24782 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24783 var mdownPos = r.hoverData.mdownPos;
24784 var mdownGPos = r.hoverData.mdownGPos;
24785 var select = r.selection;
24786 var near = null;
24787
24788 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24789 near = r.findNearestElement(pos[0], pos[1], true, false);
24790 }
24791
24792 var last = r.hoverData.last;
24793 var down = r.hoverData.down;
24794 var disp = [pos[0] - select[2], pos[1] - select[3]];
24795 var draggedElements = r.dragData.possibleDragElements;
24796 var isOverThresholdDrag;
24797
24798 if (mdownGPos) {
24799 var dx = gpos[0] - mdownGPos[0];
24800 var dx2 = dx * dx;
24801 var dy = gpos[1] - mdownGPos[1];
24802 var dy2 = dy * dy;
24803 var dist2 = dx2 + dy2;
24804 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24805 }
24806
24807 var multSelKeyDown = isMultSelKeyDown(e);
24808
24809 if (isOverThresholdDrag) {
24810 r.hoverData.tapholdCancelled = true;
24811 }
24812
24813 var updateDragDelta = function updateDragDelta() {
24814 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24815
24816 if (dragDelta.length === 0) {
24817 dragDelta.push(disp[0]);
24818 dragDelta.push(disp[1]);
24819 } else {
24820 dragDelta[0] += disp[0];
24821 dragDelta[1] += disp[1];
24822 }
24823 };
24824
24825 preventDefault = true;
24826 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24827 x: pos[0],
24828 y: pos[1]
24829 });
24830
24831 var goIntoBoxMode = function goIntoBoxMode() {
24832 r.data.bgActivePosistion = undefined;
24833
24834 if (!r.hoverData.selecting) {
24835 cy.emit({
24836 originalEvent: e,
24837 type: 'boxstart',
24838 position: {
24839 x: pos[0],
24840 y: pos[1]
24841 }
24842 });
24843 }
24844
24845 select[4] = 1;
24846 r.hoverData.selecting = true;
24847 r.redrawHint('select', true);
24848 r.redraw();
24849 }; // trigger context drag if rmouse down
24850
24851
24852 if (r.hoverData.which === 3) {
24853 // but only if over threshold
24854 if (isOverThresholdDrag) {
24855 var cxtEvt = {
24856 originalEvent: e,
24857 type: 'cxtdrag',
24858 position: {
24859 x: pos[0],
24860 y: pos[1]
24861 }
24862 };
24863
24864 if (down) {
24865 down.emit(cxtEvt);
24866 } else {
24867 cy.emit(cxtEvt);
24868 }
24869
24870 r.hoverData.cxtDragged = true;
24871
24872 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24873 if (r.hoverData.cxtOver) {
24874 r.hoverData.cxtOver.emit({
24875 originalEvent: e,
24876 type: 'cxtdragout',
24877 position: {
24878 x: pos[0],
24879 y: pos[1]
24880 }
24881 });
24882 }
24883
24884 r.hoverData.cxtOver = near;
24885
24886 if (near) {
24887 near.emit({
24888 originalEvent: e,
24889 type: 'cxtdragover',
24890 position: {
24891 x: pos[0],
24892 y: pos[1]
24893 }
24894 });
24895 }
24896 }
24897 } // Check if we are drag panning the entire graph
24898
24899 } else if (r.hoverData.dragging) {
24900 preventDefault = true;
24901
24902 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24903 var deltaP;
24904
24905 if (r.hoverData.justStartedPan) {
24906 var mdPos = r.hoverData.mdownPos;
24907 deltaP = {
24908 x: (pos[0] - mdPos[0]) * zoom,
24909 y: (pos[1] - mdPos[1]) * zoom
24910 };
24911 r.hoverData.justStartedPan = false;
24912 } else {
24913 deltaP = {
24914 x: disp[0] * zoom,
24915 y: disp[1] * zoom
24916 };
24917 }
24918
24919 cy.panBy(deltaP);
24920 cy.emit('dragpan');
24921 r.hoverData.dragged = true;
24922 } // Needs reproject due to pan changing viewport
24923
24924
24925 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24926 } else if (select[4] == 1 && (down == null || down.pannable())) {
24927 if (isOverThresholdDrag) {
24928 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24929 goIntoBoxMode();
24930 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24931 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24932
24933 if (allowPassthrough) {
24934 r.hoverData.dragging = true;
24935 r.hoverData.justStartedPan = true;
24936 select[4] = 0;
24937 r.data.bgActivePosistion = array2point(mdownPos);
24938 r.redrawHint('select', true);
24939 r.redraw();
24940 }
24941 }
24942
24943 if (down && down.pannable() && down.active()) {
24944 down.unactivate();
24945 }
24946 }
24947 } else {
24948 if (down && down.pannable() && down.active()) {
24949 down.unactivate();
24950 }
24951
24952 if ((!down || !down.grabbed()) && near != last) {
24953 if (last) {
24954 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24955 x: pos[0],
24956 y: pos[1]
24957 });
24958 }
24959
24960 if (near) {
24961 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24962 x: pos[0],
24963 y: pos[1]
24964 });
24965 }
24966
24967 r.hoverData.last = near;
24968 }
24969
24970 if (down) {
24971 if (isOverThresholdDrag) {
24972 // then we can take action
24973 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24974 // then selection overrides
24975 if (down && down.grabbed()) {
24976 freeDraggedElements(draggedElements);
24977 down.emit('freeon');
24978 draggedElements.emit('free');
24979
24980 if (r.dragData.didDrag) {
24981 down.emit('dragfreeon');
24982 draggedElements.emit('dragfree');
24983 }
24984 }
24985
24986 goIntoBoxMode();
24987 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24988 // drag node
24989 var justStartedDrag = !r.dragData.didDrag;
24990
24991 if (justStartedDrag) {
24992 r.redrawHint('eles', true);
24993 }
24994
24995 r.dragData.didDrag = true; // indicate that we actually did drag the node
24996 // now, add the elements to the drag layer if not done already
24997
24998 if (!r.hoverData.draggingEles) {
24999 addNodesToDrag(draggedElements, {
25000 inDragLayer: true
25001 });
25002 }
25003
25004 var totalShift = {
25005 x: 0,
25006 y: 0
25007 };
25008
25009 if (number$1(disp[0]) && number$1(disp[1])) {
25010 totalShift.x += disp[0];
25011 totalShift.y += disp[1];
25012
25013 if (justStartedDrag) {
25014 var dragDelta = r.hoverData.dragDelta;
25015
25016 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
25017 totalShift.x += dragDelta[0];
25018 totalShift.y += dragDelta[1];
25019 }
25020 }
25021 }
25022
25023 r.hoverData.draggingEles = true;
25024 draggedElements.silentShift(totalShift).emit('position drag');
25025 r.redrawHint('drag', true);
25026 r.redraw();
25027 }
25028 } else {
25029 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25030 updateDragDelta();
25031 }
25032 } // prevent the dragging from triggering text selection on the page
25033
25034
25035 preventDefault = true;
25036 }
25037
25038 select[2] = pos[0];
25039 select[3] = pos[1];
25040
25041 if (preventDefault) {
25042 if (e.stopPropagation) e.stopPropagation();
25043 if (e.preventDefault) e.preventDefault();
25044 return false;
25045 }
25046 }, false);
25047 var clickTimeout, didDoubleClick, prevClickTimeStamp;
25048 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
25049 // eslint-disable-line no-undef
25050 var capture = r.hoverData.capture;
25051
25052 if (!capture) {
25053 return;
25054 }
25055
25056 r.hoverData.capture = false;
25057 var cy = r.cy;
25058 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25059 var select = r.selection;
25060 var near = r.findNearestElement(pos[0], pos[1], true, false);
25061 var draggedElements = r.dragData.possibleDragElements;
25062 var down = r.hoverData.down;
25063 var multSelKeyDown = isMultSelKeyDown(e);
25064
25065 if (r.data.bgActivePosistion) {
25066 r.redrawHint('select', true);
25067 r.redraw();
25068 }
25069
25070 r.hoverData.tapholdCancelled = true;
25071 r.data.bgActivePosistion = undefined; // not active bg now
25072
25073 if (down) {
25074 down.unactivate();
25075 }
25076
25077 if (r.hoverData.which === 3) {
25078 var cxtEvt = {
25079 originalEvent: e,
25080 type: 'cxttapend',
25081 position: {
25082 x: pos[0],
25083 y: pos[1]
25084 }
25085 };
25086
25087 if (down) {
25088 down.emit(cxtEvt);
25089 } else {
25090 cy.emit(cxtEvt);
25091 }
25092
25093 if (!r.hoverData.cxtDragged) {
25094 var cxtTap = {
25095 originalEvent: e,
25096 type: 'cxttap',
25097 position: {
25098 x: pos[0],
25099 y: pos[1]
25100 }
25101 };
25102
25103 if (down) {
25104 down.emit(cxtTap);
25105 } else {
25106 cy.emit(cxtTap);
25107 }
25108 }
25109
25110 r.hoverData.cxtDragged = false;
25111 r.hoverData.which = null;
25112 } else if (r.hoverData.which === 1) {
25113 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25114 x: pos[0],
25115 y: pos[1]
25116 });
25117
25118 if (!r.dragData.didDrag && // didn't move a node around
25119 !r.hoverData.dragged && // didn't pan
25120 !r.hoverData.selecting && // not box selection
25121 !r.hoverData.isOverThresholdDrag // didn't move too much
25122 ) {
25123 triggerEvents(down, ["click", "tap", "vclick"], e, {
25124 x: pos[0],
25125 y: pos[1]
25126 });
25127 didDoubleClick = false;
25128
25129 if (e.timeStamp - prevClickTimeStamp <= cy.multiClickDebounceTime()) {
25130 clickTimeout && clearTimeout(clickTimeout);
25131 didDoubleClick = true;
25132 prevClickTimeStamp = null;
25133 triggerEvents(down, ["dblclick", "dbltap", "vdblclick"], e, {
25134 x: pos[0],
25135 y: pos[1]
25136 });
25137 } else {
25138 clickTimeout = setTimeout(function () {
25139 if (didDoubleClick) return;
25140 triggerEvents(down, ["oneclick", "onetap", "voneclick"], e, {
25141 x: pos[0],
25142 y: pos[1]
25143 });
25144 }, cy.multiClickDebounceTime());
25145 prevClickTimeStamp = e.timeStamp;
25146 }
25147 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25148
25149
25150 if (down == null // not mousedown on node
25151 && !r.dragData.didDrag // didn't move the node around
25152 && !r.hoverData.selecting // not box selection
25153 && !r.hoverData.dragged // didn't pan
25154 && !isMultSelKeyDown(e)) {
25155 cy.$(isSelected).unselect(['tapunselect']);
25156
25157 if (draggedElements.length > 0) {
25158 r.redrawHint('eles', true);
25159 }
25160
25161 r.dragData.possibleDragElements = draggedElements = cy.collection();
25162 } // Single selection
25163
25164
25165 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25166 if (near != null && near._private.selectable) {
25167 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25168 if (near.selected()) {
25169 near.unselect(['tapunselect']);
25170 } else {
25171 near.select(['tapselect']);
25172 }
25173 } else {
25174 if (!multSelKeyDown) {
25175 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25176 near.select(['tapselect']);
25177 }
25178 }
25179
25180 r.redrawHint('eles', true);
25181 }
25182 }
25183
25184 if (r.hoverData.selecting) {
25185 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25186 r.redrawHint('select', true);
25187
25188 if (box.length > 0) {
25189 r.redrawHint('eles', true);
25190 }
25191
25192 cy.emit({
25193 type: 'boxend',
25194 originalEvent: e,
25195 position: {
25196 x: pos[0],
25197 y: pos[1]
25198 }
25199 });
25200
25201 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25202 return ele.selectable() && !ele.selected();
25203 };
25204
25205 if (cy.selectionType() === 'additive') {
25206 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25207 } else {
25208 if (!multSelKeyDown) {
25209 cy.$(isSelected).unmerge(box).unselect();
25210 }
25211
25212 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25213 } // always need redraw in case eles unselectable
25214
25215
25216 r.redraw();
25217 } // Cancel drag pan
25218
25219
25220 if (r.hoverData.dragging) {
25221 r.hoverData.dragging = false;
25222 r.redrawHint('select', true);
25223 r.redrawHint('eles', true);
25224 r.redraw();
25225 }
25226
25227 if (!select[4]) {
25228 r.redrawHint('drag', true);
25229 r.redrawHint('eles', true);
25230 var downWasGrabbed = down && down.grabbed();
25231 freeDraggedElements(draggedElements);
25232
25233 if (downWasGrabbed) {
25234 down.emit('freeon');
25235 draggedElements.emit('free');
25236
25237 if (r.dragData.didDrag) {
25238 down.emit('dragfreeon');
25239 draggedElements.emit('dragfree');
25240 }
25241 }
25242 }
25243 } // else not right mouse
25244
25245
25246 select[4] = 0;
25247 r.hoverData.down = null;
25248 r.hoverData.cxtStarted = false;
25249 r.hoverData.draggingEles = false;
25250 r.hoverData.selecting = false;
25251 r.hoverData.isOverThresholdDrag = false;
25252 r.dragData.didDrag = false;
25253 r.hoverData.dragged = false;
25254 r.hoverData.dragDelta = [];
25255 r.hoverData.mdownPos = null;
25256 r.hoverData.mdownGPos = null;
25257 }, false);
25258
25259 var wheelHandler = function wheelHandler(e) {
25260 if (r.scrollingPage) {
25261 return;
25262 } // while scrolling, ignore wheel-to-zoom
25263
25264
25265 var cy = r.cy;
25266 var zoom = cy.zoom();
25267 var pan = cy.pan();
25268 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25269 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25270
25271 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25272 // if pan dragging or cxt dragging, wheel movements make no zoom
25273 e.preventDefault();
25274 return;
25275 }
25276
25277 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25278 e.preventDefault();
25279 r.data.wheelZooming = true;
25280 clearTimeout(r.data.wheelTimeout);
25281 r.data.wheelTimeout = setTimeout(function () {
25282 r.data.wheelZooming = false;
25283 r.redrawHint('eles', true);
25284 r.redraw();
25285 }, 150);
25286 var diff;
25287
25288 if (e.deltaY != null) {
25289 diff = e.deltaY / -250;
25290 } else if (e.wheelDeltaY != null) {
25291 diff = e.wheelDeltaY / 1000;
25292 } else {
25293 diff = e.wheelDelta / 1000;
25294 }
25295
25296 diff = diff * r.wheelSensitivity;
25297 var needsWheelFix = e.deltaMode === 1;
25298
25299 if (needsWheelFix) {
25300 // fixes slow wheel events on ff/linux and ff/windows
25301 diff *= 33;
25302 }
25303
25304 var newZoom = cy.zoom() * Math.pow(10, diff);
25305
25306 if (e.type === 'gesturechange') {
25307 newZoom = r.gestureStartZoom * e.scale;
25308 }
25309
25310 cy.zoom({
25311 level: newZoom,
25312 renderedPosition: {
25313 x: rpos[0],
25314 y: rpos[1]
25315 }
25316 });
25317 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25318 }
25319 }; // Functions to help with whether mouse wheel should trigger zooming
25320 // --
25321
25322
25323 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25324 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25325 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25326 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25327
25328 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25329 // eslint-disable-line no-unused-vars
25330 r.scrollingPage = true;
25331 clearTimeout(r.scrollingPageTimeout);
25332 r.scrollingPageTimeout = setTimeout(function () {
25333 r.scrollingPage = false;
25334 }, 250);
25335 }, true); // desktop safari pinch to zoom start
25336
25337 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25338 r.gestureStartZoom = r.cy.zoom();
25339
25340 if (!r.hasTouchStarted) {
25341 // don't affect touch devices like iphone
25342 e.preventDefault();
25343 }
25344 }, true);
25345 r.registerBinding(r.container, 'gesturechange', function (e) {
25346 if (!r.hasTouchStarted) {
25347 // don't affect touch devices like iphone
25348 wheelHandler(e);
25349 }
25350 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25351 // Handle mouseout on Cytoscape container
25352
25353 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25354 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25355 r.cy.emit({
25356 originalEvent: e,
25357 type: 'mouseout',
25358 position: {
25359 x: pos[0],
25360 y: pos[1]
25361 }
25362 });
25363 }, false);
25364 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25365 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25366 r.cy.emit({
25367 originalEvent: e,
25368 type: 'mouseover',
25369 position: {
25370 x: pos[0],
25371 y: pos[1]
25372 }
25373 });
25374 }, false);
25375 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25376
25377 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25378
25379 var center1, modelCenter1; // center point on start pinch to zoom
25380
25381 var offsetLeft, offsetTop;
25382 var containerWidth, containerHeight;
25383 var twoFingersStartInside;
25384
25385 var distance = function distance(x1, y1, x2, y2) {
25386 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25387 };
25388
25389 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25390 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25391 };
25392
25393 var touchstartHandler;
25394 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25395 r.hasTouchStarted = true;
25396
25397 if (!eventInContainer(e)) {
25398 return;
25399 }
25400
25401 blurActiveDomElement();
25402 r.touchData.capture = true;
25403 r.data.bgActivePosistion = undefined;
25404 var cy = r.cy;
25405 var now = r.touchData.now;
25406 var earlier = r.touchData.earlier;
25407
25408 if (e.touches[0]) {
25409 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25410 now[0] = pos[0];
25411 now[1] = pos[1];
25412 }
25413
25414 if (e.touches[1]) {
25415 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25416 now[2] = pos[0];
25417 now[3] = pos[1];
25418 }
25419
25420 if (e.touches[2]) {
25421 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25422 now[4] = pos[0];
25423 now[5] = pos[1];
25424 } // record starting points for pinch-to-zoom
25425
25426
25427 if (e.touches[1]) {
25428 r.touchData.singleTouchMoved = true;
25429 freeDraggedElements(r.dragData.touchDragEles);
25430 var offsets = r.findContainerClientCoords();
25431 offsetLeft = offsets[0];
25432 offsetTop = offsets[1];
25433 containerWidth = offsets[2];
25434 containerHeight = offsets[3];
25435 f1x1 = e.touches[0].clientX - offsetLeft;
25436 f1y1 = e.touches[0].clientY - offsetTop;
25437 f2x1 = e.touches[1].clientX - offsetLeft;
25438 f2y1 = e.touches[1].clientY - offsetTop;
25439 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25440 var pan = cy.pan();
25441 var zoom = cy.zoom();
25442 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25443 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25444 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25445 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25446
25447 var cxtDistThreshold = 200;
25448 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25449
25450 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25451 var near1 = r.findNearestElement(now[0], now[1], true, true);
25452 var near2 = r.findNearestElement(now[2], now[3], true, true);
25453
25454 if (near1 && near1.isNode()) {
25455 near1.activate().emit({
25456 originalEvent: e,
25457 type: 'cxttapstart',
25458 position: {
25459 x: now[0],
25460 y: now[1]
25461 }
25462 });
25463 r.touchData.start = near1;
25464 } else if (near2 && near2.isNode()) {
25465 near2.activate().emit({
25466 originalEvent: e,
25467 type: 'cxttapstart',
25468 position: {
25469 x: now[0],
25470 y: now[1]
25471 }
25472 });
25473 r.touchData.start = near2;
25474 } else {
25475 cy.emit({
25476 originalEvent: e,
25477 type: 'cxttapstart',
25478 position: {
25479 x: now[0],
25480 y: now[1]
25481 }
25482 });
25483 }
25484
25485 if (r.touchData.start) {
25486 r.touchData.start._private.grabbed = false;
25487 }
25488
25489 r.touchData.cxt = true;
25490 r.touchData.cxtDragged = false;
25491 r.data.bgActivePosistion = undefined;
25492 r.redraw();
25493 return;
25494 }
25495 }
25496
25497 if (e.touches[2]) {
25498 // ignore
25499 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25500 if (cy.boxSelectionEnabled()) {
25501 e.preventDefault();
25502 }
25503 } else if (e.touches[1]) ; else if (e.touches[0]) {
25504 var nears = r.findNearestElements(now[0], now[1], true, true);
25505 var near = nears[0];
25506
25507 if (near != null) {
25508 near.activate();
25509 r.touchData.start = near;
25510 r.touchData.starts = nears;
25511
25512 if (r.nodeIsGrabbable(near)) {
25513 var draggedEles = r.dragData.touchDragEles = cy.collection();
25514 var selectedNodes = null;
25515 r.redrawHint('eles', true);
25516 r.redrawHint('drag', true);
25517
25518 if (near.selected()) {
25519 // reset drag elements, since near will be added again
25520 selectedNodes = cy.$(function (ele) {
25521 return ele.selected() && r.nodeIsGrabbable(ele);
25522 });
25523 addNodesToDrag(selectedNodes, {
25524 addToList: draggedEles
25525 });
25526 } else {
25527 addNodeToDrag(near, {
25528 addToList: draggedEles
25529 });
25530 }
25531
25532 setGrabTarget(near);
25533
25534 var makeEvent = function makeEvent(type) {
25535 return {
25536 originalEvent: e,
25537 type: type,
25538 position: {
25539 x: now[0],
25540 y: now[1]
25541 }
25542 };
25543 };
25544
25545 near.emit(makeEvent('grabon'));
25546
25547 if (selectedNodes) {
25548 selectedNodes.forEach(function (n) {
25549 n.emit(makeEvent('grab'));
25550 });
25551 } else {
25552 near.emit(makeEvent('grab'));
25553 }
25554 }
25555 }
25556
25557 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25558 x: now[0],
25559 y: now[1]
25560 });
25561
25562 if (near == null) {
25563 r.data.bgActivePosistion = {
25564 x: pos[0],
25565 y: pos[1]
25566 };
25567 r.redrawHint('select', true);
25568 r.redraw();
25569 } // Tap, taphold
25570 // -----
25571
25572
25573 r.touchData.singleTouchMoved = false;
25574 r.touchData.singleTouchStartTime = +new Date();
25575 clearTimeout(r.touchData.tapholdTimeout);
25576 r.touchData.tapholdTimeout = setTimeout(function () {
25577 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25578 && !r.touchData.selecting // box selection shouldn't allow taphold through
25579 ) {
25580 triggerEvents(r.touchData.start, ['taphold'], e, {
25581 x: now[0],
25582 y: now[1]
25583 });
25584 }
25585 }, r.tapholdDuration);
25586 }
25587
25588 if (e.touches.length >= 1) {
25589 var sPos = r.touchData.startPosition = [];
25590
25591 for (var i = 0; i < now.length; i++) {
25592 sPos[i] = earlier[i] = now[i];
25593 }
25594
25595 var touch0 = e.touches[0];
25596 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25597 }
25598 }, false);
25599 var touchmoveHandler;
25600 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25601 // eslint-disable-line no-undef
25602 var capture = r.touchData.capture;
25603
25604 if (!capture && !eventInContainer(e)) {
25605 return;
25606 }
25607
25608 var select = r.selection;
25609 var cy = r.cy;
25610 var now = r.touchData.now;
25611 var earlier = r.touchData.earlier;
25612 var zoom = cy.zoom();
25613
25614 if (e.touches[0]) {
25615 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25616 now[0] = pos[0];
25617 now[1] = pos[1];
25618 }
25619
25620 if (e.touches[1]) {
25621 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25622 now[2] = pos[0];
25623 now[3] = pos[1];
25624 }
25625
25626 if (e.touches[2]) {
25627 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25628 now[4] = pos[0];
25629 now[5] = pos[1];
25630 }
25631
25632 var startGPos = r.touchData.startGPosition;
25633 var isOverThresholdDrag;
25634
25635 if (capture && e.touches[0] && startGPos) {
25636 var disp = [];
25637
25638 for (var j = 0; j < now.length; j++) {
25639 disp[j] = now[j] - earlier[j];
25640 }
25641
25642 var dx = e.touches[0].clientX - startGPos[0];
25643 var dx2 = dx * dx;
25644 var dy = e.touches[0].clientY - startGPos[1];
25645 var dy2 = dy * dy;
25646 var dist2 = dx2 + dy2;
25647 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25648 } // context swipe cancelling
25649
25650
25651 if (capture && r.touchData.cxt) {
25652 e.preventDefault();
25653 var f1x2 = e.touches[0].clientX - offsetLeft,
25654 f1y2 = e.touches[0].clientY - offsetTop;
25655 var f2x2 = e.touches[1].clientX - offsetLeft,
25656 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25657
25658 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25659 var factorSq = distance2Sq / distance1Sq;
25660 var distThreshold = 150;
25661 var distThresholdSq = distThreshold * distThreshold;
25662 var factorThreshold = 1.5;
25663 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25664
25665 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25666 r.touchData.cxt = false;
25667 r.data.bgActivePosistion = undefined;
25668 r.redrawHint('select', true);
25669 var cxtEvt = {
25670 originalEvent: e,
25671 type: 'cxttapend',
25672 position: {
25673 x: now[0],
25674 y: now[1]
25675 }
25676 };
25677
25678 if (r.touchData.start) {
25679 r.touchData.start.unactivate().emit(cxtEvt);
25680 r.touchData.start = null;
25681 } else {
25682 cy.emit(cxtEvt);
25683 }
25684 }
25685 } // context swipe
25686
25687
25688 if (capture && r.touchData.cxt) {
25689 var cxtEvt = {
25690 originalEvent: e,
25691 type: 'cxtdrag',
25692 position: {
25693 x: now[0],
25694 y: now[1]
25695 }
25696 };
25697 r.data.bgActivePosistion = undefined;
25698 r.redrawHint('select', true);
25699
25700 if (r.touchData.start) {
25701 r.touchData.start.emit(cxtEvt);
25702 } else {
25703 cy.emit(cxtEvt);
25704 }
25705
25706 if (r.touchData.start) {
25707 r.touchData.start._private.grabbed = false;
25708 }
25709
25710 r.touchData.cxtDragged = true;
25711 var near = r.findNearestElement(now[0], now[1], true, true);
25712
25713 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25714 if (r.touchData.cxtOver) {
25715 r.touchData.cxtOver.emit({
25716 originalEvent: e,
25717 type: 'cxtdragout',
25718 position: {
25719 x: now[0],
25720 y: now[1]
25721 }
25722 });
25723 }
25724
25725 r.touchData.cxtOver = near;
25726
25727 if (near) {
25728 near.emit({
25729 originalEvent: e,
25730 type: 'cxtdragover',
25731 position: {
25732 x: now[0],
25733 y: now[1]
25734 }
25735 });
25736 }
25737 } // box selection
25738
25739 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25740 e.preventDefault();
25741 r.data.bgActivePosistion = undefined;
25742 this.lastThreeTouch = +new Date();
25743
25744 if (!r.touchData.selecting) {
25745 cy.emit({
25746 originalEvent: e,
25747 type: 'boxstart',
25748 position: {
25749 x: now[0],
25750 y: now[1]
25751 }
25752 });
25753 }
25754
25755 r.touchData.selecting = true;
25756 r.touchData.didSelect = true;
25757 select[4] = 1;
25758
25759 if (!select || select.length === 0 || select[0] === undefined) {
25760 select[0] = (now[0] + now[2] + now[4]) / 3;
25761 select[1] = (now[1] + now[3] + now[5]) / 3;
25762 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25763 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25764 } else {
25765 select[2] = (now[0] + now[2] + now[4]) / 3;
25766 select[3] = (now[1] + now[3] + now[5]) / 3;
25767 }
25768
25769 r.redrawHint('select', true);
25770 r.redraw(); // pinch to zoom
25771 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25772 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25773 // two fingers => pinch to zoom
25774 e.preventDefault();
25775 r.data.bgActivePosistion = undefined;
25776 r.redrawHint('select', true);
25777 var draggedEles = r.dragData.touchDragEles;
25778
25779 if (draggedEles) {
25780 r.redrawHint('drag', true);
25781
25782 for (var i = 0; i < draggedEles.length; i++) {
25783 var de_p = draggedEles[i]._private;
25784 de_p.grabbed = false;
25785 de_p.rscratch.inDragLayer = false;
25786 }
25787 }
25788
25789 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25790
25791 var f1x2 = e.touches[0].clientX - offsetLeft,
25792 f1y2 = e.touches[0].clientY - offsetTop;
25793 var f2x2 = e.touches[1].clientX - offsetLeft,
25794 f2y2 = e.touches[1].clientY - offsetTop;
25795 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25796 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25797
25798 var factor = distance2 / distance1;
25799
25800 if (twoFingersStartInside) {
25801 // delta finger1
25802 var df1x = f1x2 - f1x1;
25803 var df1y = f1y2 - f1y1; // delta finger 2
25804
25805 var df2x = f2x2 - f2x1;
25806 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25807 // i.e. so pinching cancels out and moving together pans
25808
25809 var tx = (df1x + df2x) / 2;
25810 var ty = (df1y + df2y) / 2; // now calculate the zoom
25811
25812 var zoom1 = cy.zoom();
25813 var zoom2 = zoom1 * factor;
25814 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25815
25816 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25817 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25818 var pan2 = {
25819 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25820 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25821 }; // remove dragged eles
25822
25823 if (_start && _start.active()) {
25824 var draggedEles = r.dragData.touchDragEles;
25825 freeDraggedElements(draggedEles);
25826 r.redrawHint('drag', true);
25827 r.redrawHint('eles', true);
25828
25829 _start.unactivate().emit('freeon');
25830
25831 draggedEles.emit('free');
25832
25833 if (r.dragData.didDrag) {
25834 _start.emit('dragfreeon');
25835
25836 draggedEles.emit('dragfree');
25837 }
25838 }
25839
25840 cy.viewport({
25841 zoom: zoom2,
25842 pan: pan2,
25843 cancelOnFailedZoom: true
25844 });
25845 cy.emit('pinchzoom');
25846 distance1 = distance2;
25847 f1x1 = f1x2;
25848 f1y1 = f1y2;
25849 f2x1 = f2x2;
25850 f2y1 = f2y2;
25851 r.pinching = true;
25852 } // Re-project
25853
25854
25855 if (e.touches[0]) {
25856 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25857 now[0] = pos[0];
25858 now[1] = pos[1];
25859 }
25860
25861 if (e.touches[1]) {
25862 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25863 now[2] = pos[0];
25864 now[3] = pos[1];
25865 }
25866
25867 if (e.touches[2]) {
25868 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25869 now[4] = pos[0];
25870 now[5] = pos[1];
25871 }
25872 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25873 ) {
25874 var start = r.touchData.start;
25875 var last = r.touchData.last;
25876 var near;
25877
25878 if (!r.hoverData.draggingEles && !r.swipePanning) {
25879 near = r.findNearestElement(now[0], now[1], true, true);
25880 }
25881
25882 if (capture && start != null) {
25883 e.preventDefault();
25884 } // dragging nodes
25885
25886
25887 if (capture && start != null && r.nodeIsDraggable(start)) {
25888 if (isOverThresholdDrag) {
25889 // then dragging can happen
25890 var draggedEles = r.dragData.touchDragEles;
25891 var justStartedDrag = !r.dragData.didDrag;
25892
25893 if (justStartedDrag) {
25894 addNodesToDrag(draggedEles, {
25895 inDragLayer: true
25896 });
25897 }
25898
25899 r.dragData.didDrag = true;
25900 var totalShift = {
25901 x: 0,
25902 y: 0
25903 };
25904
25905 if (number$1(disp[0]) && number$1(disp[1])) {
25906 totalShift.x += disp[0];
25907 totalShift.y += disp[1];
25908
25909 if (justStartedDrag) {
25910 r.redrawHint('eles', true);
25911 var dragDelta = r.touchData.dragDelta;
25912
25913 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
25914 totalShift.x += dragDelta[0];
25915 totalShift.y += dragDelta[1];
25916 }
25917 }
25918 }
25919
25920 r.hoverData.draggingEles = true;
25921 draggedEles.silentShift(totalShift).emit('position drag');
25922 r.redrawHint('drag', true);
25923
25924 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25925 r.redrawHint('eles', true);
25926 }
25927
25928 r.redraw();
25929 } else {
25930 // otherise keep track of drag delta for later
25931 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25932
25933 if (dragDelta.length === 0) {
25934 dragDelta.push(disp[0]);
25935 dragDelta.push(disp[1]);
25936 } else {
25937 dragDelta[0] += disp[0];
25938 dragDelta[1] += disp[1];
25939 }
25940 }
25941 } // touchmove
25942
25943
25944 {
25945 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25946 x: now[0],
25947 y: now[1]
25948 });
25949
25950 if ((!start || !start.grabbed()) && near != last) {
25951 if (last) {
25952 last.emit({
25953 originalEvent: e,
25954 type: 'tapdragout',
25955 position: {
25956 x: now[0],
25957 y: now[1]
25958 }
25959 });
25960 }
25961
25962 if (near) {
25963 near.emit({
25964 originalEvent: e,
25965 type: 'tapdragover',
25966 position: {
25967 x: now[0],
25968 y: now[1]
25969 }
25970 });
25971 }
25972 }
25973
25974 r.touchData.last = near;
25975 } // check to cancel taphold
25976
25977 if (capture) {
25978 for (var i = 0; i < now.length; i++) {
25979 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25980 r.touchData.singleTouchMoved = true;
25981 }
25982 }
25983 } // panning
25984
25985
25986 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25987 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25988
25989 if (allowPassthrough) {
25990 e.preventDefault();
25991
25992 if (!r.data.bgActivePosistion) {
25993 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25994 }
25995
25996 if (r.swipePanning) {
25997 cy.panBy({
25998 x: disp[0] * zoom,
25999 y: disp[1] * zoom
26000 });
26001 cy.emit('dragpan');
26002 } else if (isOverThresholdDrag) {
26003 r.swipePanning = true;
26004 cy.panBy({
26005 x: dx * zoom,
26006 y: dy * zoom
26007 });
26008 cy.emit('dragpan');
26009
26010 if (start) {
26011 start.unactivate();
26012 r.redrawHint('select', true);
26013 r.touchData.start = null;
26014 }
26015 }
26016 } // Re-project
26017
26018
26019 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26020 now[0] = pos[0];
26021 now[1] = pos[1];
26022 }
26023 }
26024
26025 for (var j = 0; j < now.length; j++) {
26026 earlier[j] = now[j];
26027 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
26028
26029
26030 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
26031 r.data.bgActivePosistion = undefined;
26032 r.redrawHint('select', true);
26033 r.redraw();
26034 }
26035 }, false);
26036 var touchcancelHandler;
26037 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
26038 // eslint-disable-line no-unused-vars
26039 var start = r.touchData.start;
26040 r.touchData.capture = false;
26041
26042 if (start) {
26043 start.unactivate();
26044 }
26045 });
26046 var touchendHandler, didDoubleTouch, touchTimeout, prevTouchTimeStamp;
26047 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
26048 // eslint-disable-line no-unused-vars
26049 var start = r.touchData.start;
26050 var capture = r.touchData.capture;
26051
26052 if (capture) {
26053 if (e.touches.length === 0) {
26054 r.touchData.capture = false;
26055 }
26056
26057 e.preventDefault();
26058 } else {
26059 return;
26060 }
26061
26062 var select = r.selection;
26063 r.swipePanning = false;
26064 r.hoverData.draggingEles = false;
26065 var cy = r.cy;
26066 var zoom = cy.zoom();
26067 var now = r.touchData.now;
26068 var earlier = r.touchData.earlier;
26069
26070 if (e.touches[0]) {
26071 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26072 now[0] = pos[0];
26073 now[1] = pos[1];
26074 }
26075
26076 if (e.touches[1]) {
26077 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26078 now[2] = pos[0];
26079 now[3] = pos[1];
26080 }
26081
26082 if (e.touches[2]) {
26083 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26084 now[4] = pos[0];
26085 now[5] = pos[1];
26086 }
26087
26088 if (start) {
26089 start.unactivate();
26090 }
26091
26092 var ctxTapend;
26093
26094 if (r.touchData.cxt) {
26095 ctxTapend = {
26096 originalEvent: e,
26097 type: 'cxttapend',
26098 position: {
26099 x: now[0],
26100 y: now[1]
26101 }
26102 };
26103
26104 if (start) {
26105 start.emit(ctxTapend);
26106 } else {
26107 cy.emit(ctxTapend);
26108 }
26109
26110 if (!r.touchData.cxtDragged) {
26111 var ctxTap = {
26112 originalEvent: e,
26113 type: 'cxttap',
26114 position: {
26115 x: now[0],
26116 y: now[1]
26117 }
26118 };
26119
26120 if (start) {
26121 start.emit(ctxTap);
26122 } else {
26123 cy.emit(ctxTap);
26124 }
26125 }
26126
26127 if (r.touchData.start) {
26128 r.touchData.start._private.grabbed = false;
26129 }
26130
26131 r.touchData.cxt = false;
26132 r.touchData.start = null;
26133 r.redraw();
26134 return;
26135 } // no more box selection if we don't have three fingers
26136
26137
26138 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26139 r.touchData.selecting = false;
26140 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26141 select[0] = undefined;
26142 select[1] = undefined;
26143 select[2] = undefined;
26144 select[3] = undefined;
26145 select[4] = 0;
26146 r.redrawHint('select', true);
26147 cy.emit({
26148 type: 'boxend',
26149 originalEvent: e,
26150 position: {
26151 x: now[0],
26152 y: now[1]
26153 }
26154 });
26155
26156 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26157 return ele.selectable() && !ele.selected();
26158 };
26159
26160 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26161
26162 if (box.nonempty()) {
26163 r.redrawHint('eles', true);
26164 }
26165
26166 r.redraw();
26167 }
26168
26169 if (start != null) {
26170 start.unactivate();
26171 }
26172
26173 if (e.touches[2]) {
26174 r.data.bgActivePosistion = undefined;
26175 r.redrawHint('select', true);
26176 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26177 r.data.bgActivePosistion = undefined;
26178 r.redrawHint('select', true);
26179 var draggedEles = r.dragData.touchDragEles;
26180
26181 if (start != null) {
26182 var startWasGrabbed = start._private.grabbed;
26183 freeDraggedElements(draggedEles);
26184 r.redrawHint('drag', true);
26185 r.redrawHint('eles', true);
26186
26187 if (startWasGrabbed) {
26188 start.emit('freeon');
26189 draggedEles.emit('free');
26190
26191 if (r.dragData.didDrag) {
26192 start.emit('dragfreeon');
26193 draggedEles.emit('dragfree');
26194 }
26195 }
26196
26197 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26198 x: now[0],
26199 y: now[1]
26200 });
26201 start.unactivate();
26202 r.touchData.start = null;
26203 } else {
26204 var near = r.findNearestElement(now[0], now[1], true, true);
26205 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26206 x: now[0],
26207 y: now[1]
26208 });
26209 }
26210
26211 var dx = r.touchData.startPosition[0] - now[0];
26212 var dx2 = dx * dx;
26213 var dy = r.touchData.startPosition[1] - now[1];
26214 var dy2 = dy * dy;
26215 var dist2 = dx2 + dy2;
26216 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26217
26218 if (!r.touchData.singleTouchMoved) {
26219 if (!start) {
26220 cy.$(':selected').unselect(['tapunselect']);
26221 }
26222
26223 triggerEvents(start, ['tap', 'vclick'], e, {
26224 x: now[0],
26225 y: now[1]
26226 });
26227 didDoubleTouch = false;
26228
26229 if (e.timeStamp - prevTouchTimeStamp <= cy.multiClickDebounceTime()) {
26230 touchTimeout && clearTimeout(touchTimeout);
26231 didDoubleTouch = true;
26232 prevTouchTimeStamp = null;
26233 triggerEvents(start, ['dbltap', 'vdblclick'], e, {
26234 x: now[0],
26235 y: now[1]
26236 });
26237 } else {
26238 touchTimeout = setTimeout(function () {
26239 if (didDoubleTouch) return;
26240 triggerEvents(start, ['onetap', 'voneclick'], e, {
26241 x: now[0],
26242 y: now[1]
26243 });
26244 }, cy.multiClickDebounceTime());
26245 prevTouchTimeStamp = e.timeStamp;
26246 }
26247 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26248
26249
26250 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26251 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26252 ) {
26253 if (cy.selectionType() === 'single') {
26254 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26255 start.select(['tapselect']);
26256 } else {
26257 if (start.selected()) {
26258 start.unselect(['tapunselect']);
26259 } else {
26260 start.select(['tapselect']);
26261 }
26262 }
26263
26264 r.redrawHint('eles', true);
26265 }
26266
26267 r.touchData.singleTouchMoved = true;
26268 }
26269
26270 for (var j = 0; j < now.length; j++) {
26271 earlier[j] = now[j];
26272 }
26273
26274 r.dragData.didDrag = false; // reset for next touchstart
26275
26276 if (e.touches.length === 0) {
26277 r.touchData.dragDelta = [];
26278 r.touchData.startPosition = null;
26279 r.touchData.startGPosition = null;
26280 r.touchData.didSelect = false;
26281 }
26282
26283 if (e.touches.length < 2) {
26284 if (e.touches.length === 1) {
26285 // the old start global pos'n may not be the same finger that remains
26286 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26287 }
26288
26289 r.pinching = false;
26290 r.redrawHint('eles', true);
26291 r.redraw();
26292 } //r.redraw();
26293
26294 }, false); // fallback compatibility layer for ms pointer events
26295
26296 if (typeof TouchEvent === 'undefined') {
26297 var pointers = [];
26298
26299 var makeTouch = function makeTouch(e) {
26300 return {
26301 clientX: e.clientX,
26302 clientY: e.clientY,
26303 force: 1,
26304 identifier: e.pointerId,
26305 pageX: e.pageX,
26306 pageY: e.pageY,
26307 radiusX: e.width / 2,
26308 radiusY: e.height / 2,
26309 screenX: e.screenX,
26310 screenY: e.screenY,
26311 target: e.target
26312 };
26313 };
26314
26315 var makePointer = function makePointer(e) {
26316 return {
26317 event: e,
26318 touch: makeTouch(e)
26319 };
26320 };
26321
26322 var addPointer = function addPointer(e) {
26323 pointers.push(makePointer(e));
26324 };
26325
26326 var removePointer = function removePointer(e) {
26327 for (var i = 0; i < pointers.length; i++) {
26328 var p = pointers[i];
26329
26330 if (p.event.pointerId === e.pointerId) {
26331 pointers.splice(i, 1);
26332 return;
26333 }
26334 }
26335 };
26336
26337 var updatePointer = function updatePointer(e) {
26338 var p = pointers.filter(function (p) {
26339 return p.event.pointerId === e.pointerId;
26340 })[0];
26341 p.event = e;
26342 p.touch = makeTouch(e);
26343 };
26344
26345 var addTouchesToEvent = function addTouchesToEvent(e) {
26346 e.touches = pointers.map(function (p) {
26347 return p.touch;
26348 });
26349 };
26350
26351 var pointerIsMouse = function pointerIsMouse(e) {
26352 return e.pointerType === 'mouse' || e.pointerType === 4;
26353 };
26354
26355 r.registerBinding(r.container, 'pointerdown', function (e) {
26356 if (pointerIsMouse(e)) {
26357 return;
26358 } // mouse already handled
26359
26360
26361 e.preventDefault();
26362 addPointer(e);
26363 addTouchesToEvent(e);
26364 touchstartHandler(e);
26365 });
26366 r.registerBinding(r.container, 'pointerup', function (e) {
26367 if (pointerIsMouse(e)) {
26368 return;
26369 } // mouse already handled
26370
26371
26372 removePointer(e);
26373 addTouchesToEvent(e);
26374 touchendHandler(e);
26375 });
26376 r.registerBinding(r.container, 'pointercancel', function (e) {
26377 if (pointerIsMouse(e)) {
26378 return;
26379 } // mouse already handled
26380
26381
26382 removePointer(e);
26383 addTouchesToEvent(e);
26384 touchcancelHandler(e);
26385 });
26386 r.registerBinding(r.container, 'pointermove', function (e) {
26387 if (pointerIsMouse(e)) {
26388 return;
26389 } // mouse already handled
26390
26391
26392 e.preventDefault();
26393 updatePointer(e);
26394 addTouchesToEvent(e);
26395 touchmoveHandler(e);
26396 });
26397 }
26398};
26399
26400var BRp$2 = {};
26401
26402BRp$2.generatePolygon = function (name, points) {
26403 return this.nodeShapes[name] = {
26404 renderer: this,
26405 name: name,
26406 points: points,
26407 draw: function draw(context, centerX, centerY, width, height) {
26408 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26409 },
26410 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26411 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26412 },
26413 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26414 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26415 }
26416 };
26417};
26418
26419BRp$2.generateEllipse = function () {
26420 return this.nodeShapes['ellipse'] = {
26421 renderer: this,
26422 name: 'ellipse',
26423 draw: function draw(context, centerX, centerY, width, height) {
26424 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26425 },
26426 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26427 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26428 },
26429 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26430 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26431 }
26432 };
26433};
26434
26435BRp$2.generateRoundPolygon = function (name, points) {
26436 // Pre-compute control points
26437 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26438 // the unit vectors.
26439 // For simplicity the layout will be:
26440 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26441 var allPoints = new Array(points.length * 2);
26442
26443 for (var i = 0; i < points.length / 2; i++) {
26444 var sourceIndex = i * 2;
26445 var destIndex = void 0;
26446
26447 if (i < points.length / 2 - 1) {
26448 destIndex = (i + 1) * 2;
26449 } else {
26450 destIndex = 0;
26451 }
26452
26453 allPoints[i * 4] = points[sourceIndex];
26454 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26455 var xDest = points[destIndex] - points[sourceIndex];
26456 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26457 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26458 allPoints[i * 4 + 2] = xDest / norm;
26459 allPoints[i * 4 + 3] = yDest / norm;
26460 }
26461
26462 return this.nodeShapes[name] = {
26463 renderer: this,
26464 name: name,
26465 points: allPoints,
26466 draw: function draw(context, centerX, centerY, width, height) {
26467 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26468 },
26469 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26470 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26471 },
26472 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26473 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26474 }
26475 };
26476};
26477
26478BRp$2.generateRoundRectangle = function () {
26479 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26480 renderer: this,
26481 name: 'round-rectangle',
26482 points: generateUnitNgonPointsFitToSquare(4, 0),
26483 draw: function draw(context, centerX, centerY, width, height) {
26484 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26485 },
26486 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26487 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26488 },
26489 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26490 var cornerRadius = getRoundRectangleRadius(width, height);
26491 var diam = cornerRadius * 2; // Check hBox
26492
26493 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26494 return true;
26495 } // Check vBox
26496
26497
26498 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26499 return true;
26500 } // Check top left quarter circle
26501
26502
26503 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26504 return true;
26505 } // Check top right quarter circle
26506
26507
26508 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26509 return true;
26510 } // Check bottom right quarter circle
26511
26512
26513 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26514 return true;
26515 } // Check bottom left quarter circle
26516
26517
26518 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26519 return true;
26520 }
26521
26522 return false;
26523 }
26524 };
26525};
26526
26527BRp$2.generateCutRectangle = function () {
26528 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26529 renderer: this,
26530 name: 'cut-rectangle',
26531 cornerLength: getCutRectangleCornerLength(),
26532 points: generateUnitNgonPointsFitToSquare(4, 0),
26533 draw: function draw(context, centerX, centerY, width, height) {
26534 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26535 },
26536 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26537 var cl = this.cornerLength;
26538 var hh = height / 2;
26539 var hw = width / 2;
26540 var xBegin = centerX - hw;
26541 var xEnd = centerX + hw;
26542 var yBegin = centerY - hh;
26543 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26544
26545 return {
26546 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26547 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26548 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26549 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26550 };
26551 },
26552 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26553 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26554 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26555 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26556 },
26557 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26558 // Check hBox
26559 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26560 return true;
26561 } // Check vBox
26562
26563
26564 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26565 return true;
26566 }
26567
26568 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26569 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26570 }
26571 };
26572};
26573
26574BRp$2.generateBarrel = function () {
26575 return this.nodeShapes['barrel'] = {
26576 renderer: this,
26577 name: 'barrel',
26578 points: generateUnitNgonPointsFitToSquare(4, 0),
26579 draw: function draw(context, centerX, centerY, width, height) {
26580 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26581 },
26582 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26583 // use two fixed t values for the bezier curve approximation
26584 var t0 = 0.15;
26585 var t1 = 0.5;
26586 var t2 = 0.85;
26587 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26588
26589 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26590 // approximate curve pts based on the two t values
26591 var m0 = qbezierPtAt({
26592 x: pts[0],
26593 y: pts[1]
26594 }, {
26595 x: pts[2],
26596 y: pts[3]
26597 }, {
26598 x: pts[4],
26599 y: pts[5]
26600 }, t0);
26601 var m1 = qbezierPtAt({
26602 x: pts[0],
26603 y: pts[1]
26604 }, {
26605 x: pts[2],
26606 y: pts[3]
26607 }, {
26608 x: pts[4],
26609 y: pts[5]
26610 }, t1);
26611 var m2 = qbezierPtAt({
26612 x: pts[0],
26613 y: pts[1]
26614 }, {
26615 x: pts[2],
26616 y: pts[3]
26617 }, {
26618 x: pts[4],
26619 y: pts[5]
26620 }, t2);
26621 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26622 };
26623
26624 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26625 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26626 },
26627 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26628 var hh = height / 2;
26629 var hw = width / 2;
26630 var xBegin = centerX - hw;
26631 var xEnd = centerX + hw;
26632 var yBegin = centerY - hh;
26633 var yEnd = centerY + hh;
26634 var curveConstants = getBarrelCurveConstants(width, height);
26635 var hOffset = curveConstants.heightOffset;
26636 var wOffset = curveConstants.widthOffset;
26637 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26638
26639 var pts = {
26640 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26641 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26642 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26643 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26644 };
26645 pts.topLeft.isTop = true;
26646 pts.topRight.isTop = true;
26647 pts.bottomLeft.isBottom = true;
26648 pts.bottomRight.isBottom = true;
26649 return pts;
26650 },
26651 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26652 var curveConstants = getBarrelCurveConstants(width, height);
26653 var hOffset = curveConstants.heightOffset;
26654 var wOffset = curveConstants.widthOffset; // Check hBox
26655
26656 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26657 return true;
26658 } // Check vBox
26659
26660
26661 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26662 return true;
26663 }
26664
26665 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26666
26667 var getCurveT = function getCurveT(x, y, curvePts) {
26668 var x0 = curvePts[4];
26669 var x1 = curvePts[2];
26670 var x2 = curvePts[0];
26671 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26672
26673 var y2 = curvePts[1];
26674 var xMin = Math.min(x0, x2);
26675 var xMax = Math.max(x0, x2);
26676 var yMin = Math.min(y0, y2);
26677 var yMax = Math.max(y0, y2);
26678
26679 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26680 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26681 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26682 var validRoots = roots.filter(function (r) {
26683 return 0 <= r && r <= 1;
26684 });
26685
26686 if (validRoots.length > 0) {
26687 return validRoots[0];
26688 }
26689 }
26690
26691 return null;
26692 };
26693
26694 var curveRegions = Object.keys(barrelCurvePts);
26695
26696 for (var i = 0; i < curveRegions.length; i++) {
26697 var corner = curveRegions[i];
26698 var cornerPts = barrelCurvePts[corner];
26699 var t = getCurveT(x, y, cornerPts);
26700
26701 if (t == null) {
26702 continue;
26703 }
26704
26705 var y0 = cornerPts[5];
26706 var y1 = cornerPts[3];
26707 var y2 = cornerPts[1];
26708 var bezY = qbezierAt(y0, y1, y2, t);
26709
26710 if (cornerPts.isTop && bezY <= y) {
26711 return true;
26712 }
26713
26714 if (cornerPts.isBottom && y <= bezY) {
26715 return true;
26716 }
26717 }
26718
26719 return false;
26720 }
26721 };
26722};
26723
26724BRp$2.generateBottomRoundrectangle = function () {
26725 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26726 renderer: this,
26727 name: 'bottom-round-rectangle',
26728 points: generateUnitNgonPointsFitToSquare(4, 0),
26729 draw: function draw(context, centerX, centerY, width, height) {
26730 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26731 },
26732 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26733 var topStartX = nodeX - (width / 2 + padding);
26734 var topStartY = nodeY - (height / 2 + padding);
26735 var topEndY = topStartY;
26736 var topEndX = nodeX + (width / 2 + padding);
26737 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26738
26739 if (topIntersections.length > 0) {
26740 return topIntersections;
26741 }
26742
26743 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26744 },
26745 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26746 var cornerRadius = getRoundRectangleRadius(width, height);
26747 var diam = 2 * cornerRadius; // Check hBox
26748
26749 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26750 return true;
26751 } // Check vBox
26752
26753
26754 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26755 return true;
26756 } // check non-rounded top side
26757
26758
26759 var outerWidth = width / 2 + 2 * padding;
26760 var outerHeight = height / 2 + 2 * padding;
26761 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26762
26763 if (pointInsidePolygonPoints(x, y, points)) {
26764 return true;
26765 } // Check bottom right quarter circle
26766
26767
26768 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26769 return true;
26770 } // Check bottom left quarter circle
26771
26772
26773 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26774 return true;
26775 }
26776
26777 return false;
26778 }
26779 };
26780};
26781
26782BRp$2.registerNodeShapes = function () {
26783 var nodeShapes = this.nodeShapes = {};
26784 var renderer = this;
26785 this.generateEllipse();
26786 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26787 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26788 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26789 nodeShapes['square'] = nodeShapes['rectangle'];
26790 this.generateRoundRectangle();
26791 this.generateCutRectangle();
26792 this.generateBarrel();
26793 this.generateBottomRoundrectangle();
26794 {
26795 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26796 this.generatePolygon('diamond', diamondPoints);
26797 this.generateRoundPolygon('round-diamond', diamondPoints);
26798 }
26799 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26800 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26801 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26802 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26803 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26804 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26805 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26806 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26807 var star5Points = new Array(20);
26808 {
26809 var outerPoints = generateUnitNgonPoints(5, 0);
26810 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26811
26812 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26813 innerRadius *= 1.57;
26814
26815 for (var i = 0; i < innerPoints.length / 2; i++) {
26816 innerPoints[i * 2] *= innerRadius;
26817 innerPoints[i * 2 + 1] *= innerRadius;
26818 }
26819
26820 for (var i = 0; i < 20 / 4; i++) {
26821 star5Points[i * 4] = outerPoints[i * 2];
26822 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26823 star5Points[i * 4 + 2] = innerPoints[i * 2];
26824 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26825 }
26826 }
26827 star5Points = fitPolygonToSquare(star5Points);
26828 this.generatePolygon('star', star5Points);
26829 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26830 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26831 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]);
26832 {
26833 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26834 this.generatePolygon('tag', tagPoints);
26835 this.generateRoundPolygon('round-tag', tagPoints);
26836 }
26837
26838 nodeShapes.makePolygon = function (points) {
26839 // use caching on user-specified polygons so they are as fast as native shapes
26840 var key = points.join('$');
26841 var name = 'polygon-' + key;
26842 var shape;
26843
26844 if (shape = this[name]) {
26845 // got cached shape
26846 return shape;
26847 } // create and cache new shape
26848
26849
26850 return renderer.generatePolygon(name, points);
26851 };
26852};
26853
26854var BRp$1 = {};
26855
26856BRp$1.timeToRender = function () {
26857 return this.redrawTotalTime / this.redrawCount;
26858};
26859
26860BRp$1.redraw = function (options) {
26861 options = options || staticEmptyObject();
26862 var r = this;
26863
26864 if (r.averageRedrawTime === undefined) {
26865 r.averageRedrawTime = 0;
26866 }
26867
26868 if (r.lastRedrawTime === undefined) {
26869 r.lastRedrawTime = 0;
26870 }
26871
26872 if (r.lastDrawTime === undefined) {
26873 r.lastDrawTime = 0;
26874 }
26875
26876 r.requestedFrame = true;
26877 r.renderOptions = options;
26878};
26879
26880BRp$1.beforeRender = function (fn, priority) {
26881 // the renderer can't add tick callbacks when destroyed
26882 if (this.destroyed) {
26883 return;
26884 }
26885
26886 if (priority == null) {
26887 error('Priority is not optional for beforeRender');
26888 }
26889
26890 var cbs = this.beforeRenderCallbacks;
26891 cbs.push({
26892 fn: fn,
26893 priority: priority
26894 }); // higher priority callbacks executed first
26895
26896 cbs.sort(function (a, b) {
26897 return b.priority - a.priority;
26898 });
26899};
26900
26901var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26902 var cbs = r.beforeRenderCallbacks;
26903
26904 for (var i = 0; i < cbs.length; i++) {
26905 cbs[i].fn(willDraw, startTime);
26906 }
26907};
26908
26909BRp$1.startRenderLoop = function () {
26910 var r = this;
26911 var cy = r.cy;
26912
26913 if (r.renderLoopStarted) {
26914 return;
26915 } else {
26916 r.renderLoopStarted = true;
26917 }
26918
26919 var renderFn = function renderFn(requestTime) {
26920 if (r.destroyed) {
26921 return;
26922 }
26923
26924 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26925 beforeRenderCallbacks(r, true, requestTime);
26926 var startTime = performanceNow();
26927 r.render(r.renderOptions);
26928 var endTime = r.lastDrawTime = performanceNow();
26929
26930 if (r.averageRedrawTime === undefined) {
26931 r.averageRedrawTime = endTime - startTime;
26932 }
26933
26934 if (r.redrawCount === undefined) {
26935 r.redrawCount = 0;
26936 }
26937
26938 r.redrawCount++;
26939
26940 if (r.redrawTotalTime === undefined) {
26941 r.redrawTotalTime = 0;
26942 }
26943
26944 var duration = endTime - startTime;
26945 r.redrawTotalTime += duration;
26946 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26947
26948 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26949 r.requestedFrame = false;
26950 } else {
26951 beforeRenderCallbacks(r, false, requestTime);
26952 }
26953
26954 r.skipFrame = false;
26955 requestAnimationFrame(renderFn);
26956 };
26957
26958 requestAnimationFrame(renderFn);
26959};
26960
26961var BaseRenderer = function BaseRenderer(options) {
26962 this.init(options);
26963};
26964
26965var BR = BaseRenderer;
26966var BRp = BR.prototype;
26967BRp.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26968
26969BRp.init = function (options) {
26970 var r = this;
26971 r.options = options;
26972 r.cy = options.cy;
26973 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26974
26975 if (window$1) {
26976 var document = window$1.document;
26977 var head = document.head;
26978 var stylesheetId = '__________cytoscape_stylesheet';
26979 var className = '__________cytoscape_container';
26980 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26981
26982 if (ctr.className.indexOf(className) < 0) {
26983 ctr.className = (ctr.className || '') + ' ' + className;
26984 }
26985
26986 if (!stylesheetAlreadyExists) {
26987 var stylesheet = document.createElement('style');
26988 stylesheet.id = stylesheetId;
26989 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26990 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26991 }
26992
26993 var computedStyle = window$1.getComputedStyle(ctr);
26994 var position = computedStyle.getPropertyValue('position');
26995
26996 if (position === 'static') {
26997 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26998 }
26999 }
27000
27001 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
27002
27003 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
27004
27005 r.hoverData = {
27006 down: null,
27007 last: null,
27008 downTime: null,
27009 triggerMode: null,
27010 dragging: false,
27011 initialPan: [null, null],
27012 capture: false
27013 };
27014 r.dragData = {
27015 possibleDragElements: []
27016 };
27017 r.touchData = {
27018 start: null,
27019 capture: false,
27020 // These 3 fields related to tap, taphold events
27021 startPosition: [null, null, null, null, null, null],
27022 singleTouchStartTime: null,
27023 singleTouchMoved: true,
27024 now: [null, null, null, null, null, null],
27025 earlier: [null, null, null, null, null, null]
27026 };
27027 r.redraws = 0;
27028 r.showFps = options.showFps;
27029 r.debug = options.debug;
27030 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
27031 r.textureOnViewport = options.textureOnViewport;
27032 r.wheelSensitivity = options.wheelSensitivity;
27033 r.motionBlurEnabled = options.motionBlur; // on by default
27034
27035 r.forcedPixelRatio = number$1(options.pixelRatio) ? options.pixelRatio : null;
27036 r.motionBlur = options.motionBlur; // for initial kick off
27037
27038 r.motionBlurOpacity = options.motionBlurOpacity;
27039 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
27040 r.motionBlurPxRatio = 1;
27041 r.mbPxRBlurry = 1; //0.8;
27042
27043 r.minMbLowQualFrames = 4;
27044 r.fullQualityMb = false;
27045 r.clearedForMotionBlur = [];
27046 r.desktopTapThreshold = options.desktopTapThreshold;
27047 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
27048 r.touchTapThreshold = options.touchTapThreshold;
27049 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
27050 r.tapholdDuration = 500;
27051 r.bindings = [];
27052 r.beforeRenderCallbacks = [];
27053 r.beforeRenderPriorities = {
27054 // higher priority execs before lower one
27055 animations: 400,
27056 eleCalcs: 300,
27057 eleTxrDeq: 200,
27058 lyrTxrDeq: 150,
27059 lyrTxrSkip: 100
27060 };
27061 r.registerNodeShapes();
27062 r.registerArrowShapes();
27063 r.registerCalculationListeners();
27064};
27065
27066BRp.notify = function (eventName, eles) {
27067 var r = this;
27068 var cy = r.cy; // the renderer can't be notified after it's destroyed
27069
27070 if (this.destroyed) {
27071 return;
27072 }
27073
27074 if (eventName === 'init') {
27075 r.load();
27076 return;
27077 }
27078
27079 if (eventName === 'destroy') {
27080 r.destroy();
27081 return;
27082 }
27083
27084 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
27085 r.invalidateCachedZSortedEles();
27086 }
27087
27088 if (eventName === 'viewport') {
27089 r.redrawHint('select', true);
27090 }
27091
27092 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
27093 r.invalidateContainerClientCoordsCache();
27094 r.matchCanvasSize(r.container);
27095 }
27096
27097 r.redrawHint('eles', true);
27098 r.redrawHint('drag', true);
27099 this.startRenderLoop();
27100 this.redraw();
27101};
27102
27103BRp.destroy = function () {
27104 var r = this;
27105 r.destroyed = true;
27106 r.cy.stopAnimationLoop();
27107
27108 for (var i = 0; i < r.bindings.length; i++) {
27109 var binding = r.bindings[i];
27110 var b = binding;
27111 var tgt = b.target;
27112 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
27113 }
27114
27115 r.bindings = [];
27116 r.beforeRenderCallbacks = [];
27117 r.onUpdateEleCalcsFns = [];
27118
27119 if (r.removeObserver) {
27120 r.removeObserver.disconnect();
27121 }
27122
27123 if (r.styleObserver) {
27124 r.styleObserver.disconnect();
27125 }
27126
27127 if (r.resizeObserver) {
27128 r.resizeObserver.disconnect();
27129 }
27130
27131 if (r.labelCalcDiv) {
27132 try {
27133 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
27134 } catch (e) {// ie10 issue #1014
27135 }
27136 }
27137};
27138
27139BRp.isHeadless = function () {
27140 return false;
27141};
27142
27143[BRp$f, BRp$5, BRp$4, BRp$3, BRp$2, BRp$1].forEach(function (props) {
27144 extend(BRp, props);
27145});
27146
27147var fullFpsTime = 1000 / 60; // assume 60 frames per second
27148
27149var defs = {
27150 setupDequeueing: function setupDequeueing(opts) {
27151 return function setupDequeueingImpl() {
27152 var self = this;
27153 var r = this.renderer;
27154
27155 if (self.dequeueingSetup) {
27156 return;
27157 } else {
27158 self.dequeueingSetup = true;
27159 }
27160
27161 var queueRedraw = debounce__default["default"](function () {
27162 r.redrawHint('eles', true);
27163 r.redrawHint('drag', true);
27164 r.redraw();
27165 }, opts.deqRedrawThreshold);
27166
27167 var dequeue = function dequeue(willDraw, frameStartTime) {
27168 var startTime = performanceNow();
27169 var avgRenderTime = r.averageRedrawTime;
27170 var renderTime = r.lastRedrawTime;
27171 var deqd = [];
27172 var extent = r.cy.extent();
27173 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
27174 // queue won't automatically be flushed before dequeueing starts
27175
27176 if (!willDraw) {
27177 r.flushRenderedStyleQueue();
27178 }
27179
27180 while (true) {
27181 // eslint-disable-line no-constant-condition
27182 var now = performanceNow();
27183 var duration = now - startTime;
27184 var frameDuration = now - frameStartTime;
27185
27186 if (renderTime < fullFpsTime) {
27187 // if we're rendering faster than the ideal fps, then do dequeueing
27188 // during all of the remaining frame time
27189 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27190
27191 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27192 break;
27193 }
27194 } else {
27195 if (willDraw) {
27196 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27197 break;
27198 }
27199 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27200 break;
27201 }
27202 }
27203
27204 var thisDeqd = opts.deq(self, pixelRatio, extent);
27205
27206 if (thisDeqd.length > 0) {
27207 for (var i = 0; i < thisDeqd.length; i++) {
27208 deqd.push(thisDeqd[i]);
27209 }
27210 } else {
27211 break;
27212 }
27213 } // callbacks on dequeue
27214
27215
27216 if (deqd.length > 0) {
27217 opts.onDeqd(self, deqd);
27218
27219 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27220 queueRedraw();
27221 }
27222 }
27223 };
27224
27225 var priority = opts.priority || noop$1;
27226 r.beforeRender(dequeue, priority(self));
27227 };
27228 }
27229};
27230
27231// Uses keys so elements may share the same cache.
27232
27233var ElementTextureCacheLookup = /*#__PURE__*/function () {
27234 function ElementTextureCacheLookup(getKey) {
27235 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27236
27237 _classCallCheck(this, ElementTextureCacheLookup);
27238
27239 this.idsByKey = new Map$1();
27240 this.keyForId = new Map$1();
27241 this.cachesByLvl = new Map$1();
27242 this.lvls = [];
27243 this.getKey = getKey;
27244 this.doesEleInvalidateKey = doesEleInvalidateKey;
27245 }
27246
27247 _createClass(ElementTextureCacheLookup, [{
27248 key: "getIdsFor",
27249 value: function getIdsFor(key) {
27250 if (key == null) {
27251 error("Can not get id list for null key");
27252 }
27253
27254 var idsByKey = this.idsByKey;
27255 var ids = this.idsByKey.get(key);
27256
27257 if (!ids) {
27258 ids = new Set$1();
27259 idsByKey.set(key, ids);
27260 }
27261
27262 return ids;
27263 }
27264 }, {
27265 key: "addIdForKey",
27266 value: function addIdForKey(key, id) {
27267 if (key != null) {
27268 this.getIdsFor(key).add(id);
27269 }
27270 }
27271 }, {
27272 key: "deleteIdForKey",
27273 value: function deleteIdForKey(key, id) {
27274 if (key != null) {
27275 this.getIdsFor(key)["delete"](id);
27276 }
27277 }
27278 }, {
27279 key: "getNumberOfIdsForKey",
27280 value: function getNumberOfIdsForKey(key) {
27281 if (key == null) {
27282 return 0;
27283 } else {
27284 return this.getIdsFor(key).size;
27285 }
27286 }
27287 }, {
27288 key: "updateKeyMappingFor",
27289 value: function updateKeyMappingFor(ele) {
27290 var id = ele.id();
27291 var prevKey = this.keyForId.get(id);
27292 var currKey = this.getKey(ele);
27293 this.deleteIdForKey(prevKey, id);
27294 this.addIdForKey(currKey, id);
27295 this.keyForId.set(id, currKey);
27296 }
27297 }, {
27298 key: "deleteKeyMappingFor",
27299 value: function deleteKeyMappingFor(ele) {
27300 var id = ele.id();
27301 var prevKey = this.keyForId.get(id);
27302 this.deleteIdForKey(prevKey, id);
27303 this.keyForId["delete"](id);
27304 }
27305 }, {
27306 key: "keyHasChangedFor",
27307 value: function keyHasChangedFor(ele) {
27308 var id = ele.id();
27309 var prevKey = this.keyForId.get(id);
27310 var newKey = this.getKey(ele);
27311 return prevKey !== newKey;
27312 }
27313 }, {
27314 key: "isInvalid",
27315 value: function isInvalid(ele) {
27316 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27317 }
27318 }, {
27319 key: "getCachesAt",
27320 value: function getCachesAt(lvl) {
27321 var cachesByLvl = this.cachesByLvl,
27322 lvls = this.lvls;
27323 var caches = cachesByLvl.get(lvl);
27324
27325 if (!caches) {
27326 caches = new Map$1();
27327 cachesByLvl.set(lvl, caches);
27328 lvls.push(lvl);
27329 }
27330
27331 return caches;
27332 }
27333 }, {
27334 key: "getCache",
27335 value: function getCache(key, lvl) {
27336 return this.getCachesAt(lvl).get(key);
27337 }
27338 }, {
27339 key: "get",
27340 value: function get(ele, lvl) {
27341 var key = this.getKey(ele);
27342 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27343
27344 if (cache != null) {
27345 this.updateKeyMappingFor(ele);
27346 }
27347
27348 return cache;
27349 }
27350 }, {
27351 key: "getForCachedKey",
27352 value: function getForCachedKey(ele, lvl) {
27353 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27354
27355 var cache = this.getCache(key, lvl);
27356 return cache;
27357 }
27358 }, {
27359 key: "hasCache",
27360 value: function hasCache(key, lvl) {
27361 return this.getCachesAt(lvl).has(key);
27362 }
27363 }, {
27364 key: "has",
27365 value: function has(ele, lvl) {
27366 var key = this.getKey(ele);
27367 return this.hasCache(key, lvl);
27368 }
27369 }, {
27370 key: "setCache",
27371 value: function setCache(key, lvl, cache) {
27372 cache.key = key;
27373 this.getCachesAt(lvl).set(key, cache);
27374 }
27375 }, {
27376 key: "set",
27377 value: function set(ele, lvl, cache) {
27378 var key = this.getKey(ele);
27379 this.setCache(key, lvl, cache);
27380 this.updateKeyMappingFor(ele);
27381 }
27382 }, {
27383 key: "deleteCache",
27384 value: function deleteCache(key, lvl) {
27385 this.getCachesAt(lvl)["delete"](key);
27386 }
27387 }, {
27388 key: "delete",
27389 value: function _delete(ele, lvl) {
27390 var key = this.getKey(ele);
27391 this.deleteCache(key, lvl);
27392 }
27393 }, {
27394 key: "invalidateKey",
27395 value: function invalidateKey(key) {
27396 var _this = this;
27397
27398 this.lvls.forEach(function (lvl) {
27399 return _this.deleteCache(key, lvl);
27400 });
27401 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27402
27403 }, {
27404 key: "invalidate",
27405 value: function invalidate(ele) {
27406 var id = ele.id();
27407 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27408
27409 this.deleteKeyMappingFor(ele);
27410 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27411
27412 if (entireKeyInvalidated) {
27413 // clear mapping for current key
27414 this.invalidateKey(key);
27415 }
27416
27417 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27418 }
27419 }]);
27420
27421 return ElementTextureCacheLookup;
27422}();
27423
27424var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27425
27426var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27427
27428var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27429
27430var maxLvl$1 = 3; // when larger than this scale just render directly (caching is not helpful)
27431
27432var maxZoom$1 = 7.99; // beyond this zoom level, layered textures are not used
27433
27434var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27435
27436var defTxrWidth = 1024; // default/minimum texture width
27437
27438var maxTxrW = 1024; // the maximum width of a texture
27439
27440var maxTxrH = 1024; // the maximum height of a texture
27441
27442var minUtility = 0.2; // if usage of texture is less than this, it is retired
27443
27444var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27445
27446var maxFullnessChecks = 10; // dequeued after this many checks
27447
27448var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27449
27450var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27451
27452var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27453
27454var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27455
27456var deqRedrawThreshold$1 = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27457
27458var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27459
27460var getTxrReasons = {
27461 dequeue: 'dequeue',
27462 downscale: 'downscale',
27463 highQuality: 'highQuality'
27464};
27465var initDefaults = defaults$g({
27466 getKey: null,
27467 doesEleInvalidateKey: falsify,
27468 drawElement: null,
27469 getBoundingBox: null,
27470 getRotationPoint: null,
27471 getRotationOffset: null,
27472 isVisible: trueify,
27473 allowEdgeTxrCaching: true,
27474 allowParentTxrCaching: true
27475});
27476
27477var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27478 var self = this;
27479 self.renderer = renderer;
27480 self.onDequeues = [];
27481 var opts = initDefaults(initOptions);
27482 extend(self, opts);
27483 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27484 self.setupDequeueing();
27485};
27486
27487var ETCp = ElementTextureCache.prototype;
27488ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27489
27490ETCp.getTextureQueue = function (txrH) {
27491 var self = this;
27492 self.eleImgCaches = self.eleImgCaches || {};
27493 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27494}; // the list of usused textures which can be recycled (in use in texture queue)
27495
27496
27497ETCp.getRetiredTextureQueue = function (txrH) {
27498 var self = this;
27499 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27500 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27501 return rtxtrQ;
27502}; // queue of element draw requests at different scale levels
27503
27504
27505ETCp.getElementQueue = function () {
27506 var self = this;
27507 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap__default["default"](function (a, b) {
27508 return b.reqs - a.reqs;
27509 });
27510 return q;
27511}; // queue of element draw requests at different scale levels (element id lookup)
27512
27513
27514ETCp.getElementKeyToQueue = function () {
27515 var self = this;
27516 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27517 return k2q;
27518};
27519
27520ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27521 var self = this;
27522 var r = this.renderer;
27523 var zoom = r.cy.zoom();
27524 var lookup = this.lookup;
27525
27526 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27527 return null;
27528 }
27529
27530 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27531 return null;
27532 }
27533
27534 if (lvl == null) {
27535 lvl = Math.ceil(log2(zoom * pxRatio));
27536 }
27537
27538 if (lvl < minLvl$1) {
27539 lvl = minLvl$1;
27540 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27541 return null;
27542 }
27543
27544 var scale = Math.pow(2, lvl);
27545 var eleScaledH = bb.h * scale;
27546 var eleScaledW = bb.w * scale;
27547 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27548
27549 if (!this.isVisible(ele, scaledLabelShown)) {
27550 return null;
27551 }
27552
27553 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27554
27555 if (eleCache && eleCache.invalidated) {
27556 eleCache.invalidated = false;
27557 eleCache.texture.invalidatedWidth -= eleCache.width;
27558 }
27559
27560 if (eleCache) {
27561 return eleCache;
27562 }
27563
27564 var txrH; // which texture height this ele belongs to
27565
27566 if (eleScaledH <= minTxrH) {
27567 txrH = minTxrH;
27568 } else if (eleScaledH <= txrStepH) {
27569 txrH = txrStepH;
27570 } else {
27571 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27572 }
27573
27574 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27575 return null; // caching large elements is not efficient
27576 }
27577
27578 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27579
27580 var txr = txrQ[txrQ.length - 2];
27581
27582 var addNewTxr = function addNewTxr() {
27583 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27584 }; // try the last one if there is no second last one
27585
27586
27587 if (!txr) {
27588 txr = txrQ[txrQ.length - 1];
27589 } // if the last one doesn't exist, we need a first one
27590
27591
27592 if (!txr) {
27593 txr = addNewTxr();
27594 } // if there's no room in the current texture, we need a new one
27595
27596
27597 if (txr.width - txr.usedWidth < eleScaledW) {
27598 txr = addNewTxr();
27599 }
27600
27601 var scalableFrom = function scalableFrom(otherCache) {
27602 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27603 };
27604
27605 var deqing = reason && reason === getTxrReasons.dequeue;
27606 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27607 var downscaleReq = reason && reason === getTxrReasons.downscale;
27608 var higherCache; // the nearest cache with a higher level
27609
27610 for (var l = lvl + 1; l <= maxLvl$1; l++) {
27611 var c = lookup.get(ele, l);
27612
27613 if (c) {
27614 higherCache = c;
27615 break;
27616 }
27617 }
27618
27619 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27620
27621 var downscale = function downscale() {
27622 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27623 }; // reset ele area in texture
27624
27625
27626 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27627 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27628
27629 if (scalableFrom(oneUpCache)) {
27630 // then we can relatively cheaply rescale the existing image w/o rerendering
27631 downscale();
27632 } else if (scalableFrom(higherCache)) {
27633 // then use the higher cache for now and queue the next level down
27634 // to cheaply scale towards the smaller level
27635 if (highQualityReq) {
27636 for (var _l = higherCache.level; _l > lvl; _l--) {
27637 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27638 }
27639
27640 downscale();
27641 } else {
27642 self.queueElement(ele, higherCache.level - 1);
27643 return higherCache;
27644 }
27645 } else {
27646 var lowerCache; // the nearest cache with a lower level
27647
27648 if (!deqing && !highQualityReq && !downscaleReq) {
27649 for (var _l2 = lvl - 1; _l2 >= minLvl$1; _l2--) {
27650 var _c = lookup.get(ele, _l2);
27651
27652 if (_c) {
27653 lowerCache = _c;
27654 break;
27655 }
27656 }
27657 }
27658
27659 if (scalableFrom(lowerCache)) {
27660 // then use the lower quality cache for now and queue the better one for later
27661 self.queueElement(ele, lvl);
27662 return lowerCache;
27663 }
27664
27665 txr.context.translate(txr.usedWidth, 0);
27666 txr.context.scale(scale, scale);
27667 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27668 txr.context.scale(1 / scale, 1 / scale);
27669 txr.context.translate(-txr.usedWidth, 0);
27670 }
27671
27672 eleCache = {
27673 x: txr.usedWidth,
27674 texture: txr,
27675 level: lvl,
27676 scale: scale,
27677 width: eleScaledW,
27678 height: eleScaledH,
27679 scaledLabelShown: scaledLabelShown
27680 };
27681 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27682 txr.eleCaches.push(eleCache);
27683 lookup.set(ele, lvl, eleCache);
27684 self.checkTextureFullness(txr);
27685 return eleCache;
27686};
27687
27688ETCp.invalidateElements = function (eles) {
27689 for (var i = 0; i < eles.length; i++) {
27690 this.invalidateElement(eles[i]);
27691 }
27692};
27693
27694ETCp.invalidateElement = function (ele) {
27695 var self = this;
27696 var lookup = self.lookup;
27697 var caches = [];
27698 var invalid = lookup.isInvalid(ele);
27699
27700 if (!invalid) {
27701 return; // override the invalidation request if the element key has not changed
27702 }
27703
27704 for (var lvl = minLvl$1; lvl <= maxLvl$1; lvl++) {
27705 var cache = lookup.getForCachedKey(ele, lvl);
27706
27707 if (cache) {
27708 caches.push(cache);
27709 }
27710 }
27711
27712 var noOtherElesUseCache = lookup.invalidate(ele);
27713
27714 if (noOtherElesUseCache) {
27715 for (var i = 0; i < caches.length; i++) {
27716 var _cache = caches[i];
27717 var txr = _cache.texture; // remove space from the texture it belongs to
27718
27719 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27720
27721 _cache.invalidated = true; // retire the texture if its utility is low
27722
27723 self.checkTextureUtility(txr);
27724 }
27725 } // remove from queue since the old req was for the old state
27726
27727
27728 self.removeFromQueue(ele);
27729};
27730
27731ETCp.checkTextureUtility = function (txr) {
27732 // invalidate all entries in the cache if the cache size is small
27733 if (txr.invalidatedWidth >= minUtility * txr.width) {
27734 this.retireTexture(txr);
27735 }
27736};
27737
27738ETCp.checkTextureFullness = function (txr) {
27739 // if texture has been mostly filled and passed over several times, remove
27740 // it from the queue so we don't need to waste time looking at it to put new things
27741 var self = this;
27742 var txrQ = self.getTextureQueue(txr.height);
27743
27744 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27745 removeFromArray(txrQ, txr);
27746 } else {
27747 txr.fullnessChecks++;
27748 }
27749};
27750
27751ETCp.retireTexture = function (txr) {
27752 var self = this;
27753 var txrH = txr.height;
27754 var txrQ = self.getTextureQueue(txrH);
27755 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27756
27757 removeFromArray(txrQ, txr);
27758 txr.retired = true; // remove the refs from the eles to the caches:
27759
27760 var eleCaches = txr.eleCaches;
27761
27762 for (var i = 0; i < eleCaches.length; i++) {
27763 var eleCache = eleCaches[i];
27764 lookup.deleteCache(eleCache.key, eleCache.level);
27765 }
27766
27767 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27768
27769 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27770 rtxtrQ.push(txr);
27771};
27772
27773ETCp.addTexture = function (txrH, minW) {
27774 var self = this;
27775 var txrQ = self.getTextureQueue(txrH);
27776 var txr = {};
27777 txrQ.push(txr);
27778 txr.eleCaches = [];
27779 txr.height = txrH;
27780 txr.width = Math.max(defTxrWidth, minW);
27781 txr.usedWidth = 0;
27782 txr.invalidatedWidth = 0;
27783 txr.fullnessChecks = 0;
27784 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27785 txr.context = txr.canvas.getContext('2d');
27786 return txr;
27787};
27788
27789ETCp.recycleTexture = function (txrH, minW) {
27790 var self = this;
27791 var txrQ = self.getTextureQueue(txrH);
27792 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27793
27794 for (var i = 0; i < rtxtrQ.length; i++) {
27795 var txr = rtxtrQ[i];
27796
27797 if (txr.width >= minW) {
27798 txr.retired = false;
27799 txr.usedWidth = 0;
27800 txr.invalidatedWidth = 0;
27801 txr.fullnessChecks = 0;
27802 clearArray(txr.eleCaches);
27803 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27804 txr.context.clearRect(0, 0, txr.width, txr.height);
27805 removeFromArray(rtxtrQ, txr);
27806 txrQ.push(txr);
27807 return txr;
27808 }
27809 }
27810};
27811
27812ETCp.queueElement = function (ele, lvl) {
27813 var self = this;
27814 var q = self.getElementQueue();
27815 var k2q = self.getElementKeyToQueue();
27816 var key = this.getKey(ele);
27817 var existingReq = k2q[key];
27818
27819 if (existingReq) {
27820 // use the max lvl b/c in between lvls are cheap to make
27821 existingReq.level = Math.max(existingReq.level, lvl);
27822 existingReq.eles.merge(ele);
27823 existingReq.reqs++;
27824 q.updateItem(existingReq);
27825 } else {
27826 var req = {
27827 eles: ele.spawn().merge(ele),
27828 level: lvl,
27829 reqs: 1,
27830 key: key
27831 };
27832 q.push(req);
27833 k2q[key] = req;
27834 }
27835};
27836
27837ETCp.dequeue = function (pxRatio
27838/*, extent*/
27839) {
27840 var self = this;
27841 var q = self.getElementQueue();
27842 var k2q = self.getElementKeyToQueue();
27843 var dequeued = [];
27844 var lookup = self.lookup;
27845
27846 for (var i = 0; i < maxDeqSize$1; i++) {
27847 if (q.size() > 0) {
27848 var req = q.pop();
27849 var key = req.key;
27850 var ele = req.eles[0]; // all eles have the same key
27851
27852 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27853
27854 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27855
27856 if (cacheExists) {
27857 continue;
27858 }
27859
27860 dequeued.push(req);
27861 var bb = self.getBoundingBox(ele);
27862 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27863 } else {
27864 break;
27865 }
27866 }
27867
27868 return dequeued;
27869};
27870
27871ETCp.removeFromQueue = function (ele) {
27872 var self = this;
27873 var q = self.getElementQueue();
27874 var k2q = self.getElementKeyToQueue();
27875 var key = this.getKey(ele);
27876 var req = k2q[key];
27877
27878 if (req != null) {
27879 if (req.eles.length === 1) {
27880 // remove if last ele in the req
27881 // bring to front of queue
27882 req.reqs = MAX_INT$1;
27883 q.updateItem(req);
27884 q.pop(); // remove from queue
27885
27886 k2q[key] = null; // remove from lookup map
27887 } else {
27888 // otherwise just remove ele from req
27889 req.eles.unmerge(ele);
27890 }
27891 }
27892};
27893
27894ETCp.onDequeue = function (fn) {
27895 this.onDequeues.push(fn);
27896};
27897
27898ETCp.offDequeue = function (fn) {
27899 removeFromArray(this.onDequeues, fn);
27900};
27901
27902ETCp.setupDequeueing = defs.setupDequeueing({
27903 deqRedrawThreshold: deqRedrawThreshold$1,
27904 deqCost: deqCost$1,
27905 deqAvgCost: deqAvgCost$1,
27906 deqNoDrawCost: deqNoDrawCost$1,
27907 deqFastCost: deqFastCost$1,
27908 deq: function deq(self, pxRatio, extent) {
27909 return self.dequeue(pxRatio, extent);
27910 },
27911 onDeqd: function onDeqd(self, deqd) {
27912 for (var i = 0; i < self.onDequeues.length; i++) {
27913 var fn = self.onDequeues[i];
27914 fn(deqd);
27915 }
27916 },
27917 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27918 for (var i = 0; i < deqd.length; i++) {
27919 var eles = deqd[i].eles;
27920
27921 for (var j = 0; j < eles.length; j++) {
27922 var bb = eles[j].boundingBox();
27923
27924 if (boundingBoxesIntersect(bb, extent)) {
27925 return true;
27926 }
27927 }
27928 }
27929
27930 return false;
27931 },
27932 priority: function priority(self) {
27933 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27934 }
27935});
27936
27937var defNumLayers = 1; // default number of layers to use
27938
27939var minLvl = -4; // when scaling smaller than that we don't need to re-render
27940
27941var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful)
27942
27943var maxZoom = 3.99; // beyond this zoom level, layered textures are not used
27944
27945var deqRedrawThreshold = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27946
27947var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27948
27949var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27950
27951var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27952
27953var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27954
27955var deqFastCost = 0.9; // % of frame time to be used when >60fps
27956
27957var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27958
27959var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27960
27961var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27962
27963var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27964// var log = function(){ console.log.apply( console, arguments ); };
27965
27966var LayeredTextureCache = function LayeredTextureCache(renderer) {
27967 var self = this;
27968 var r = self.renderer = renderer;
27969 var cy = r.cy;
27970 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27971
27972 self.firstGet = true;
27973 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27974 self.skipping = false;
27975 self.eleTxrDeqs = cy.collection();
27976 self.scheduleElementRefinement = debounce__default["default"](function () {
27977 self.refineElementTextures(self.eleTxrDeqs);
27978 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27979 }, refineEleDebounceTime);
27980 r.beforeRender(function (willDraw, now) {
27981 if (now - self.lastInvalidationTime <= invalidThreshold) {
27982 self.skipping = true;
27983 } else {
27984 self.skipping = false;
27985 }
27986 }, r.beforeRenderPriorities.lyrTxrSkip);
27987
27988 var qSort = function qSort(a, b) {
27989 return b.reqs - a.reqs;
27990 };
27991
27992 self.layersQueue = new Heap__default["default"](qSort);
27993 self.setupDequeueing();
27994};
27995
27996var LTCp = LayeredTextureCache.prototype;
27997var layerIdPool = 0;
27998var MAX_INT = Math.pow(2, 53) - 1;
27999
28000LTCp.makeLayer = function (bb, lvl) {
28001 var scale = Math.pow(2, lvl);
28002 var w = Math.ceil(bb.w * scale);
28003 var h = Math.ceil(bb.h * scale);
28004 var canvas = this.renderer.makeOffscreenCanvas(w, h);
28005 var layer = {
28006 id: layerIdPool = ++layerIdPool % MAX_INT,
28007 bb: bb,
28008 level: lvl,
28009 width: w,
28010 height: h,
28011 canvas: canvas,
28012 context: canvas.getContext('2d'),
28013 eles: [],
28014 elesQueue: [],
28015 reqs: 0
28016 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
28017
28018 var cxt = layer.context;
28019 var dx = -layer.bb.x1;
28020 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
28021
28022 cxt.scale(scale, scale);
28023 cxt.translate(dx, dy);
28024 return layer;
28025};
28026
28027LTCp.getLayers = function (eles, pxRatio, lvl) {
28028 var self = this;
28029 var r = self.renderer;
28030 var cy = r.cy;
28031 var zoom = cy.zoom();
28032 var firstGet = self.firstGet;
28033 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
28034 //log eles.map(function(ele){ return ele.id() }) );
28035
28036 if (lvl == null) {
28037 lvl = Math.ceil(log2(zoom * pxRatio));
28038
28039 if (lvl < minLvl) {
28040 lvl = minLvl;
28041 } else if (zoom >= maxZoom || lvl > maxLvl) {
28042 return null;
28043 }
28044 }
28045
28046 self.validateLayersElesOrdering(lvl, eles);
28047 var layersByLvl = self.layersByLevel;
28048 var scale = Math.pow(2, lvl);
28049 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
28050 var bb;
28051 var lvlComplete = self.levelIsComplete(lvl, eles);
28052 var tmpLayers;
28053
28054 var checkTempLevels = function checkTempLevels() {
28055 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
28056 self.validateLayersElesOrdering(l, eles);
28057
28058 if (self.levelIsComplete(l, eles)) {
28059 tmpLayers = layersByLvl[l];
28060 return true;
28061 }
28062 };
28063
28064 var checkLvls = function checkLvls(dir) {
28065 if (tmpLayers) {
28066 return;
28067 }
28068
28069 for (var l = lvl + dir; minLvl <= l && l <= maxLvl; l += dir) {
28070 if (canUseAsTmpLvl(l)) {
28071 break;
28072 }
28073 }
28074 };
28075
28076 checkLvls(+1);
28077 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
28078
28079 for (var i = layers.length - 1; i >= 0; i--) {
28080 var layer = layers[i];
28081
28082 if (layer.invalid) {
28083 removeFromArray(layers, layer);
28084 }
28085 }
28086 };
28087
28088 if (!lvlComplete) {
28089 // if the current level is incomplete, then use the closest, best quality layerset temporarily
28090 // and later queue the current layerset so we can get the proper quality level soon
28091 checkTempLevels();
28092 } else {
28093 // log('level complete, using existing layers\n--');
28094 return layers;
28095 }
28096
28097 var getBb = function getBb() {
28098 if (!bb) {
28099 bb = makeBoundingBox();
28100
28101 for (var i = 0; i < eles.length; i++) {
28102 updateBoundingBox(bb, eles[i].boundingBox());
28103 }
28104 }
28105
28106 return bb;
28107 };
28108
28109 var makeLayer = function makeLayer(opts) {
28110 opts = opts || {};
28111 var after = opts.after;
28112 getBb();
28113 var area = bb.w * scale * (bb.h * scale);
28114
28115 if (area > maxLayerArea) {
28116 return null;
28117 }
28118
28119 var layer = self.makeLayer(bb, lvl);
28120
28121 if (after != null) {
28122 var index = layers.indexOf(after) + 1;
28123 layers.splice(index, 0, layer);
28124 } else if (opts.insert === undefined || opts.insert) {
28125 // no after specified => first layer made so put at start
28126 layers.unshift(layer);
28127 } // if( tmpLayers ){
28128 //self.queueLayer( layer );
28129 // }
28130
28131
28132 return layer;
28133 };
28134
28135 if (self.skipping && !firstGet) {
28136 // log('skip layers');
28137 return null;
28138 } // log('do layers');
28139
28140
28141 var layer = null;
28142 var maxElesPerLayer = eles.length / defNumLayers;
28143 var allowLazyQueueing = !firstGet;
28144
28145 for (var i = 0; i < eles.length; i++) {
28146 var ele = eles[i];
28147 var rs = ele._private.rscratch;
28148 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
28149
28150 var existingLayer = caches[lvl];
28151
28152 if (existingLayer) {
28153 // reuse layer for later eles
28154 // log('reuse layer for', ele.id());
28155 layer = existingLayer;
28156 continue;
28157 }
28158
28159 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
28160 // log('make new layer for ele %s', ele.id());
28161 layer = makeLayer({
28162 insert: true,
28163 after: layer
28164 }); // if now layer can be built then we can't use layers at this level
28165
28166 if (!layer) {
28167 return null;
28168 } // log('new layer with id %s', layer.id);
28169
28170 }
28171
28172 if (tmpLayers || allowLazyQueueing) {
28173 // log('queue ele %s in layer %s', ele.id(), layer.id);
28174 self.queueLayer(layer, ele);
28175 } else {
28176 // log('draw ele %s in layer %s', ele.id(), layer.id);
28177 self.drawEleInLayer(layer, ele, lvl, pxRatio);
28178 }
28179
28180 layer.eles.push(ele);
28181 caches[lvl] = layer;
28182 } // log('--');
28183
28184
28185 if (tmpLayers) {
28186 // then we only queued the current layerset and can't draw it yet
28187 return tmpLayers;
28188 }
28189
28190 if (allowLazyQueueing) {
28191 // log('lazy queue level', lvl);
28192 return null;
28193 }
28194
28195 return layers;
28196}; // a layer may want to use an ele cache of a higher level to avoid blurriness
28197// so the layer level might not equal the ele level
28198
28199
28200LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28201 return lvl;
28202};
28203
28204LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28205 var self = this;
28206 var r = this.renderer;
28207 var context = layer.context;
28208 var bb = ele.boundingBox();
28209
28210 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28211 return;
28212 }
28213
28214 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28215
28216 {
28217 r.setImgSmoothing(context, false);
28218 }
28219
28220 {
28221 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28222 }
28223
28224 {
28225 r.setImgSmoothing(context, true);
28226 }
28227};
28228
28229LTCp.levelIsComplete = function (lvl, eles) {
28230 var self = this;
28231 var layers = self.layersByLevel[lvl];
28232
28233 if (!layers || layers.length === 0) {
28234 return false;
28235 }
28236
28237 var numElesInLayers = 0;
28238
28239 for (var i = 0; i < layers.length; i++) {
28240 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28241
28242 if (layer.reqs > 0) {
28243 return false;
28244 } // if the layer is invalid, the level is not complete
28245
28246
28247 if (layer.invalid) {
28248 return false;
28249 }
28250
28251 numElesInLayers += layer.eles.length;
28252 } // we should have exactly the number of eles passed in to be complete
28253
28254
28255 if (numElesInLayers !== eles.length) {
28256 return false;
28257 }
28258
28259 return true;
28260};
28261
28262LTCp.validateLayersElesOrdering = function (lvl, eles) {
28263 var layers = this.layersByLevel[lvl];
28264
28265 if (!layers) {
28266 return;
28267 } // if in a layer the eles are not in the same order, then the layer is invalid
28268 // (i.e. there is an ele in between the eles in the layer)
28269
28270
28271 for (var i = 0; i < layers.length; i++) {
28272 var layer = layers[i];
28273 var offset = -1; // find the offset
28274
28275 for (var j = 0; j < eles.length; j++) {
28276 if (layer.eles[0] === eles[j]) {
28277 offset = j;
28278 break;
28279 }
28280 }
28281
28282 if (offset < 0) {
28283 // then the layer has nonexistant elements and is invalid
28284 this.invalidateLayer(layer);
28285 continue;
28286 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28287
28288
28289 var o = offset;
28290
28291 for (var j = 0; j < layer.eles.length; j++) {
28292 if (layer.eles[j] !== eles[o + j]) {
28293 // log('invalidate based on ordering', layer.id);
28294 this.invalidateLayer(layer);
28295 break;
28296 }
28297 }
28298 }
28299};
28300
28301LTCp.updateElementsInLayers = function (eles, update) {
28302 var self = this;
28303 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28304 // layer itself along the way
28305
28306 for (var i = 0; i < eles.length; i++) {
28307 var req = isEles ? null : eles[i];
28308 var ele = isEles ? eles[i] : eles[i].ele;
28309 var rs = ele._private.rscratch;
28310 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28311
28312 for (var l = minLvl; l <= maxLvl; l++) {
28313 var layer = caches[l];
28314
28315 if (!layer) {
28316 continue;
28317 } // if update is a request from the ele cache, then it affects only
28318 // the matching level
28319
28320
28321 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28322 continue;
28323 }
28324
28325 update(layer, ele, req);
28326 }
28327 }
28328};
28329
28330LTCp.haveLayers = function () {
28331 var self = this;
28332 var haveLayers = false;
28333
28334 for (var l = minLvl; l <= maxLvl; l++) {
28335 var layers = self.layersByLevel[l];
28336
28337 if (layers && layers.length > 0) {
28338 haveLayers = true;
28339 break;
28340 }
28341 }
28342
28343 return haveLayers;
28344};
28345
28346LTCp.invalidateElements = function (eles) {
28347 var self = this;
28348
28349 if (eles.length === 0) {
28350 return;
28351 }
28352
28353 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28354
28355 if (eles.length === 0 || !self.haveLayers()) {
28356 return;
28357 }
28358
28359 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28360 self.invalidateLayer(layer);
28361 });
28362};
28363
28364LTCp.invalidateLayer = function (layer) {
28365 // log('update invalidate layer time');
28366 this.lastInvalidationTime = performanceNow();
28367
28368 if (layer.invalid) {
28369 return;
28370 } // save cycles
28371
28372
28373 var lvl = layer.level;
28374 var eles = layer.eles;
28375 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28376
28377 removeFromArray(layers, layer); // layer.eles = [];
28378
28379 layer.elesQueue = [];
28380 layer.invalid = true;
28381
28382 if (layer.replacement) {
28383 layer.replacement.invalid = true;
28384 }
28385
28386 for (var i = 0; i < eles.length; i++) {
28387 var caches = eles[i]._private.rscratch.imgLayerCaches;
28388
28389 if (caches) {
28390 caches[lvl] = null;
28391 }
28392 }
28393};
28394
28395LTCp.refineElementTextures = function (eles) {
28396 var self = this; // log('refine', eles.length);
28397
28398 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28399 var rLyr = layer.replacement;
28400
28401 if (!rLyr) {
28402 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28403 rLyr.replaces = layer;
28404 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28405 }
28406
28407 if (!rLyr.reqs) {
28408 for (var i = 0; i < rLyr.eles.length; i++) {
28409 self.queueLayer(rLyr, rLyr.eles[i]);
28410 } // log('queue replacement layer refinement', rLyr.id);
28411
28412 }
28413 });
28414};
28415
28416LTCp.enqueueElementRefinement = function (ele) {
28417
28418 this.eleTxrDeqs.merge(ele);
28419 this.scheduleElementRefinement();
28420};
28421
28422LTCp.queueLayer = function (layer, ele) {
28423 var self = this;
28424 var q = self.layersQueue;
28425 var elesQ = layer.elesQueue;
28426 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28427
28428 if (layer.replacement) {
28429 return;
28430 }
28431
28432 if (ele) {
28433 if (hasId[ele.id()]) {
28434 return;
28435 }
28436
28437 elesQ.push(ele);
28438 hasId[ele.id()] = true;
28439 }
28440
28441 if (layer.reqs) {
28442 layer.reqs++;
28443 q.updateItem(layer);
28444 } else {
28445 layer.reqs = 1;
28446 q.push(layer);
28447 }
28448};
28449
28450LTCp.dequeue = function (pxRatio) {
28451 var self = this;
28452 var q = self.layersQueue;
28453 var deqd = [];
28454 var eleDeqs = 0;
28455
28456 while (eleDeqs < maxDeqSize) {
28457 if (q.size() === 0) {
28458 break;
28459 }
28460
28461 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28462
28463 if (layer.replacement) {
28464 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28465 q.pop();
28466 continue;
28467 } // if this is a replacement layer that has been superceded, then forget it
28468
28469
28470 if (layer.replaces && layer !== layer.replaces.replacement) {
28471 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28472 q.pop();
28473 continue;
28474 }
28475
28476 if (layer.invalid) {
28477 // log('replacement layer %s is invalid; dequeued', layer.id);
28478 q.pop();
28479 continue;
28480 }
28481
28482 var ele = layer.elesQueue.shift();
28483
28484 if (ele) {
28485 // log('dequeue layer %s', layer.id);
28486 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28487 eleDeqs++;
28488 }
28489
28490 if (deqd.length === 0) {
28491 // we need only one entry in deqd to queue redrawing etc
28492 deqd.push(true);
28493 } // if the layer has all its eles done, then remove from the queue
28494
28495
28496 if (layer.elesQueue.length === 0) {
28497 q.pop();
28498 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28499 // when a replacement layer is dequeued, it replaces the old layer in the level
28500
28501 if (layer.replaces) {
28502 self.applyLayerReplacement(layer);
28503 }
28504
28505 self.requestRedraw();
28506 }
28507 }
28508
28509 return deqd;
28510};
28511
28512LTCp.applyLayerReplacement = function (layer) {
28513 var self = this;
28514 var layersInLevel = self.layersByLevel[layer.level];
28515 var replaced = layer.replaces;
28516 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28517 // refs would be a mistake (i.e. overwriting the true active layer)
28518
28519 if (index < 0 || replaced.invalid) {
28520 // log('replacement layer would have no effect', layer.id);
28521 return;
28522 }
28523
28524 layersInLevel[index] = layer; // replace level ref
28525 // replace refs in eles
28526
28527 for (var i = 0; i < layer.eles.length; i++) {
28528 var _p = layer.eles[i]._private;
28529 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28530
28531 if (cache) {
28532 cache[layer.level] = layer;
28533 }
28534 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28535
28536
28537 self.requestRedraw();
28538};
28539
28540LTCp.requestRedraw = debounce__default["default"](function () {
28541 var r = this.renderer;
28542 r.redrawHint('eles', true);
28543 r.redrawHint('drag', true);
28544 r.redraw();
28545}, 100);
28546LTCp.setupDequeueing = defs.setupDequeueing({
28547 deqRedrawThreshold: deqRedrawThreshold,
28548 deqCost: deqCost,
28549 deqAvgCost: deqAvgCost,
28550 deqNoDrawCost: deqNoDrawCost,
28551 deqFastCost: deqFastCost,
28552 deq: function deq(self, pxRatio) {
28553 return self.dequeue(pxRatio);
28554 },
28555 onDeqd: noop$1,
28556 shouldRedraw: trueify,
28557 priority: function priority(self) {
28558 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28559 }
28560});
28561
28562var CRp$a = {};
28563var impl;
28564
28565function polygon(context, points) {
28566 for (var i = 0; i < points.length; i++) {
28567 var pt = points[i];
28568 context.lineTo(pt.x, pt.y);
28569 }
28570}
28571
28572function triangleBackcurve(context, points, controlPoint) {
28573 var firstPt;
28574
28575 for (var i = 0; i < points.length; i++) {
28576 var pt = points[i];
28577
28578 if (i === 0) {
28579 firstPt = pt;
28580 }
28581
28582 context.lineTo(pt.x, pt.y);
28583 }
28584
28585 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28586}
28587
28588function triangleTee(context, trianglePoints, teePoints) {
28589 if (context.beginPath) {
28590 context.beginPath();
28591 }
28592
28593 var triPts = trianglePoints;
28594
28595 for (var i = 0; i < triPts.length; i++) {
28596 var pt = triPts[i];
28597 context.lineTo(pt.x, pt.y);
28598 }
28599
28600 var teePts = teePoints;
28601 var firstTeePt = teePoints[0];
28602 context.moveTo(firstTeePt.x, firstTeePt.y);
28603
28604 for (var i = 1; i < teePts.length; i++) {
28605 var pt = teePts[i];
28606 context.lineTo(pt.x, pt.y);
28607 }
28608
28609 if (context.closePath) {
28610 context.closePath();
28611 }
28612}
28613
28614function circleTriangle(context, trianglePoints, rx, ry, r) {
28615 if (context.beginPath) {
28616 context.beginPath();
28617 }
28618
28619 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28620 var triPts = trianglePoints;
28621 var firstTrPt = triPts[0];
28622 context.moveTo(firstTrPt.x, firstTrPt.y);
28623
28624 for (var i = 0; i < triPts.length; i++) {
28625 var pt = triPts[i];
28626 context.lineTo(pt.x, pt.y);
28627 }
28628
28629 if (context.closePath) {
28630 context.closePath();
28631 }
28632}
28633
28634function circle(context, rx, ry, r) {
28635 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28636}
28637
28638CRp$a.arrowShapeImpl = function (name) {
28639 return (impl || (impl = {
28640 'polygon': polygon,
28641 'triangle-backcurve': triangleBackcurve,
28642 'triangle-tee': triangleTee,
28643 'circle-triangle': circleTriangle,
28644 'triangle-cross': triangleTee,
28645 'circle': circle
28646 }))[name];
28647};
28648
28649var CRp$9 = {};
28650
28651CRp$9.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28652 var r = this;
28653
28654 if (ele.isNode()) {
28655 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28656 } else {
28657 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28658 }
28659};
28660
28661CRp$9.drawElementOverlay = function (context, ele) {
28662 var r = this;
28663
28664 if (ele.isNode()) {
28665 r.drawNodeOverlay(context, ele);
28666 } else {
28667 r.drawEdgeOverlay(context, ele);
28668 }
28669};
28670
28671CRp$9.drawElementUnderlay = function (context, ele) {
28672 var r = this;
28673
28674 if (ele.isNode()) {
28675 r.drawNodeUnderlay(context, ele);
28676 } else {
28677 r.drawEdgeUnderlay(context, ele);
28678 }
28679};
28680
28681CRp$9.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28682 var r = this;
28683 var bb = eleTxrCache.getBoundingBox(ele);
28684
28685 if (bb.w === 0 || bb.h === 0) {
28686 return;
28687 } // ignore zero size case
28688
28689
28690 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28691
28692 if (eleCache != null) {
28693 var opacity = getOpacity(r, ele);
28694
28695 if (opacity === 0) {
28696 return;
28697 }
28698
28699 var theta = getRotation(r, ele);
28700 var x1 = bb.x1,
28701 y1 = bb.y1,
28702 w = bb.w,
28703 h = bb.h;
28704 var x, y, sx, sy, smooth;
28705
28706 if (theta !== 0) {
28707 var rotPt = eleTxrCache.getRotationPoint(ele);
28708 sx = rotPt.x;
28709 sy = rotPt.y;
28710 context.translate(sx, sy);
28711 context.rotate(theta);
28712 smooth = r.getImgSmoothing(context);
28713
28714 if (!smooth) {
28715 r.setImgSmoothing(context, true);
28716 }
28717
28718 var off = eleTxrCache.getRotationOffset(ele);
28719 x = off.x;
28720 y = off.y;
28721 } else {
28722 x = x1;
28723 y = y1;
28724 }
28725
28726 var oldGlobalAlpha;
28727
28728 if (opacity !== 1) {
28729 oldGlobalAlpha = context.globalAlpha;
28730 context.globalAlpha = oldGlobalAlpha * opacity;
28731 }
28732
28733 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28734
28735 if (opacity !== 1) {
28736 context.globalAlpha = oldGlobalAlpha;
28737 }
28738
28739 if (theta !== 0) {
28740 context.rotate(-theta);
28741 context.translate(-sx, -sy);
28742
28743 if (!smooth) {
28744 r.setImgSmoothing(context, false);
28745 }
28746 }
28747 } else {
28748 eleTxrCache.drawElement(context, ele); // direct draw fallback
28749 }
28750};
28751
28752var getZeroRotation = function getZeroRotation() {
28753 return 0;
28754};
28755
28756var getLabelRotation = function getLabelRotation(r, ele) {
28757 return r.getTextAngle(ele, null);
28758};
28759
28760var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28761 return r.getTextAngle(ele, 'source');
28762};
28763
28764var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28765 return r.getTextAngle(ele, 'target');
28766};
28767
28768var getOpacity = function getOpacity(r, ele) {
28769 return ele.effectiveOpacity();
28770};
28771
28772var getTextOpacity = function getTextOpacity(e, ele) {
28773 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28774};
28775
28776CRp$9.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28777 var r = this;
28778 var _r$data = r.data,
28779 eleTxrCache = _r$data.eleTxrCache,
28780 lblTxrCache = _r$data.lblTxrCache,
28781 slbTxrCache = _r$data.slbTxrCache,
28782 tlbTxrCache = _r$data.tlbTxrCache;
28783 var bb = ele.boundingBox();
28784 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28785
28786 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28787 return;
28788 }
28789
28790 if (!extent || boundingBoxesIntersect(bb, extent)) {
28791 var isEdge = ele.isEdge();
28792
28793 var badLine = ele.element()._private.rscratch.badLine;
28794
28795 r.drawElementUnderlay(context, ele);
28796 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28797
28798 if (!isEdge || !badLine) {
28799 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28800 }
28801
28802 if (isEdge && !badLine) {
28803 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28804 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28805 }
28806
28807 r.drawElementOverlay(context, ele);
28808 }
28809};
28810
28811CRp$9.drawElements = function (context, eles) {
28812 var r = this;
28813
28814 for (var i = 0; i < eles.length; i++) {
28815 var ele = eles[i];
28816 r.drawElement(context, ele);
28817 }
28818};
28819
28820CRp$9.drawCachedElements = function (context, eles, pxRatio, extent) {
28821 var r = this;
28822
28823 for (var i = 0; i < eles.length; i++) {
28824 var ele = eles[i];
28825 r.drawCachedElement(context, ele, pxRatio, extent);
28826 }
28827};
28828
28829CRp$9.drawCachedNodes = function (context, eles, pxRatio, extent) {
28830 var r = this;
28831
28832 for (var i = 0; i < eles.length; i++) {
28833 var ele = eles[i];
28834
28835 if (!ele.isNode()) {
28836 continue;
28837 }
28838
28839 r.drawCachedElement(context, ele, pxRatio, extent);
28840 }
28841};
28842
28843CRp$9.drawLayeredElements = function (context, eles, pxRatio, extent) {
28844 var r = this;
28845 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28846
28847 if (layers) {
28848 for (var i = 0; i < layers.length; i++) {
28849 var layer = layers[i];
28850 var bb = layer.bb;
28851
28852 if (bb.w === 0 || bb.h === 0) {
28853 continue;
28854 }
28855
28856 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28857 }
28858 } else {
28859 // fall back on plain caching if no layers
28860 r.drawCachedElements(context, eles, pxRatio, extent);
28861 }
28862};
28863
28864/* global Path2D */
28865var CRp$8 = {};
28866
28867CRp$8.drawEdge = function (context, edge, shiftToOriginWithBb) {
28868 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28869 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28870 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28871 var r = this;
28872 var rs = edge._private.rscratch;
28873
28874 if (shouldDrawOpacity && !edge.visible()) {
28875 return;
28876 } // if bezier ctrl pts can not be calculated, then die
28877
28878
28879 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28880 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28881 return;
28882 }
28883
28884 var bb;
28885
28886 if (shiftToOriginWithBb) {
28887 bb = shiftToOriginWithBb;
28888 context.translate(-bb.x1, -bb.y1);
28889 }
28890
28891 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28892 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28893 var curveStyle = edge.pstyle('curve-style').value;
28894 var lineStyle = edge.pstyle('line-style').value;
28895 var edgeWidth = edge.pstyle('width').pfValue;
28896 var lineCap = edge.pstyle('line-cap').value;
28897 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
28898
28899 var effectiveArrowOpacity = opacity * lineOpacity;
28900
28901 var drawLine = function drawLine() {
28902 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28903
28904 if (curveStyle === 'straight-triangle') {
28905 r.eleStrokeStyle(context, edge, strokeOpacity);
28906 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28907 } else {
28908 context.lineWidth = edgeWidth;
28909 context.lineCap = lineCap;
28910 r.eleStrokeStyle(context, edge, strokeOpacity);
28911 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28912 context.lineCap = 'butt'; // reset for other drawing functions
28913 }
28914 };
28915
28916 var drawOverlay = function drawOverlay() {
28917 if (!shouldDrawOverlay) {
28918 return;
28919 }
28920
28921 r.drawEdgeOverlay(context, edge);
28922 };
28923
28924 var drawUnderlay = function drawUnderlay() {
28925 if (!shouldDrawOverlay) {
28926 return;
28927 }
28928
28929 r.drawEdgeUnderlay(context, edge);
28930 };
28931
28932 var drawArrows = function drawArrows() {
28933 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28934 r.drawArrowheads(context, edge, arrowOpacity);
28935 };
28936
28937 var drawText = function drawText() {
28938 r.drawElementText(context, edge, null, drawLabel);
28939 };
28940
28941 context.lineJoin = 'round';
28942 var ghost = edge.pstyle('ghost').value === 'yes';
28943
28944 if (ghost) {
28945 var gx = edge.pstyle('ghost-offset-x').pfValue;
28946 var gy = edge.pstyle('ghost-offset-y').pfValue;
28947 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28948 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28949 context.translate(gx, gy);
28950 drawLine(effectiveGhostOpacity);
28951 drawArrows(effectiveGhostOpacity);
28952 context.translate(-gx, -gy);
28953 }
28954
28955 drawUnderlay();
28956 drawLine();
28957 drawArrows();
28958 drawOverlay();
28959 drawText();
28960
28961 if (shiftToOriginWithBb) {
28962 context.translate(bb.x1, bb.y1);
28963 }
28964};
28965
28966var drawEdgeOverlayUnderlay = function drawEdgeOverlayUnderlay(overlayOrUnderlay) {
28967 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
28968 throw new Error('Invalid state');
28969 }
28970
28971 return function (context, edge) {
28972 if (!edge.visible()) {
28973 return;
28974 }
28975
28976 var opacity = edge.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
28977
28978 if (opacity === 0) {
28979 return;
28980 }
28981
28982 var r = this;
28983 var usePaths = r.usePaths();
28984 var rs = edge._private.rscratch;
28985 var padding = edge.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
28986 var width = 2 * padding;
28987 var color = edge.pstyle("".concat(overlayOrUnderlay, "-color")).value;
28988 context.lineWidth = width;
28989
28990 if (rs.edgeType === 'self' && !usePaths) {
28991 context.lineCap = 'butt';
28992 } else {
28993 context.lineCap = 'round';
28994 }
28995
28996 r.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28997 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28998 };
28999};
29000
29001CRp$8.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
29002CRp$8.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
29003
29004CRp$8.drawEdgePath = function (edge, context, pts, type) {
29005 var rs = edge._private.rscratch;
29006 var canvasCxt = context;
29007 var path;
29008 var pathCacheHit = false;
29009 var usePaths = this.usePaths();
29010 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
29011 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
29012
29013 if (usePaths) {
29014 var pathCacheKey = pts.join('$');
29015 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
29016
29017 if (keyMatches) {
29018 path = context = rs.pathCache;
29019 pathCacheHit = true;
29020 } else {
29021 path = context = new Path2D();
29022 rs.pathCacheKey = pathCacheKey;
29023 rs.pathCache = path;
29024 }
29025 }
29026
29027 if (canvasCxt.setLineDash) {
29028 // for very outofdate browsers
29029 switch (type) {
29030 case 'dotted':
29031 canvasCxt.setLineDash([1, 1]);
29032 break;
29033
29034 case 'dashed':
29035 canvasCxt.setLineDash(lineDashPattern);
29036 canvasCxt.lineDashOffset = lineDashOffset;
29037 break;
29038
29039 case 'solid':
29040 canvasCxt.setLineDash([]);
29041 break;
29042 }
29043 }
29044
29045 if (!pathCacheHit && !rs.badLine) {
29046 if (context.beginPath) {
29047 context.beginPath();
29048 }
29049
29050 context.moveTo(pts[0], pts[1]);
29051
29052 switch (rs.edgeType) {
29053 case 'bezier':
29054 case 'self':
29055 case 'compound':
29056 case 'multibezier':
29057 for (var i = 2; i + 3 < pts.length; i += 4) {
29058 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
29059 }
29060
29061 break;
29062
29063 case 'straight':
29064 case 'segments':
29065 case 'haystack':
29066 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
29067 context.lineTo(pts[_i], pts[_i + 1]);
29068 }
29069
29070 break;
29071 }
29072 }
29073
29074 context = canvasCxt;
29075
29076 if (usePaths) {
29077 context.stroke(path);
29078 } else {
29079 context.stroke();
29080 } // reset any line dashes
29081
29082
29083 if (context.setLineDash) {
29084 // for very outofdate browsers
29085 context.setLineDash([]);
29086 }
29087};
29088
29089CRp$8.drawEdgeTrianglePath = function (edge, context, pts) {
29090 // use line stroke style for triangle fill style
29091 context.fillStyle = context.strokeStyle;
29092 var edgeWidth = edge.pstyle('width').pfValue;
29093
29094 for (var i = 0; i + 1 < pts.length; i += 2) {
29095 var vector = [pts[i + 2] - pts[i], pts[i + 3] - pts[i + 1]];
29096 var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
29097 var normal = [vector[1] / length, -vector[0] / length];
29098 var triangleHead = [normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2];
29099 context.beginPath();
29100 context.moveTo(pts[i] - triangleHead[0], pts[i + 1] - triangleHead[1]);
29101 context.lineTo(pts[i] + triangleHead[0], pts[i + 1] + triangleHead[1]);
29102 context.lineTo(pts[i + 2], pts[i + 3]);
29103 context.closePath();
29104 context.fill();
29105 }
29106};
29107
29108CRp$8.drawArrowheads = function (context, edge, opacity) {
29109 var rs = edge._private.rscratch;
29110 var isHaystack = rs.edgeType === 'haystack';
29111
29112 if (!isHaystack) {
29113 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
29114 }
29115
29116 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
29117 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
29118
29119 if (!isHaystack) {
29120 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
29121 }
29122};
29123
29124CRp$8.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
29125 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
29126 return;
29127 }
29128
29129 var self = this;
29130 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
29131
29132 if (arrowShape === 'none') {
29133 return;
29134 }
29135
29136 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
29137 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
29138 var edgeWidth = edge.pstyle('width').pfValue;
29139 var edgeOpacity = edge.pstyle('opacity').value;
29140
29141 if (opacity === undefined) {
29142 opacity = edgeOpacity;
29143 }
29144
29145 var gco = context.globalCompositeOperation;
29146
29147 if (opacity !== 1 || arrowFill === 'hollow') {
29148 // then extra clear is needed
29149 context.globalCompositeOperation = 'destination-out';
29150 self.colorFillStyle(context, 255, 255, 255, 1);
29151 self.colorStrokeStyle(context, 255, 255, 255, 1);
29152 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
29153 context.globalCompositeOperation = gco;
29154 } // otherwise, the opaque arrow clears it for free :)
29155
29156
29157 var color = edge.pstyle(prefix + '-arrow-color').value;
29158 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
29159 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
29160 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
29161};
29162
29163CRp$8.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
29164 var r = this;
29165 var usePaths = this.usePaths() && shape !== 'triangle-cross';
29166 var pathCacheHit = false;
29167 var path;
29168 var canvasContext = context;
29169 var translation = {
29170 x: x,
29171 y: y
29172 };
29173 var scale = edge.pstyle('arrow-scale').value;
29174 var size = this.getArrowWidth(edgeWidth, scale);
29175 var shapeImpl = r.arrowShapes[shape];
29176
29177 if (usePaths) {
29178 var cache = r.arrowPathCache = r.arrowPathCache || [];
29179 var key = hashString(shape);
29180 var cachedPath = cache[key];
29181
29182 if (cachedPath != null) {
29183 path = context = cachedPath;
29184 pathCacheHit = true;
29185 } else {
29186 path = context = new Path2D();
29187 cache[key] = path;
29188 }
29189 }
29190
29191 if (!pathCacheHit) {
29192 if (context.beginPath) {
29193 context.beginPath();
29194 }
29195
29196 if (usePaths) {
29197 // store in the path cache with values easily manipulated later
29198 shapeImpl.draw(context, 1, 0, {
29199 x: 0,
29200 y: 0
29201 }, 1);
29202 } else {
29203 shapeImpl.draw(context, size, angle, translation, edgeWidth);
29204 }
29205
29206 if (context.closePath) {
29207 context.closePath();
29208 }
29209 }
29210
29211 context = canvasContext;
29212
29213 if (usePaths) {
29214 // set transform to arrow position/orientation
29215 context.translate(x, y);
29216 context.rotate(angle);
29217 context.scale(size, size);
29218 }
29219
29220 if (fill === 'filled' || fill === 'both') {
29221 if (usePaths) {
29222 context.fill(path);
29223 } else {
29224 context.fill();
29225 }
29226 }
29227
29228 if (fill === 'hollow' || fill === 'both') {
29229 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
29230 context.lineJoin = 'miter';
29231
29232 if (usePaths) {
29233 context.stroke(path);
29234 } else {
29235 context.stroke();
29236 }
29237 }
29238
29239 if (usePaths) {
29240 // reset transform by applying inverse
29241 context.scale(1 / size, 1 / size);
29242 context.rotate(-angle);
29243 context.translate(-x, -y);
29244 }
29245};
29246
29247var CRp$7 = {};
29248
29249CRp$7.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29250 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29251 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29252 return;
29253 }
29254
29255 try {
29256 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29257 } catch (e) {
29258 warn(e);
29259 }
29260};
29261
29262CRp$7.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29263 var r = this;
29264 var pos = node.position();
29265 var nodeX = pos.x;
29266 var nodeY = pos.y;
29267 var styleObj = node.cy().style();
29268 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29269 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29270 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29271 var nodeW = node.width();
29272 var nodeH = node.height();
29273 var paddingX2 = node.padding() * 2;
29274 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29275 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29276 var rs = node._private.rscratch;
29277 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29278 var shouldClip = clip === 'node';
29279 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29280 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29281 var imgW = img.width || img.cachedW;
29282 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29283
29284 if (null == imgW || null == imgH) {
29285 document.body.appendChild(img); // eslint-disable-line no-undef
29286
29287 imgW = img.cachedW = img.width || img.offsetWidth;
29288 imgH = img.cachedH = img.height || img.offsetHeight;
29289 document.body.removeChild(img); // eslint-disable-line no-undef
29290 }
29291
29292 var w = imgW;
29293 var h = imgH;
29294
29295 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29296 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29297 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29298 } else {
29299 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29300 }
29301 }
29302
29303 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29304 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29305 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29306 } else {
29307 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29308 }
29309 }
29310
29311 if (w === 0 || h === 0) {
29312 return; // no point in drawing empty image (and chrome is broken in this case)
29313 }
29314
29315 if (fit === 'contain') {
29316 var scale = Math.min(nodeTW / w, nodeTH / h);
29317 w *= scale;
29318 h *= scale;
29319 } else if (fit === 'cover') {
29320 var scale = Math.max(nodeTW / w, nodeTH / h);
29321 w *= scale;
29322 h *= scale;
29323 }
29324
29325 var x = nodeX - nodeTW / 2; // left
29326
29327 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29328 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29329
29330 if (posXUnits === '%') {
29331 x += (nodeTW - w) * posXPfVal;
29332 } else {
29333 x += posXPfVal;
29334 }
29335
29336 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29337 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29338
29339 if (offXUnits === '%') {
29340 x += (nodeTW - w) * offXPfVal;
29341 } else {
29342 x += offXPfVal;
29343 }
29344
29345 var y = nodeY - nodeTH / 2; // top
29346
29347 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29348 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29349
29350 if (posYUnits === '%') {
29351 y += (nodeTH - h) * posYPfVal;
29352 } else {
29353 y += posYPfVal;
29354 }
29355
29356 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29357 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29358
29359 if (offYUnits === '%') {
29360 y += (nodeTH - h) * offYPfVal;
29361 } else {
29362 y += offYPfVal;
29363 }
29364
29365 if (rs.pathCache) {
29366 x -= nodeX;
29367 y -= nodeY;
29368 nodeX = 0;
29369 nodeY = 0;
29370 }
29371
29372 var gAlpha = context.globalAlpha;
29373 context.globalAlpha = imgOpacity;
29374 var smoothingEnabled = r.getImgSmoothing(context);
29375 var isSmoothingSwitched = false;
29376
29377 if (smooth === 'no' && smoothingEnabled) {
29378 r.setImgSmoothing(context, false);
29379 isSmoothingSwitched = true;
29380 } else if (smooth === 'yes' && !smoothingEnabled) {
29381 r.setImgSmoothing(context, true);
29382 isSmoothingSwitched = true;
29383 }
29384
29385 if (repeat === 'no-repeat') {
29386 if (shouldClip) {
29387 context.save();
29388
29389 if (rs.pathCache) {
29390 context.clip(rs.pathCache);
29391 } else {
29392 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29393 context.clip();
29394 }
29395 }
29396
29397 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29398
29399 if (shouldClip) {
29400 context.restore();
29401 }
29402 } else {
29403 var pattern = context.createPattern(img, repeat);
29404 context.fillStyle = pattern;
29405 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29406 context.translate(x, y);
29407 context.fill();
29408 context.translate(-x, -y);
29409 }
29410
29411 context.globalAlpha = gAlpha;
29412
29413 if (isSmoothingSwitched) {
29414 r.setImgSmoothing(context, smoothingEnabled);
29415 }
29416};
29417
29418var CRp$6 = {};
29419
29420CRp$6.eleTextBiggerThanMin = function (ele, scale) {
29421 if (!scale) {
29422 var zoom = ele.cy().zoom();
29423 var pxRatio = this.getPixelRatio();
29424 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29425
29426 scale = Math.pow(2, lvl);
29427 }
29428
29429 var computedSize = ele.pstyle('font-size').pfValue * scale;
29430 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29431
29432 if (computedSize < minSize) {
29433 return false;
29434 }
29435
29436 return true;
29437};
29438
29439CRp$6.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29440 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29441 var r = this;
29442
29443 if (force == null) {
29444 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29445 return;
29446 }
29447 } else if (force === false) {
29448 return;
29449 }
29450
29451 if (ele.isNode()) {
29452 var label = ele.pstyle('label');
29453
29454 if (!label || !label.value) {
29455 return;
29456 }
29457
29458 var justification = r.getLabelJustification(ele);
29459 context.textAlign = justification;
29460 context.textBaseline = 'bottom';
29461 } else {
29462 var badLine = ele.element()._private.rscratch.badLine;
29463
29464 var _label = ele.pstyle('label');
29465
29466 var srcLabel = ele.pstyle('source-label');
29467 var tgtLabel = ele.pstyle('target-label');
29468
29469 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29470 return;
29471 }
29472
29473 context.textAlign = 'center';
29474 context.textBaseline = 'bottom';
29475 }
29476
29477 var applyRotation = !shiftToOriginWithBb;
29478 var bb;
29479
29480 if (shiftToOriginWithBb) {
29481 bb = shiftToOriginWithBb;
29482 context.translate(-bb.x1, -bb.y1);
29483 }
29484
29485 if (prefix == null) {
29486 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29487
29488 if (ele.isEdge()) {
29489 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29490 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29491 }
29492 } else {
29493 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29494 }
29495
29496 if (shiftToOriginWithBb) {
29497 context.translate(bb.x1, bb.y1);
29498 }
29499};
29500
29501CRp$6.getFontCache = function (context) {
29502 var cache;
29503 this.fontCaches = this.fontCaches || [];
29504
29505 for (var i = 0; i < this.fontCaches.length; i++) {
29506 cache = this.fontCaches[i];
29507
29508 if (cache.context === context) {
29509 return cache;
29510 }
29511 }
29512
29513 cache = {
29514 context: context
29515 };
29516 this.fontCaches.push(cache);
29517 return cache;
29518}; // set up canvas context with font
29519// returns transformed text string
29520
29521
29522CRp$6.setupTextStyle = function (context, ele) {
29523 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29524 // Font style
29525 var labelStyle = ele.pstyle('font-style').strValue;
29526 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29527 var labelFamily = ele.pstyle('font-family').strValue;
29528 var labelWeight = ele.pstyle('font-weight').strValue;
29529 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29530 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29531 var color = ele.pstyle('color').value;
29532 var outlineColor = ele.pstyle('text-outline-color').value;
29533 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29534 context.lineJoin = 'round'; // so text outlines aren't jagged
29535
29536 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29537 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29538}; // TODO ensure re-used
29539
29540
29541function roundRect(ctx, x, y, width, height) {
29542 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29543 ctx.beginPath();
29544 ctx.moveTo(x + radius, y);
29545 ctx.lineTo(x + width - radius, y);
29546 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29547 ctx.lineTo(x + width, y + height - radius);
29548 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29549 ctx.lineTo(x + radius, y + height);
29550 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29551 ctx.lineTo(x, y + radius);
29552 ctx.quadraticCurveTo(x, y, x + radius, y);
29553 ctx.closePath();
29554 ctx.fill();
29555}
29556
29557CRp$6.getTextAngle = function (ele, prefix) {
29558 var theta;
29559 var _p = ele._private;
29560 var rscratch = _p.rscratch;
29561 var pdash = prefix ? prefix + '-' : '';
29562 var rotation = ele.pstyle(pdash + 'text-rotation');
29563 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29564
29565 if (rotation.strValue === 'autorotate') {
29566 theta = ele.isEdge() ? textAngle : 0;
29567 } else if (rotation.strValue === 'none') {
29568 theta = 0;
29569 } else {
29570 theta = rotation.pfValue;
29571 }
29572
29573 return theta;
29574};
29575
29576CRp$6.drawText = function (context, ele, prefix) {
29577 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29578 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29579 var _p = ele._private;
29580 var rscratch = _p.rscratch;
29581 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29582
29583 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29584 return;
29585 } // use 'main' as an alias for the main label (i.e. null prefix)
29586
29587
29588 if (prefix === 'main') {
29589 prefix = null;
29590 }
29591
29592 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29593 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29594 var orgTextX, orgTextY; // used for rotation
29595
29596 var text = this.getLabelText(ele, prefix);
29597
29598 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29599 this.setupTextStyle(context, ele, useEleOpacity);
29600 var pdash = prefix ? prefix + '-' : '';
29601 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29602 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29603 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29604 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29605 var isEdge = ele.isEdge();
29606 var halign = ele.pstyle('text-halign').value;
29607 var valign = ele.pstyle('text-valign').value;
29608
29609 if (isEdge) {
29610 halign = 'center';
29611 valign = 'center';
29612 }
29613
29614 textX += marginX;
29615 textY += marginY;
29616 var theta;
29617
29618 if (!applyRotation) {
29619 theta = 0;
29620 } else {
29621 theta = this.getTextAngle(ele, prefix);
29622 }
29623
29624 if (theta !== 0) {
29625 orgTextX = textX;
29626 orgTextY = textY;
29627 context.translate(orgTextX, orgTextY);
29628 context.rotate(theta);
29629 textX = 0;
29630 textY = 0;
29631 }
29632
29633 switch (valign) {
29634 case 'top':
29635 break;
29636
29637 case 'center':
29638 textY += textH / 2;
29639 break;
29640
29641 case 'bottom':
29642 textY += textH;
29643 break;
29644 }
29645
29646 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29647 var borderOpacity = ele.pstyle('text-border-opacity').value;
29648 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29649 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29650
29651 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29652 var bgX = textX - backgroundPadding;
29653
29654 switch (halign) {
29655 case 'left':
29656 bgX -= textW;
29657 break;
29658
29659 case 'center':
29660 bgX -= textW / 2;
29661 break;
29662 }
29663
29664 var bgY = textY - textH - backgroundPadding;
29665 var bgW = textW + 2 * backgroundPadding;
29666 var bgH = textH + 2 * backgroundPadding;
29667
29668 if (backgroundOpacity > 0) {
29669 var textFill = context.fillStyle;
29670 var textBackgroundColor = ele.pstyle('text-background-color').value;
29671 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29672 var styleShape = ele.pstyle('text-background-shape').strValue;
29673
29674 if (styleShape.indexOf('round') === 0) {
29675 roundRect(context, bgX, bgY, bgW, bgH, 2);
29676 } else {
29677 context.fillRect(bgX, bgY, bgW, bgH);
29678 }
29679
29680 context.fillStyle = textFill;
29681 }
29682
29683 if (textBorderWidth > 0 && borderOpacity > 0) {
29684 var textStroke = context.strokeStyle;
29685 var textLineWidth = context.lineWidth;
29686 var textBorderColor = ele.pstyle('text-border-color').value;
29687 var textBorderStyle = ele.pstyle('text-border-style').value;
29688 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29689 context.lineWidth = textBorderWidth;
29690
29691 if (context.setLineDash) {
29692 // for very outofdate browsers
29693 switch (textBorderStyle) {
29694 case 'dotted':
29695 context.setLineDash([1, 1]);
29696 break;
29697
29698 case 'dashed':
29699 context.setLineDash([4, 2]);
29700 break;
29701
29702 case 'double':
29703 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29704
29705 context.setLineDash([]);
29706 break;
29707
29708 case 'solid':
29709 context.setLineDash([]);
29710 break;
29711 }
29712 }
29713
29714 context.strokeRect(bgX, bgY, bgW, bgH);
29715
29716 if (textBorderStyle === 'double') {
29717 var whiteWidth = textBorderWidth / 2;
29718 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29719 }
29720
29721 if (context.setLineDash) {
29722 // for very outofdate browsers
29723 context.setLineDash([]);
29724 }
29725
29726 context.lineWidth = textLineWidth;
29727 context.strokeStyle = textStroke;
29728 }
29729 }
29730
29731 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29732
29733 if (lineWidth > 0) {
29734 context.lineWidth = lineWidth;
29735 }
29736
29737 if (ele.pstyle('text-wrap').value === 'wrap') {
29738 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29739 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29740 var halfTextW = textW / 2;
29741 var justification = this.getLabelJustification(ele);
29742
29743 if (justification === 'auto') ; else if (halign === 'left') {
29744 // auto justification : right
29745 if (justification === 'left') {
29746 textX += -textW;
29747 } else if (justification === 'center') {
29748 textX += -halfTextW;
29749 } // else same as auto
29750
29751 } else if (halign === 'center') {
29752 // auto justfication : center
29753 if (justification === 'left') {
29754 textX += -halfTextW;
29755 } else if (justification === 'right') {
29756 textX += halfTextW;
29757 } // else same as auto
29758
29759 } else if (halign === 'right') {
29760 // auto justification : left
29761 if (justification === 'center') {
29762 textX += halfTextW;
29763 } else if (justification === 'right') {
29764 textX += textW;
29765 } // else same as auto
29766
29767 }
29768
29769 switch (valign) {
29770 case 'top':
29771 textY -= (lines.length - 1) * lineHeight;
29772 break;
29773
29774 case 'center':
29775 case 'bottom':
29776 textY -= (lines.length - 1) * lineHeight;
29777 break;
29778 }
29779
29780 for (var l = 0; l < lines.length; l++) {
29781 if (lineWidth > 0) {
29782 context.strokeText(lines[l], textX, textY);
29783 }
29784
29785 context.fillText(lines[l], textX, textY);
29786 textY += lineHeight;
29787 }
29788 } else {
29789 if (lineWidth > 0) {
29790 context.strokeText(text, textX, textY);
29791 }
29792
29793 context.fillText(text, textX, textY);
29794 }
29795
29796 if (theta !== 0) {
29797 context.rotate(-theta);
29798 context.translate(-orgTextX, -orgTextY);
29799 }
29800 }
29801};
29802
29803/* global Path2D */
29804var CRp$5 = {};
29805
29806CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29807 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29808 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29809 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29810 var r = this;
29811 var nodeWidth, nodeHeight;
29812 var _p = node._private;
29813 var rs = _p.rscratch;
29814 var pos = node.position();
29815
29816 if (!number$1(pos.x) || !number$1(pos.y)) {
29817 return; // can't draw node with undefined position
29818 }
29819
29820 if (shouldDrawOpacity && !node.visible()) {
29821 return;
29822 }
29823
29824 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29825 var usePaths = r.usePaths();
29826 var path;
29827 var pathCacheHit = false;
29828 var padding = node.padding();
29829 nodeWidth = node.width() + 2 * padding;
29830 nodeHeight = node.height() + 2 * padding; //
29831 // setup shift
29832
29833 var bb;
29834
29835 if (shiftToOriginWithBb) {
29836 bb = shiftToOriginWithBb;
29837 context.translate(-bb.x1, -bb.y1);
29838 } //
29839 // load bg image
29840
29841
29842 var bgImgProp = node.pstyle('background-image');
29843 var urls = bgImgProp.value;
29844 var urlDefined = new Array(urls.length);
29845 var image = new Array(urls.length);
29846 var numImages = 0;
29847
29848 for (var i = 0; i < urls.length; i++) {
29849 var url = urls[i];
29850 var defd = urlDefined[i] = url != null && url !== 'none';
29851
29852 if (defd) {
29853 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29854 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29855
29856 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29857 _p.backgroundTimestamp = Date.now();
29858 node.emitAndNotify('background');
29859 });
29860 }
29861 } //
29862 // setup styles
29863
29864
29865 var darkness = node.pstyle('background-blacken').value;
29866 var borderWidth = node.pstyle('border-width').pfValue;
29867 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29868 var borderColor = node.pstyle('border-color').value;
29869 var borderStyle = node.pstyle('border-style').value;
29870 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29871 context.lineJoin = 'miter'; // so borders are square with the node shape
29872
29873 var setupShapeColor = function setupShapeColor() {
29874 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29875 r.eleFillStyle(context, node, bgOpy);
29876 };
29877
29878 var setupBorderColor = function setupBorderColor() {
29879 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29880 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29881 }; //
29882 // setup shape
29883
29884
29885 var styleShape = node.pstyle('shape').strValue;
29886 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29887
29888 if (usePaths) {
29889 context.translate(pos.x, pos.y);
29890 var pathCache = r.nodePathCache = r.nodePathCache || [];
29891 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29892 var cachedPath = pathCache[key];
29893
29894 if (cachedPath != null) {
29895 path = cachedPath;
29896 pathCacheHit = true;
29897 rs.pathCache = path;
29898 } else {
29899 path = new Path2D();
29900 pathCache[key] = rs.pathCache = path;
29901 }
29902 }
29903
29904 var drawShape = function drawShape() {
29905 if (!pathCacheHit) {
29906 var npos = pos;
29907
29908 if (usePaths) {
29909 npos = {
29910 x: 0,
29911 y: 0
29912 };
29913 }
29914
29915 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29916 }
29917
29918 if (usePaths) {
29919 context.fill(path);
29920 } else {
29921 context.fill();
29922 }
29923 };
29924
29925 var drawImages = function drawImages() {
29926 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29927 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29928 var prevBging = _p.backgrounding;
29929 var totalCompleted = 0;
29930
29931 for (var _i = 0; _i < image.length; _i++) {
29932 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29933
29934 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29935 totalCompleted++;
29936 continue;
29937 }
29938
29939 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29940 totalCompleted++;
29941 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29942 }
29943 }
29944
29945 _p.backgrounding = !(totalCompleted === numImages);
29946
29947 if (prevBging !== _p.backgrounding) {
29948 // update style b/c :backgrounding state changed
29949 node.updateStyle(false);
29950 }
29951 };
29952
29953 var drawPie = function drawPie() {
29954 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29955 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29956
29957 if (r.hasPie(node)) {
29958 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29959
29960 if (redrawShape) {
29961 if (!usePaths) {
29962 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29963 }
29964 }
29965 }
29966 };
29967
29968 var darken = function darken() {
29969 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29970 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29971 var c = darkness > 0 ? 0 : 255;
29972
29973 if (darkness !== 0) {
29974 r.colorFillStyle(context, c, c, c, opacity);
29975
29976 if (usePaths) {
29977 context.fill(path);
29978 } else {
29979 context.fill();
29980 }
29981 }
29982 };
29983
29984 var drawBorder = function drawBorder() {
29985 if (borderWidth > 0) {
29986 context.lineWidth = borderWidth;
29987 context.lineCap = 'butt';
29988
29989 if (context.setLineDash) {
29990 // for very outofdate browsers
29991 switch (borderStyle) {
29992 case 'dotted':
29993 context.setLineDash([1, 1]);
29994 break;
29995
29996 case 'dashed':
29997 context.setLineDash([4, 2]);
29998 break;
29999
30000 case 'solid':
30001 case 'double':
30002 context.setLineDash([]);
30003 break;
30004 }
30005 }
30006
30007 if (usePaths) {
30008 context.stroke(path);
30009 } else {
30010 context.stroke();
30011 }
30012
30013 if (borderStyle === 'double') {
30014 context.lineWidth = borderWidth / 3;
30015 var gco = context.globalCompositeOperation;
30016 context.globalCompositeOperation = 'destination-out';
30017
30018 if (usePaths) {
30019 context.stroke(path);
30020 } else {
30021 context.stroke();
30022 }
30023
30024 context.globalCompositeOperation = gco;
30025 } // reset in case we changed the border style
30026
30027
30028 if (context.setLineDash) {
30029 // for very outofdate browsers
30030 context.setLineDash([]);
30031 }
30032 }
30033 };
30034
30035 var drawOverlay = function drawOverlay() {
30036 if (shouldDrawOverlay) {
30037 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
30038 }
30039 };
30040
30041 var drawUnderlay = function drawUnderlay() {
30042 if (shouldDrawOverlay) {
30043 r.drawNodeUnderlay(context, node, pos, nodeWidth, nodeHeight);
30044 }
30045 };
30046
30047 var drawText = function drawText() {
30048 r.drawElementText(context, node, null, drawLabel);
30049 };
30050
30051 var ghost = node.pstyle('ghost').value === 'yes';
30052
30053 if (ghost) {
30054 var gx = node.pstyle('ghost-offset-x').pfValue;
30055 var gy = node.pstyle('ghost-offset-y').pfValue;
30056 var ghostOpacity = node.pstyle('ghost-opacity').value;
30057 var effGhostOpacity = ghostOpacity * eleOpacity;
30058 context.translate(gx, gy);
30059 setupShapeColor(ghostOpacity * bgOpacity);
30060 drawShape();
30061 drawImages(effGhostOpacity, true);
30062 setupBorderColor(ghostOpacity * borderOpacity);
30063 drawBorder();
30064 drawPie(darkness !== 0 || borderWidth !== 0);
30065 drawImages(effGhostOpacity, false);
30066 darken(effGhostOpacity);
30067 context.translate(-gx, -gy);
30068 }
30069
30070 if (usePaths) {
30071 context.translate(-pos.x, -pos.y);
30072 }
30073
30074 drawUnderlay();
30075
30076 if (usePaths) {
30077 context.translate(pos.x, pos.y);
30078 }
30079
30080 setupShapeColor();
30081 drawShape();
30082 drawImages(eleOpacity, true);
30083 setupBorderColor();
30084 drawBorder();
30085 drawPie(darkness !== 0 || borderWidth !== 0);
30086 drawImages(eleOpacity, false);
30087 darken();
30088
30089 if (usePaths) {
30090 context.translate(-pos.x, -pos.y);
30091 }
30092
30093 drawText();
30094 drawOverlay(); //
30095 // clean up shift
30096
30097 if (shiftToOriginWithBb) {
30098 context.translate(bb.x1, bb.y1);
30099 }
30100};
30101
30102var drawNodeOverlayUnderlay = function drawNodeOverlayUnderlay(overlayOrUnderlay) {
30103 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
30104 throw new Error('Invalid state');
30105 }
30106
30107 return function (context, node, pos, nodeWidth, nodeHeight) {
30108 var r = this;
30109
30110 if (!node.visible()) {
30111 return;
30112 }
30113
30114 var padding = node.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
30115 var opacity = node.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
30116 var color = node.pstyle("".concat(overlayOrUnderlay, "-color")).value;
30117 var shape = node.pstyle("".concat(overlayOrUnderlay, "-shape")).value;
30118
30119 if (opacity > 0) {
30120 pos = pos || node.position();
30121
30122 if (nodeWidth == null || nodeHeight == null) {
30123 var _padding = node.padding();
30124
30125 nodeWidth = node.width() + 2 * _padding;
30126 nodeHeight = node.height() + 2 * _padding;
30127 }
30128
30129 r.colorFillStyle(context, color[0], color[1], color[2], opacity);
30130 r.nodeShapes[shape].draw(context, pos.x, pos.y, nodeWidth + padding * 2, nodeHeight + padding * 2);
30131 context.fill();
30132 }
30133 };
30134};
30135
30136CRp$5.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
30137CRp$5.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay'); // does the node have at least one pie piece?
30138
30139CRp$5.hasPie = function (node) {
30140 node = node[0]; // ensure ele ref
30141
30142 return node._private.hasPie;
30143};
30144
30145CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
30146 node = node[0]; // ensure ele ref
30147
30148 pos = pos || node.position();
30149 var cyStyle = node.cy().style();
30150 var pieSize = node.pstyle('pie-size');
30151 var x = pos.x;
30152 var y = pos.y;
30153 var nodeW = node.width();
30154 var nodeH = node.height();
30155 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
30156
30157 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
30158
30159 var usePaths = this.usePaths();
30160
30161 if (usePaths) {
30162 x = 0;
30163 y = 0;
30164 }
30165
30166 if (pieSize.units === '%') {
30167 radius = radius * pieSize.pfValue;
30168 } else if (pieSize.pfValue !== undefined) {
30169 radius = pieSize.pfValue / 2;
30170 }
30171
30172 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
30173 // 1..N
30174 var size = node.pstyle('pie-' + i + '-background-size').value;
30175 var color = node.pstyle('pie-' + i + '-background-color').value;
30176 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
30177 var percent = size / 100; // map integer range [0, 100] to [0, 1]
30178 // percent can't push beyond 1
30179
30180 if (percent + lastPercent > 1) {
30181 percent = 1 - lastPercent;
30182 }
30183
30184 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
30185
30186 var angleDelta = 2 * Math.PI * percent;
30187 var angleEnd = angleStart + angleDelta; // ignore if
30188 // - zero size
30189 // - we're already beyond the full circle
30190 // - adding the current slice would go beyond the full circle
30191
30192 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
30193 continue;
30194 }
30195
30196 context.beginPath();
30197 context.moveTo(x, y);
30198 context.arc(x, y, radius, angleStart, angleEnd);
30199 context.closePath();
30200 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30201 context.fill();
30202 lastPercent += percent;
30203 }
30204};
30205
30206var CRp$4 = {};
30207var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
30208
30209CRp$4.getPixelRatio = function () {
30210 var context = this.data.contexts[0];
30211
30212 if (this.forcedPixelRatio != null) {
30213 return this.forcedPixelRatio;
30214 }
30215
30216 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
30217 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
30218};
30219
30220CRp$4.paintCache = function (context) {
30221 var caches = this.paintCaches = this.paintCaches || [];
30222 var needToCreateCache = true;
30223 var cache;
30224
30225 for (var i = 0; i < caches.length; i++) {
30226 cache = caches[i];
30227
30228 if (cache.context === context) {
30229 needToCreateCache = false;
30230 break;
30231 }
30232 }
30233
30234 if (needToCreateCache) {
30235 cache = {
30236 context: context
30237 };
30238 caches.push(cache);
30239 }
30240
30241 return cache;
30242};
30243
30244CRp$4.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
30245 var gradientStyle;
30246 var usePaths = this.usePaths();
30247 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
30248 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
30249
30250 if (fill === 'radial-gradient') {
30251 if (ele.isEdge()) {
30252 var start = ele.sourceEndpoint(),
30253 end = ele.targetEndpoint(),
30254 mid = ele.midpoint();
30255 var d1 = dist(start, mid);
30256 var d2 = dist(end, mid);
30257 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
30258 } else {
30259 var pos = usePaths ? {
30260 x: 0,
30261 y: 0
30262 } : ele.position(),
30263 width = ele.paddedWidth(),
30264 height = ele.paddedHeight();
30265 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
30266 }
30267 } else {
30268 if (ele.isEdge()) {
30269 var _start = ele.sourceEndpoint(),
30270 _end = ele.targetEndpoint();
30271
30272 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30273 } else {
30274 var _pos = usePaths ? {
30275 x: 0,
30276 y: 0
30277 } : ele.position(),
30278 _width = ele.paddedWidth(),
30279 _height = ele.paddedHeight(),
30280 halfWidth = _width / 2,
30281 halfHeight = _height / 2;
30282
30283 var direction = ele.pstyle('background-gradient-direction').value;
30284
30285 switch (direction) {
30286 case 'to-bottom':
30287 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30288 break;
30289
30290 case 'to-top':
30291 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30292 break;
30293
30294 case 'to-left':
30295 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30296 break;
30297
30298 case 'to-right':
30299 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30300 break;
30301
30302 case 'to-bottom-right':
30303 case 'to-right-bottom':
30304 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30305 break;
30306
30307 case 'to-top-right':
30308 case 'to-right-top':
30309 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30310 break;
30311
30312 case 'to-bottom-left':
30313 case 'to-left-bottom':
30314 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30315 break;
30316
30317 case 'to-top-left':
30318 case 'to-left-top':
30319 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30320 break;
30321 }
30322 }
30323 }
30324
30325 if (!gradientStyle) return null; // invalid gradient style
30326
30327 var hasPositions = positions.length === colors.length;
30328 var length = colors.length;
30329
30330 for (var i = 0; i < length; i++) {
30331 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30332 }
30333
30334 return gradientStyle;
30335};
30336
30337CRp$4.gradientFillStyle = function (context, ele, fill, opacity) {
30338 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30339 if (!gradientStyle) return null; // error
30340
30341 context.fillStyle = gradientStyle;
30342};
30343
30344CRp$4.colorFillStyle = function (context, r, g, b, a) {
30345 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30346 // var cache = this.paintCache(context);
30347 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30348 // if( cache.fillStyle !== fillStyle ){
30349 // context.fillStyle = cache.fillStyle = fillStyle;
30350 // }
30351};
30352
30353CRp$4.eleFillStyle = function (context, ele, opacity) {
30354 var backgroundFill = ele.pstyle('background-fill').value;
30355
30356 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30357 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30358 } else {
30359 var backgroundColor = ele.pstyle('background-color').value;
30360 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30361 }
30362};
30363
30364CRp$4.gradientStrokeStyle = function (context, ele, fill, opacity) {
30365 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30366 if (!gradientStyle) return null; // error
30367
30368 context.strokeStyle = gradientStyle;
30369};
30370
30371CRp$4.colorStrokeStyle = function (context, r, g, b, a) {
30372 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30373 // var cache = this.paintCache(context);
30374 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30375 // if( cache.strokeStyle !== strokeStyle ){
30376 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30377 // }
30378};
30379
30380CRp$4.eleStrokeStyle = function (context, ele, opacity) {
30381 var lineFill = ele.pstyle('line-fill').value;
30382
30383 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30384 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30385 } else {
30386 var lineColor = ele.pstyle('line-color').value;
30387 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30388 }
30389}; // Resize canvas
30390
30391
30392CRp$4.matchCanvasSize = function (container) {
30393 var r = this;
30394 var data = r.data;
30395 var bb = r.findContainerClientCoords();
30396 var width = bb[2];
30397 var height = bb[3];
30398 var pixelRatio = r.getPixelRatio();
30399 var mbPxRatio = r.motionBlurPxRatio;
30400
30401 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30402 pixelRatio = mbPxRatio;
30403 }
30404
30405 var canvasWidth = width * pixelRatio;
30406 var canvasHeight = height * pixelRatio;
30407 var canvas;
30408
30409 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30410 return; // save cycles if same
30411 }
30412
30413 r.fontCaches = null; // resizing resets the style
30414
30415 var canvasContainer = data.canvasContainer;
30416 canvasContainer.style.width = width + 'px';
30417 canvasContainer.style.height = height + 'px';
30418
30419 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30420 canvas = data.canvases[i];
30421 canvas.width = canvasWidth;
30422 canvas.height = canvasHeight;
30423 canvas.style.width = width + 'px';
30424 canvas.style.height = height + 'px';
30425 }
30426
30427 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30428 canvas = data.bufferCanvases[i];
30429 canvas.width = canvasWidth;
30430 canvas.height = canvasHeight;
30431 canvas.style.width = width + 'px';
30432 canvas.style.height = height + 'px';
30433 }
30434
30435 r.textureMult = 1;
30436
30437 if (pixelRatio <= 1) {
30438 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30439 r.textureMult = 2;
30440 canvas.width = canvasWidth * r.textureMult;
30441 canvas.height = canvasHeight * r.textureMult;
30442 }
30443
30444 r.canvasWidth = canvasWidth;
30445 r.canvasHeight = canvasHeight;
30446};
30447
30448CRp$4.renderTo = function (cxt, zoom, pan, pxRatio) {
30449 this.render({
30450 forcedContext: cxt,
30451 forcedZoom: zoom,
30452 forcedPan: pan,
30453 drawAllLayers: true,
30454 forcedPxRatio: pxRatio
30455 });
30456};
30457
30458CRp$4.render = function (options) {
30459 options = options || staticEmptyObject();
30460 var forcedContext = options.forcedContext;
30461 var drawAllLayers = options.drawAllLayers;
30462 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30463 var forcedZoom = options.forcedZoom;
30464 var forcedPan = options.forcedPan;
30465 var r = this;
30466 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30467 var cy = r.cy;
30468 var data = r.data;
30469 var needDraw = data.canvasNeedsRedraw;
30470 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30471 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30472 var mbPxRatio = r.motionBlurPxRatio;
30473 var hasCompoundNodes = cy.hasCompoundNodes();
30474 var inNodeDragGesture = r.hoverData.draggingEles;
30475 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30476 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30477 var motionBlurFadeEffect = motionBlur;
30478
30479 if (!forcedContext) {
30480 if (r.prevPxRatio !== pixelRatio) {
30481 r.invalidateContainerClientCoordsCache();
30482 r.matchCanvasSize(r.container);
30483 r.redrawHint('eles', true);
30484 r.redrawHint('drag', true);
30485 }
30486
30487 r.prevPxRatio = pixelRatio;
30488 }
30489
30490 if (!forcedContext && r.motionBlurTimeout) {
30491 clearTimeout(r.motionBlurTimeout);
30492 }
30493
30494 if (motionBlur) {
30495 if (r.mbFrames == null) {
30496 r.mbFrames = 0;
30497 }
30498
30499 r.mbFrames++;
30500
30501 if (r.mbFrames < 3) {
30502 // need several frames before even high quality motionblur
30503 motionBlurFadeEffect = false;
30504 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30505
30506
30507 if (r.mbFrames > r.minMbLowQualFrames) {
30508 //r.fullQualityMb = false;
30509 r.motionBlurPxRatio = r.mbPxRBlurry;
30510 }
30511 }
30512
30513 if (r.clearingMotionBlur) {
30514 r.motionBlurPxRatio = 1;
30515 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30516 // because a rogue async texture frame would clear needDraw
30517
30518
30519 if (r.textureDrawLastFrame && !textureDraw) {
30520 needDraw[r.NODE] = true;
30521 needDraw[r.SELECT_BOX] = true;
30522 }
30523
30524 var style = cy.style();
30525 var zoom = cy.zoom();
30526 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30527 var pan = cy.pan();
30528 var effectivePan = {
30529 x: pan.x,
30530 y: pan.y
30531 };
30532 var vp = {
30533 zoom: zoom,
30534 pan: {
30535 x: pan.x,
30536 y: pan.y
30537 }
30538 };
30539 var prevVp = r.prevViewport;
30540 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)
30541
30542 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30543 r.motionBlurPxRatio = 1;
30544 }
30545
30546 if (forcedPan) {
30547 effectivePan = forcedPan;
30548 } // apply pixel ratio
30549
30550
30551 effectiveZoom *= pixelRatio;
30552 effectivePan.x *= pixelRatio;
30553 effectivePan.y *= pixelRatio;
30554 var eles = r.getCachedZSortedEles();
30555
30556 function mbclear(context, x, y, w, h) {
30557 var gco = context.globalCompositeOperation;
30558 context.globalCompositeOperation = 'destination-out';
30559 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30560 context.fillRect(x, y, w, h);
30561 context.globalCompositeOperation = gco;
30562 }
30563
30564 function setContextTransform(context, clear) {
30565 var ePan, eZoom, w, h;
30566
30567 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30568 ePan = {
30569 x: pan.x * mbPxRatio,
30570 y: pan.y * mbPxRatio
30571 };
30572 eZoom = zoom * mbPxRatio;
30573 w = r.canvasWidth * mbPxRatio;
30574 h = r.canvasHeight * mbPxRatio;
30575 } else {
30576 ePan = effectivePan;
30577 eZoom = effectiveZoom;
30578 w = r.canvasWidth;
30579 h = r.canvasHeight;
30580 }
30581
30582 context.setTransform(1, 0, 0, 1, 0, 0);
30583
30584 if (clear === 'motionBlur') {
30585 mbclear(context, 0, 0, w, h);
30586 } else if (!forcedContext && (clear === undefined || clear)) {
30587 context.clearRect(0, 0, w, h);
30588 }
30589
30590 if (!drawAllLayers) {
30591 context.translate(ePan.x, ePan.y);
30592 context.scale(eZoom, eZoom);
30593 }
30594
30595 if (forcedPan) {
30596 context.translate(forcedPan.x, forcedPan.y);
30597 }
30598
30599 if (forcedZoom) {
30600 context.scale(forcedZoom, forcedZoom);
30601 }
30602 }
30603
30604 if (!textureDraw) {
30605 r.textureDrawLastFrame = false;
30606 }
30607
30608 if (textureDraw) {
30609 r.textureDrawLastFrame = true;
30610
30611 if (!r.textureCache) {
30612 r.textureCache = {};
30613 r.textureCache.bb = cy.mutableElements().boundingBox();
30614 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30615 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30616 cxt.setTransform(1, 0, 0, 1, 0, 0);
30617 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30618 r.render({
30619 forcedContext: cxt,
30620 drawOnlyNodeLayer: true,
30621 forcedPxRatio: pixelRatio * r.textureMult
30622 });
30623 var vp = r.textureCache.viewport = {
30624 zoom: cy.zoom(),
30625 pan: cy.pan(),
30626 width: r.canvasWidth,
30627 height: r.canvasHeight
30628 };
30629 vp.mpan = {
30630 x: (0 - vp.pan.x) / vp.zoom,
30631 y: (0 - vp.pan.y) / vp.zoom
30632 };
30633 }
30634
30635 needDraw[r.DRAG] = false;
30636 needDraw[r.NODE] = false;
30637 var context = data.contexts[r.NODE];
30638 var texture = r.textureCache.texture;
30639 var vp = r.textureCache.viewport;
30640 context.setTransform(1, 0, 0, 1, 0, 0);
30641
30642 if (motionBlur) {
30643 mbclear(context, 0, 0, vp.width, vp.height);
30644 } else {
30645 context.clearRect(0, 0, vp.width, vp.height);
30646 }
30647
30648 var outsideBgColor = style.core('outside-texture-bg-color').value;
30649 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30650 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30651 context.fillRect(0, 0, vp.width, vp.height);
30652 var zoom = cy.zoom();
30653 setContextTransform(context, false);
30654 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30655 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30656 } else if (r.textureOnViewport && !forcedContext) {
30657 // clear the cache since we don't need it
30658 r.textureCache = null;
30659 }
30660
30661 var extent = cy.extent();
30662 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30663 var hideEdges = r.hideEdgesOnViewport && vpManip;
30664 var needMbClear = [];
30665 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30666
30667 if (needMbClear[r.NODE]) {
30668 r.clearedForMotionBlur[r.NODE] = true;
30669 }
30670
30671 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30672
30673 if (needMbClear[r.DRAG]) {
30674 r.clearedForMotionBlur[r.DRAG] = true;
30675 }
30676
30677 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30678 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30679 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30680 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30681 setContextTransform(context, clear);
30682
30683 if (hideEdges) {
30684 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30685 } else {
30686 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30687 }
30688
30689 if (r.debug) {
30690 r.drawDebugPoints(context, eles.nondrag);
30691 }
30692
30693 if (!drawAllLayers && !motionBlur) {
30694 needDraw[r.NODE] = false;
30695 }
30696 }
30697
30698 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30699 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30700 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30701 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30702
30703 if (hideEdges) {
30704 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30705 } else {
30706 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30707 }
30708
30709 if (r.debug) {
30710 r.drawDebugPoints(context, eles.drag);
30711 }
30712
30713 if (!drawAllLayers && !motionBlur) {
30714 needDraw[r.DRAG] = false;
30715 }
30716 }
30717
30718 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30719 var context = forcedContext || data.contexts[r.SELECT_BOX];
30720 setContextTransform(context);
30721
30722 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30723 var zoom = r.cy.zoom();
30724 var borderWidth = style.core('selection-box-border-width').value / zoom;
30725 context.lineWidth = borderWidth;
30726 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 + ')';
30727 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30728
30729 if (borderWidth > 0) {
30730 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 + ')';
30731 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30732 }
30733 }
30734
30735 if (data.bgActivePosistion && !r.hoverData.selecting) {
30736 var zoom = r.cy.zoom();
30737 var pos = data.bgActivePosistion;
30738 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 + ')';
30739 context.beginPath();
30740 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30741 context.fill();
30742 }
30743
30744 var timeToRender = r.lastRedrawTime;
30745
30746 if (r.showFps && timeToRender) {
30747 timeToRender = Math.round(timeToRender);
30748 var fps = Math.round(1000 / timeToRender);
30749 context.setTransform(1, 0, 0, 1, 0, 0);
30750 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30751 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30752 context.lineWidth = 1;
30753 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30754 var maxFps = 60;
30755 context.strokeRect(0, 30, 250, 20);
30756 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30757 }
30758
30759 if (!drawAllLayers) {
30760 needDraw[r.SELECT_BOX] = false;
30761 }
30762 } // motionblur: blit rendered blurry frames
30763
30764
30765 if (motionBlur && mbPxRatio !== 1) {
30766 var cxtNode = data.contexts[r.NODE];
30767 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30768 var cxtDrag = data.contexts[r.DRAG];
30769 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30770
30771 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30772 cxt.setTransform(1, 0, 0, 1, 0, 0);
30773
30774 if (needClear || !motionBlurFadeEffect) {
30775 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30776 } else {
30777 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30778 }
30779
30780 var pxr = mbPxRatio;
30781 cxt.drawImage(txt, // img
30782 0, 0, // sx, sy
30783 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30784 0, 0, // x, y
30785 r.canvasWidth, r.canvasHeight // w, h
30786 );
30787 };
30788
30789 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30790 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30791 needDraw[r.NODE] = false;
30792 }
30793
30794 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30795 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30796 needDraw[r.DRAG] = false;
30797 }
30798 }
30799
30800 r.prevViewport = vp;
30801
30802 if (r.clearingMotionBlur) {
30803 r.clearingMotionBlur = false;
30804 r.motionBlurCleared = true;
30805 r.motionBlur = true;
30806 }
30807
30808 if (motionBlur) {
30809 r.motionBlurTimeout = setTimeout(function () {
30810 r.motionBlurTimeout = null;
30811 r.clearedForMotionBlur[r.NODE] = false;
30812 r.clearedForMotionBlur[r.DRAG] = false;
30813 r.motionBlur = false;
30814 r.clearingMotionBlur = !textureDraw;
30815 r.mbFrames = 0;
30816 needDraw[r.NODE] = true;
30817 needDraw[r.DRAG] = true;
30818 r.redraw();
30819 }, motionBlurDelay);
30820 }
30821
30822 if (!forcedContext) {
30823 cy.emit('render');
30824 }
30825};
30826
30827var CRp$3 = {}; // @O Polygon drawing
30828
30829CRp$3.drawPolygonPath = function (context, x, y, width, height, points) {
30830 var halfW = width / 2;
30831 var halfH = height / 2;
30832
30833 if (context.beginPath) {
30834 context.beginPath();
30835 }
30836
30837 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30838
30839 for (var i = 1; i < points.length / 2; i++) {
30840 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30841 }
30842
30843 context.closePath();
30844};
30845
30846CRp$3.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30847 var halfW = width / 2;
30848 var halfH = height / 2;
30849 var cornerRadius = getRoundPolygonRadius(width, height);
30850
30851 if (context.beginPath) {
30852 context.beginPath();
30853 }
30854
30855 for (var _i = 0; _i < points.length / 4; _i++) {
30856 var sourceUv = void 0,
30857 destUv = void 0;
30858
30859 if (_i === 0) {
30860 sourceUv = points.length - 2;
30861 } else {
30862 sourceUv = _i * 4 - 2;
30863 }
30864
30865 destUv = _i * 4 + 2;
30866 var px = x + halfW * points[_i * 4];
30867 var py = y + halfH * points[_i * 4 + 1];
30868 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30869 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30870 var cp0x = px - offset * points[sourceUv];
30871 var cp0y = py - offset * points[sourceUv + 1];
30872 var cp1x = px + offset * points[destUv];
30873 var cp1y = py + offset * points[destUv + 1];
30874
30875 if (_i === 0) {
30876 context.moveTo(cp0x, cp0y);
30877 } else {
30878 context.lineTo(cp0x, cp0y);
30879 }
30880
30881 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30882 }
30883
30884 context.closePath();
30885}; // Round rectangle drawing
30886
30887
30888CRp$3.drawRoundRectanglePath = function (context, x, y, width, height) {
30889 var halfWidth = width / 2;
30890 var halfHeight = height / 2;
30891 var cornerRadius = getRoundRectangleRadius(width, height);
30892
30893 if (context.beginPath) {
30894 context.beginPath();
30895 } // Start at top middle
30896
30897
30898 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30899
30900 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30901
30902 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30903
30904 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30905
30906 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30907
30908 context.lineTo(x, y - halfHeight);
30909 context.closePath();
30910};
30911
30912CRp$3.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30913 var halfWidth = width / 2;
30914 var halfHeight = height / 2;
30915 var cornerRadius = getRoundRectangleRadius(width, height);
30916
30917 if (context.beginPath) {
30918 context.beginPath();
30919 } // Start at top middle
30920
30921
30922 context.moveTo(x, y - halfHeight);
30923 context.lineTo(x + halfWidth, y - halfHeight);
30924 context.lineTo(x + halfWidth, y);
30925 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30926 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30927 context.lineTo(x - halfWidth, y - halfHeight);
30928 context.lineTo(x, y - halfHeight);
30929 context.closePath();
30930};
30931
30932CRp$3.drawCutRectanglePath = function (context, x, y, width, height) {
30933 var halfWidth = width / 2;
30934 var halfHeight = height / 2;
30935 var cornerLength = getCutRectangleCornerLength();
30936
30937 if (context.beginPath) {
30938 context.beginPath();
30939 }
30940
30941 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30942 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30943 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30944 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30945 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30946 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30947 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30948 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30949 context.closePath();
30950};
30951
30952CRp$3.drawBarrelPath = function (context, x, y, width, height) {
30953 var halfWidth = width / 2;
30954 var halfHeight = height / 2;
30955 var xBegin = x - halfWidth;
30956 var xEnd = x + halfWidth;
30957 var yBegin = y - halfHeight;
30958 var yEnd = y + halfHeight;
30959 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30960 var wOffset = barrelCurveConstants.widthOffset;
30961 var hOffset = barrelCurveConstants.heightOffset;
30962 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30963
30964 if (context.beginPath) {
30965 context.beginPath();
30966 }
30967
30968 context.moveTo(xBegin, yBegin + hOffset);
30969 context.lineTo(xBegin, yEnd - hOffset);
30970 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30971 context.lineTo(xEnd - wOffset, yEnd);
30972 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30973 context.lineTo(xEnd, yBegin + hOffset);
30974 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30975 context.lineTo(xBegin + wOffset, yBegin);
30976 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30977 context.closePath();
30978};
30979
30980var sin0 = Math.sin(0);
30981var cos0 = Math.cos(0);
30982var sin = {};
30983var cos = {};
30984var ellipseStepSize = Math.PI / 40;
30985
30986for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30987 sin[i] = Math.sin(i);
30988 cos[i] = Math.cos(i);
30989}
30990
30991CRp$3.drawEllipsePath = function (context, centerX, centerY, width, height) {
30992 if (context.beginPath) {
30993 context.beginPath();
30994 }
30995
30996 if (context.ellipse) {
30997 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30998 } else {
30999 var xPos, yPos;
31000 var rw = width / 2;
31001 var rh = height / 2;
31002
31003 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31004 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
31005 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
31006
31007 if (i === 0) {
31008 context.moveTo(xPos, yPos);
31009 } else {
31010 context.lineTo(xPos, yPos);
31011 }
31012 }
31013 }
31014
31015 context.closePath();
31016};
31017
31018/* global atob, ArrayBuffer, Uint8Array, Blob */
31019var CRp$2 = {};
31020
31021CRp$2.createBuffer = function (w, h) {
31022 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
31023
31024 buffer.width = w;
31025 buffer.height = h;
31026 return [buffer, buffer.getContext('2d')];
31027};
31028
31029CRp$2.bufferCanvasImage = function (options) {
31030 var cy = this.cy;
31031 var eles = cy.mutableElements();
31032 var bb = eles.boundingBox();
31033 var ctrRect = this.findContainerClientCoords();
31034 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
31035 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
31036 var specdMaxDims = number$1(options.maxWidth) || number$1(options.maxHeight);
31037 var pxRatio = this.getPixelRatio();
31038 var scale = 1;
31039
31040 if (options.scale !== undefined) {
31041 width *= options.scale;
31042 height *= options.scale;
31043 scale = options.scale;
31044 } else if (specdMaxDims) {
31045 var maxScaleW = Infinity;
31046 var maxScaleH = Infinity;
31047
31048 if (number$1(options.maxWidth)) {
31049 maxScaleW = scale * options.maxWidth / width;
31050 }
31051
31052 if (number$1(options.maxHeight)) {
31053 maxScaleH = scale * options.maxHeight / height;
31054 }
31055
31056 scale = Math.min(maxScaleW, maxScaleH);
31057 width *= scale;
31058 height *= scale;
31059 }
31060
31061 if (!specdMaxDims) {
31062 width *= pxRatio;
31063 height *= pxRatio;
31064 scale *= pxRatio;
31065 }
31066
31067 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
31068
31069 buffCanvas.width = width;
31070 buffCanvas.height = height;
31071 buffCanvas.style.width = width + 'px';
31072 buffCanvas.style.height = height + 'px';
31073 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
31074
31075 if (width > 0 && height > 0) {
31076 buffCxt.clearRect(0, 0, width, height);
31077 buffCxt.globalCompositeOperation = 'source-over';
31078 var zsortedEles = this.getCachedZSortedEles();
31079
31080 if (options.full) {
31081 // draw the full bounds of the graph
31082 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
31083 buffCxt.scale(scale, scale);
31084 this.drawElements(buffCxt, zsortedEles);
31085 buffCxt.scale(1 / scale, 1 / scale);
31086 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
31087 } else {
31088 // draw the current view
31089 var pan = cy.pan();
31090 var translation = {
31091 x: pan.x * scale,
31092 y: pan.y * scale
31093 };
31094 scale *= cy.zoom();
31095 buffCxt.translate(translation.x, translation.y);
31096 buffCxt.scale(scale, scale);
31097 this.drawElements(buffCxt, zsortedEles);
31098 buffCxt.scale(1 / scale, 1 / scale);
31099 buffCxt.translate(-translation.x, -translation.y);
31100 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
31101
31102
31103 if (options.bg) {
31104 buffCxt.globalCompositeOperation = 'destination-over';
31105 buffCxt.fillStyle = options.bg;
31106 buffCxt.rect(0, 0, width, height);
31107 buffCxt.fill();
31108 }
31109 }
31110
31111 return buffCanvas;
31112};
31113
31114function b64ToBlob(b64, mimeType) {
31115 var bytes = atob(b64);
31116 var buff = new ArrayBuffer(bytes.length);
31117 var buffUint8 = new Uint8Array(buff);
31118
31119 for (var i = 0; i < bytes.length; i++) {
31120 buffUint8[i] = bytes.charCodeAt(i);
31121 }
31122
31123 return new Blob([buff], {
31124 type: mimeType
31125 });
31126}
31127
31128function b64UriToB64(b64uri) {
31129 var i = b64uri.indexOf(',');
31130 return b64uri.substr(i + 1);
31131}
31132
31133function output(options, canvas, mimeType) {
31134 var getB64Uri = function getB64Uri() {
31135 return canvas.toDataURL(mimeType, options.quality);
31136 };
31137
31138 switch (options.output) {
31139 case 'blob-promise':
31140 return new Promise$1(function (resolve, reject) {
31141 try {
31142 canvas.toBlob(function (blob) {
31143 if (blob != null) {
31144 resolve(blob);
31145 } else {
31146 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
31147 }
31148 }, mimeType, options.quality);
31149 } catch (err) {
31150 reject(err);
31151 }
31152 });
31153
31154 case 'blob':
31155 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
31156
31157 case 'base64':
31158 return b64UriToB64(getB64Uri());
31159
31160 case 'base64uri':
31161 default:
31162 return getB64Uri();
31163 }
31164}
31165
31166CRp$2.png = function (options) {
31167 return output(options, this.bufferCanvasImage(options), 'image/png');
31168};
31169
31170CRp$2.jpg = function (options) {
31171 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
31172};
31173
31174var CRp$1 = {};
31175
31176CRp$1.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
31177 switch (name) {
31178 case 'ellipse':
31179 return this.drawEllipsePath(context, centerX, centerY, width, height);
31180
31181 case 'polygon':
31182 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
31183
31184 case 'round-polygon':
31185 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
31186
31187 case 'roundrectangle':
31188 case 'round-rectangle':
31189 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
31190
31191 case 'cutrectangle':
31192 case 'cut-rectangle':
31193 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
31194
31195 case 'bottomroundrectangle':
31196 case 'bottom-round-rectangle':
31197 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
31198
31199 case 'barrel':
31200 return this.drawBarrelPath(context, centerX, centerY, width, height);
31201 }
31202};
31203
31204var CR = CanvasRenderer;
31205var CRp = CanvasRenderer.prototype;
31206CRp.CANVAS_LAYERS = 3; //
31207
31208CRp.SELECT_BOX = 0;
31209CRp.DRAG = 1;
31210CRp.NODE = 2;
31211CRp.BUFFER_COUNT = 3; //
31212
31213CRp.TEXTURE_BUFFER = 0;
31214CRp.MOTIONBLUR_BUFFER_NODE = 1;
31215CRp.MOTIONBLUR_BUFFER_DRAG = 2;
31216
31217function CanvasRenderer(options) {
31218 var r = this;
31219 r.data = {
31220 canvases: new Array(CRp.CANVAS_LAYERS),
31221 contexts: new Array(CRp.CANVAS_LAYERS),
31222 canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS),
31223 bufferCanvases: new Array(CRp.BUFFER_COUNT),
31224 bufferContexts: new Array(CRp.CANVAS_LAYERS)
31225 };
31226 var tapHlOffAttr = '-webkit-tap-highlight-color';
31227 var tapHlOffStyle = 'rgba(0,0,0,0)';
31228 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
31229
31230 var containerStyle = r.data.canvasContainer.style;
31231 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
31232 containerStyle.position = 'relative';
31233 containerStyle.zIndex = '0';
31234 containerStyle.overflow = 'hidden';
31235 var container = options.cy.container();
31236 container.appendChild(r.data.canvasContainer);
31237 container.style[tapHlOffAttr] = tapHlOffStyle;
31238 var styleMap = {
31239 '-webkit-user-select': 'none',
31240 '-moz-user-select': '-moz-none',
31241 'user-select': 'none',
31242 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
31243 'outline-style': 'none'
31244 };
31245
31246 if (ms()) {
31247 styleMap['-ms-touch-action'] = 'none';
31248 styleMap['touch-action'] = 'none';
31249 }
31250
31251 for (var i = 0; i < CRp.CANVAS_LAYERS; i++) {
31252 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31253
31254 r.data.contexts[i] = canvas.getContext('2d');
31255 Object.keys(styleMap).forEach(function (k) {
31256 canvas.style[k] = styleMap[k];
31257 });
31258 canvas.style.position = 'absolute';
31259 canvas.setAttribute('data-id', 'layer' + i);
31260 canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i);
31261 r.data.canvasContainer.appendChild(canvas);
31262 r.data.canvasNeedsRedraw[i] = false;
31263 }
31264
31265 r.data.topCanvas = r.data.canvases[0];
31266 r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node');
31267 r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox');
31268 r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag');
31269
31270 for (var i = 0; i < CRp.BUFFER_COUNT; i++) {
31271 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31272
31273 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31274 r.data.bufferCanvases[i].style.position = 'absolute';
31275 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31276 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31277 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31278 }
31279
31280 r.pathsEnabled = true;
31281 var emptyBb = makeBoundingBox();
31282
31283 var getBoxCenter = function getBoxCenter(bb) {
31284 return {
31285 x: (bb.x1 + bb.x2) / 2,
31286 y: (bb.y1 + bb.y2) / 2
31287 };
31288 };
31289
31290 var getCenterOffset = function getCenterOffset(bb) {
31291 return {
31292 x: -bb.w / 2,
31293 y: -bb.h / 2
31294 };
31295 };
31296
31297 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31298 var _p = ele[0]._private;
31299 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31300 return !same;
31301 };
31302
31303 var getStyleKey = function getStyleKey(ele) {
31304 return ele[0]._private.nodeKey;
31305 };
31306
31307 var getLabelKey = function getLabelKey(ele) {
31308 return ele[0]._private.labelStyleKey;
31309 };
31310
31311 var getSourceLabelKey = function getSourceLabelKey(ele) {
31312 return ele[0]._private.sourceLabelStyleKey;
31313 };
31314
31315 var getTargetLabelKey = function getTargetLabelKey(ele) {
31316 return ele[0]._private.targetLabelStyleKey;
31317 };
31318
31319 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31320 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31321 };
31322
31323 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31324 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31325 };
31326
31327 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31328 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31329 };
31330
31331 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31332 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31333 };
31334
31335 var getElementBox = function getElementBox(ele) {
31336 ele.boundingBox();
31337 return ele[0]._private.bodyBounds;
31338 };
31339
31340 var getLabelBox = function getLabelBox(ele) {
31341 ele.boundingBox();
31342 return ele[0]._private.labelBounds.main || emptyBb;
31343 };
31344
31345 var getSourceLabelBox = function getSourceLabelBox(ele) {
31346 ele.boundingBox();
31347 return ele[0]._private.labelBounds.source || emptyBb;
31348 };
31349
31350 var getTargetLabelBox = function getTargetLabelBox(ele) {
31351 ele.boundingBox();
31352 return ele[0]._private.labelBounds.target || emptyBb;
31353 };
31354
31355 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31356 return scaledLabelShown;
31357 };
31358
31359 var getElementRotationPoint = function getElementRotationPoint(ele) {
31360 return getBoxCenter(getElementBox(ele));
31361 };
31362
31363 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31364 var pre = prefix ? prefix + '-' : '';
31365 return {
31366 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31367 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31368 };
31369 };
31370
31371 var getRsPt = function getRsPt(ele, x, y) {
31372 var rs = ele[0]._private.rscratch;
31373 return {
31374 x: rs[x],
31375 y: rs[y]
31376 };
31377 };
31378
31379 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31380 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31381 };
31382
31383 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31384 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31385 };
31386
31387 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31388 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31389 };
31390
31391 var getElementRotationOffset = function getElementRotationOffset(ele) {
31392 return getCenterOffset(getElementBox(ele));
31393 };
31394
31395 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31396 return getCenterOffset(getSourceLabelBox(ele));
31397 };
31398
31399 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31400 return getCenterOffset(getTargetLabelBox(ele));
31401 };
31402
31403 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31404 var bb = getLabelBox(ele);
31405 var p = getCenterOffset(getLabelBox(ele));
31406
31407 if (ele.isNode()) {
31408 switch (ele.pstyle('text-halign').value) {
31409 case 'left':
31410 p.x = -bb.w;
31411 break;
31412
31413 case 'right':
31414 p.x = 0;
31415 break;
31416 }
31417
31418 switch (ele.pstyle('text-valign').value) {
31419 case 'top':
31420 p.y = -bb.h;
31421 break;
31422
31423 case 'bottom':
31424 p.y = 0;
31425 break;
31426 }
31427 }
31428
31429 return p;
31430 };
31431
31432 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31433 getKey: getStyleKey,
31434 doesEleInvalidateKey: backgroundTimestampHasChanged,
31435 drawElement: drawElement,
31436 getBoundingBox: getElementBox,
31437 getRotationPoint: getElementRotationPoint,
31438 getRotationOffset: getElementRotationOffset,
31439 allowEdgeTxrCaching: false,
31440 allowParentTxrCaching: false
31441 });
31442 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31443 getKey: getLabelKey,
31444 drawElement: drawLabel,
31445 getBoundingBox: getLabelBox,
31446 getRotationPoint: getLabelRotationPoint,
31447 getRotationOffset: getLabelRotationOffset,
31448 isVisible: isLabelVisibleAtScale
31449 });
31450 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31451 getKey: getSourceLabelKey,
31452 drawElement: drawSourceLabel,
31453 getBoundingBox: getSourceLabelBox,
31454 getRotationPoint: getSourceLabelRotationPoint,
31455 getRotationOffset: getSourceLabelRotationOffset,
31456 isVisible: isLabelVisibleAtScale
31457 });
31458 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31459 getKey: getTargetLabelKey,
31460 drawElement: drawTargetLabel,
31461 getBoundingBox: getTargetLabelBox,
31462 getRotationPoint: getTargetLabelRotationPoint,
31463 getRotationOffset: getTargetLabelRotationOffset,
31464 isVisible: isLabelVisibleAtScale
31465 });
31466 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31467 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31468 // each cache should check for sub-key diff to see that the update affects that cache particularly
31469 eleTxrCache.invalidateElements(eles);
31470 lblTxrCache.invalidateElements(eles);
31471 slbTxrCache.invalidateElements(eles);
31472 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31473
31474 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31475
31476 for (var _i = 0; _i < eles.length; _i++) {
31477 var _p = eles[_i]._private;
31478 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31479 }
31480 });
31481
31482 var refineInLayers = function refineInLayers(reqs) {
31483 for (var i = 0; i < reqs.length; i++) {
31484 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31485 }
31486 };
31487
31488 eleTxrCache.onDequeue(refineInLayers);
31489 lblTxrCache.onDequeue(refineInLayers);
31490 slbTxrCache.onDequeue(refineInLayers);
31491 tlbTxrCache.onDequeue(refineInLayers);
31492}
31493
31494CRp.redrawHint = function (group, bool) {
31495 var r = this;
31496
31497 switch (group) {
31498 case 'eles':
31499 r.data.canvasNeedsRedraw[CRp.NODE] = bool;
31500 break;
31501
31502 case 'drag':
31503 r.data.canvasNeedsRedraw[CRp.DRAG] = bool;
31504 break;
31505
31506 case 'select':
31507 r.data.canvasNeedsRedraw[CRp.SELECT_BOX] = bool;
31508 break;
31509 }
31510}; // whether to use Path2D caching for drawing
31511
31512
31513var pathsImpld = typeof Path2D !== 'undefined';
31514
31515CRp.path2dEnabled = function (on) {
31516 if (on === undefined) {
31517 return this.pathsEnabled;
31518 }
31519
31520 this.pathsEnabled = on ? true : false;
31521};
31522
31523CRp.usePaths = function () {
31524 return pathsImpld && this.pathsEnabled;
31525};
31526
31527CRp.setImgSmoothing = function (context, bool) {
31528 if (context.imageSmoothingEnabled != null) {
31529 context.imageSmoothingEnabled = bool;
31530 } else {
31531 context.webkitImageSmoothingEnabled = bool;
31532 context.mozImageSmoothingEnabled = bool;
31533 context.msImageSmoothingEnabled = bool;
31534 }
31535};
31536
31537CRp.getImgSmoothing = function (context) {
31538 if (context.imageSmoothingEnabled != null) {
31539 return context.imageSmoothingEnabled;
31540 } else {
31541 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31542 }
31543};
31544
31545CRp.makeOffscreenCanvas = function (width, height) {
31546 var canvas;
31547
31548 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ("undefined" )) {
31549 canvas = new OffscreenCanvas(width, height);
31550 } else {
31551 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31552
31553 canvas.width = width;
31554 canvas.height = height;
31555 }
31556
31557 return canvas;
31558};
31559
31560[CRp$a, CRp$9, CRp$8, CRp$7, CRp$6, CRp$5, CRp$4, CRp$3, CRp$2, CRp$1].forEach(function (props) {
31561 extend(CRp, props);
31562});
31563
31564var renderer = [{
31565 name: 'null',
31566 impl: NullRenderer
31567}, {
31568 name: 'base',
31569 impl: BR
31570}, {
31571 name: 'canvas',
31572 impl: CR
31573}];
31574
31575var incExts = [{
31576 type: 'layout',
31577 extensions: layout
31578}, {
31579 type: 'renderer',
31580 extensions: renderer
31581}];
31582
31583var extensions = {}; // registered modules for extensions, indexed by name
31584
31585var modules = {};
31586
31587function setExtension(type, name, registrant) {
31588 var ext = registrant;
31589
31590 var overrideErr = function overrideErr(field) {
31591 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31592 };
31593
31594 if (type === 'core') {
31595 if (Core.prototype[name]) {
31596 return overrideErr(name);
31597 } else {
31598 Core.prototype[name] = registrant;
31599 }
31600 } else if (type === 'collection') {
31601 if (Collection.prototype[name]) {
31602 return overrideErr(name);
31603 } else {
31604 Collection.prototype[name] = registrant;
31605 }
31606 } else if (type === 'layout') {
31607 // fill in missing layout functions in the prototype
31608 var Layout = function Layout(options) {
31609 this.options = options;
31610 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31611
31612 if (!plainObject(this._private)) {
31613 this._private = {};
31614 }
31615
31616 this._private.cy = options.cy;
31617 this._private.listeners = [];
31618 this.createEmitter();
31619 };
31620
31621 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31622 var optLayoutFns = [];
31623
31624 for (var i = 0; i < optLayoutFns.length; i++) {
31625 var fnName = optLayoutFns[i];
31626
31627 layoutProto[fnName] = layoutProto[fnName] || function () {
31628 return this;
31629 };
31630 } // either .start() or .run() is defined, so autogen the other
31631
31632
31633 if (layoutProto.start && !layoutProto.run) {
31634 layoutProto.run = function () {
31635 this.start();
31636 return this;
31637 };
31638 } else if (!layoutProto.start && layoutProto.run) {
31639 layoutProto.start = function () {
31640 this.run();
31641 return this;
31642 };
31643 }
31644
31645 var regStop = registrant.prototype.stop;
31646
31647 layoutProto.stop = function () {
31648 var opts = this.options;
31649
31650 if (opts && opts.animate) {
31651 var anis = this.animations;
31652
31653 if (anis) {
31654 for (var _i = 0; _i < anis.length; _i++) {
31655 anis[_i].stop();
31656 }
31657 }
31658 }
31659
31660 if (regStop) {
31661 regStop.call(this);
31662 } else {
31663 this.emit('layoutstop');
31664 }
31665
31666 return this;
31667 };
31668
31669 if (!layoutProto.destroy) {
31670 layoutProto.destroy = function () {
31671 return this;
31672 };
31673 }
31674
31675 layoutProto.cy = function () {
31676 return this._private.cy;
31677 };
31678
31679 var getCy = function getCy(layout) {
31680 return layout._private.cy;
31681 };
31682
31683 var emitterOpts = {
31684 addEventFields: function addEventFields(layout, evt) {
31685 evt.layout = layout;
31686 evt.cy = getCy(layout);
31687 evt.target = layout;
31688 },
31689 bubble: function bubble() {
31690 return true;
31691 },
31692 parent: function parent(layout) {
31693 return getCy(layout);
31694 }
31695 };
31696 extend(layoutProto, {
31697 createEmitter: function createEmitter() {
31698 this._private.emitter = new Emitter(emitterOpts, this);
31699 return this;
31700 },
31701 emitter: function emitter() {
31702 return this._private.emitter;
31703 },
31704 on: function on(evt, cb) {
31705 this.emitter().on(evt, cb);
31706 return this;
31707 },
31708 one: function one(evt, cb) {
31709 this.emitter().one(evt, cb);
31710 return this;
31711 },
31712 once: function once(evt, cb) {
31713 this.emitter().one(evt, cb);
31714 return this;
31715 },
31716 removeListener: function removeListener(evt, cb) {
31717 this.emitter().removeListener(evt, cb);
31718 return this;
31719 },
31720 removeAllListeners: function removeAllListeners() {
31721 this.emitter().removeAllListeners();
31722 return this;
31723 },
31724 emit: function emit(evt, params) {
31725 this.emitter().emit(evt, params);
31726 return this;
31727 }
31728 });
31729 define.eventAliasesOn(layoutProto);
31730 ext = Layout; // replace with our wrapped layout
31731 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31732 // user registered renderers inherit from base
31733 var BaseRenderer = getExtension('renderer', 'base');
31734 var bProto = BaseRenderer.prototype;
31735 var RegistrantRenderer = registrant;
31736 var rProto = registrant.prototype;
31737
31738 var Renderer = function Renderer() {
31739 BaseRenderer.apply(this, arguments);
31740 RegistrantRenderer.apply(this, arguments);
31741 };
31742
31743 var proto = Renderer.prototype;
31744
31745 for (var pName in bProto) {
31746 var pVal = bProto[pName];
31747 var existsInR = rProto[pName] != null;
31748
31749 if (existsInR) {
31750 return overrideErr(pName);
31751 }
31752
31753 proto[pName] = pVal; // take impl from base
31754 }
31755
31756 for (var _pName in rProto) {
31757 proto[_pName] = rProto[_pName]; // take impl from registrant
31758 }
31759
31760 bProto.clientFunctions.forEach(function (name) {
31761 proto[name] = proto[name] || function () {
31762 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31763 };
31764 });
31765 ext = Renderer;
31766 } else if (type === '__proto__' || type === 'constructor' || type === 'prototype') {
31767 // to avoid potential prototype pollution
31768 return error(type + ' is an illegal type to be registered, possibly lead to prototype pollutions');
31769 }
31770
31771 return setMap({
31772 map: extensions,
31773 keys: [type, name],
31774 value: ext
31775 });
31776}
31777
31778function getExtension(type, name) {
31779 return getMap({
31780 map: extensions,
31781 keys: [type, name]
31782 });
31783}
31784
31785function setModule(type, name, moduleType, moduleName, registrant) {
31786 return setMap({
31787 map: modules,
31788 keys: [type, name, moduleType, moduleName],
31789 value: registrant
31790 });
31791}
31792
31793function getModule(type, name, moduleType, moduleName) {
31794 return getMap({
31795 map: modules,
31796 keys: [type, name, moduleType, moduleName]
31797 });
31798}
31799
31800var extension = function extension() {
31801 // e.g. extension('renderer', 'svg')
31802 if (arguments.length === 2) {
31803 return getExtension.apply(null, arguments);
31804 } // e.g. extension('renderer', 'svg', { ... })
31805 else if (arguments.length === 3) {
31806 return setExtension.apply(null, arguments);
31807 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31808 else if (arguments.length === 4) {
31809 return getModule.apply(null, arguments);
31810 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31811 else if (arguments.length === 5) {
31812 return setModule.apply(null, arguments);
31813 } else {
31814 error('Invalid extension access syntax');
31815 }
31816}; // allows a core instance to access extensions internally
31817
31818
31819Core.prototype.extension = extension; // included extensions
31820
31821incExts.forEach(function (group) {
31822 group.extensions.forEach(function (ext) {
31823 setExtension(group.type, ext.name, ext.impl);
31824 });
31825});
31826
31827// (useful for init)
31828
31829var Stylesheet = function Stylesheet() {
31830 if (!(this instanceof Stylesheet)) {
31831 return new Stylesheet();
31832 }
31833
31834 this.length = 0;
31835};
31836
31837var sheetfn = Stylesheet.prototype;
31838
31839sheetfn.instanceString = function () {
31840 return 'stylesheet';
31841}; // just store the selector to be parsed later
31842
31843
31844sheetfn.selector = function (selector) {
31845 var i = this.length++;
31846 this[i] = {
31847 selector: selector,
31848 properties: []
31849 };
31850 return this; // chaining
31851}; // just store the property to be parsed later
31852
31853
31854sheetfn.css = function (name, value) {
31855 var i = this.length - 1;
31856
31857 if (string(name)) {
31858 this[i].properties.push({
31859 name: name,
31860 value: value
31861 });
31862 } else if (plainObject(name)) {
31863 var map = name;
31864 var propNames = Object.keys(map);
31865
31866 for (var j = 0; j < propNames.length; j++) {
31867 var key = propNames[j];
31868 var mapVal = map[key];
31869
31870 if (mapVal == null) {
31871 continue;
31872 }
31873
31874 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31875
31876 if (prop == null) {
31877 continue;
31878 }
31879
31880 var _name = prop.name;
31881 var _value = mapVal;
31882 this[i].properties.push({
31883 name: _name,
31884 value: _value
31885 });
31886 }
31887 }
31888
31889 return this; // chaining
31890};
31891
31892sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31893
31894sheetfn.generateStyle = function (cy) {
31895 var style = new Style(cy);
31896 return this.appendToStyle(style);
31897}; // append a dummy stylesheet object on a real style object
31898
31899
31900sheetfn.appendToStyle = function (style) {
31901 for (var i = 0; i < this.length; i++) {
31902 var context = this[i];
31903 var selector = context.selector;
31904 var props = context.properties;
31905 style.selector(selector); // apply selector
31906
31907 for (var j = 0; j < props.length; j++) {
31908 var prop = props[j];
31909 style.css(prop.name, prop.value); // apply property
31910 }
31911 }
31912
31913 return style;
31914};
31915
31916var version = "3.23.0";
31917
31918var cytoscape = function cytoscape(options) {
31919 // if no options specified, use default
31920 if (options === undefined) {
31921 options = {};
31922 } // create instance
31923
31924
31925 if (plainObject(options)) {
31926 return new Core(options);
31927 } // allow for registration of extensions
31928 else if (string(options)) {
31929 return extension.apply(extension, arguments);
31930 }
31931}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31932
31933
31934cytoscape.use = function (ext) {
31935 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31936
31937 args.unshift(cytoscape); // cytoscape is first arg to ext
31938
31939 ext.apply(null, args);
31940 return this;
31941};
31942
31943cytoscape.warnings = function (bool) {
31944 return warnings(bool);
31945}; // replaced by build system
31946
31947
31948cytoscape.version = version; // expose public apis (mostly for extensions)
31949
31950cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31951
31952module.exports = cytoscape;