UNPKG

981 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(function (global, factory) {
24 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
25 typeof define === 'function' && define.amd ? define(factory) :
26 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.cytoscape = factory());
27})(this, (function () { 'use strict';
28
29 function _typeof(obj) {
30 "@babel/helpers - typeof";
31
32 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
33 return typeof obj;
34 } : function (obj) {
35 return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
36 }, _typeof(obj);
37 }
38
39 function _classCallCheck(instance, Constructor) {
40 if (!(instance instanceof Constructor)) {
41 throw new TypeError("Cannot call a class as a function");
42 }
43 }
44
45 function _defineProperties(target, props) {
46 for (var i = 0; i < props.length; i++) {
47 var descriptor = props[i];
48 descriptor.enumerable = descriptor.enumerable || false;
49 descriptor.configurable = true;
50 if ("value" in descriptor) descriptor.writable = true;
51 Object.defineProperty(target, descriptor.key, descriptor);
52 }
53 }
54
55 function _createClass(Constructor, protoProps, staticProps) {
56 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
57 if (staticProps) _defineProperties(Constructor, staticProps);
58 Object.defineProperty(Constructor, "prototype", {
59 writable: false
60 });
61 return Constructor;
62 }
63
64 function _defineProperty$1(obj, key, value) {
65 if (key in obj) {
66 Object.defineProperty(obj, key, {
67 value: value,
68 enumerable: true,
69 configurable: true,
70 writable: true
71 });
72 } else {
73 obj[key] = value;
74 }
75
76 return obj;
77 }
78
79 function _slicedToArray(arr, i) {
80 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
81 }
82
83 function _arrayWithHoles(arr) {
84 if (Array.isArray(arr)) return arr;
85 }
86
87 function _iterableToArrayLimit(arr, i) {
88 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
89
90 if (_i == null) return;
91 var _arr = [];
92 var _n = true;
93 var _d = false;
94
95 var _s, _e;
96
97 try {
98 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
99 _arr.push(_s.value);
100
101 if (i && _arr.length === i) break;
102 }
103 } catch (err) {
104 _d = true;
105 _e = err;
106 } finally {
107 try {
108 if (!_n && _i["return"] != null) _i["return"]();
109 } finally {
110 if (_d) throw _e;
111 }
112 }
113
114 return _arr;
115 }
116
117 function _unsupportedIterableToArray(o, minLen) {
118 if (!o) return;
119 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
120 var n = Object.prototype.toString.call(o).slice(8, -1);
121 if (n === "Object" && o.constructor) n = o.constructor.name;
122 if (n === "Map" || n === "Set") return Array.from(o);
123 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
124 }
125
126 function _arrayLikeToArray(arr, len) {
127 if (len == null || len > arr.length) len = arr.length;
128
129 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
130
131 return arr2;
132 }
133
134 function _nonIterableRest() {
135 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
136 }
137
138 var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
139
140 var navigator = window$1 ? window$1.navigator : null;
141 window$1 ? window$1.document : null;
142
143 var typeofstr = _typeof('');
144
145 var typeofobj = _typeof({});
146
147 var typeoffn = _typeof(function () {});
148
149 var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
150
151 var instanceStr = function instanceStr(obj) {
152 return obj && obj.instanceString && fn$6(obj.instanceString) ? obj.instanceString() : null;
153 };
154
155 var string = function string(obj) {
156 return obj != null && _typeof(obj) == typeofstr;
157 };
158 var fn$6 = function fn(obj) {
159 return obj != null && _typeof(obj) === typeoffn;
160 };
161 var array = function array(obj) {
162 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
163 };
164 var plainObject = function plainObject(obj) {
165 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
166 };
167 var object = function object(obj) {
168 return obj != null && _typeof(obj) === typeofobj;
169 };
170 var number$1 = function number(obj) {
171 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
172 };
173 var integer = function integer(obj) {
174 return number$1(obj) && Math.floor(obj) === obj;
175 };
176 var htmlElement = function htmlElement(obj) {
177 if ('undefined' === typeofhtmlele) {
178 return undefined;
179 } else {
180 return null != obj && obj instanceof HTMLElement;
181 }
182 };
183 var elementOrCollection = function elementOrCollection(obj) {
184 return element(obj) || collection(obj);
185 };
186 var element = function element(obj) {
187 return instanceStr(obj) === 'collection' && obj._private.single;
188 };
189 var collection = function collection(obj) {
190 return instanceStr(obj) === 'collection' && !obj._private.single;
191 };
192 var core = function core(obj) {
193 return instanceStr(obj) === 'core';
194 };
195 var stylesheet = function stylesheet(obj) {
196 return instanceStr(obj) === 'stylesheet';
197 };
198 var event = function event(obj) {
199 return instanceStr(obj) === 'event';
200 };
201 var emptyString = function emptyString(obj) {
202 if (obj === undefined || obj === null) {
203 // null is empty
204 return true;
205 } else if (obj === '' || obj.match(/^\s+$/)) {
206 return true; // empty string is empty
207 }
208
209 return false; // otherwise, we don't know what we've got
210 };
211 var domElement = function domElement(obj) {
212 if (typeof HTMLElement === 'undefined') {
213 return false; // we're not in a browser so it doesn't matter
214 } else {
215 return obj instanceof HTMLElement;
216 }
217 };
218 var boundingBox = function boundingBox(obj) {
219 return plainObject(obj) && number$1(obj.x1) && number$1(obj.x2) && number$1(obj.y1) && number$1(obj.y2);
220 };
221 var promise = function promise(obj) {
222 return object(obj) && fn$6(obj.then);
223 };
224 var ms = function ms() {
225 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
226 }; // probably a better way to detect this...
227
228 var memoize$1 = function memoize(fn, keyFn) {
229 if (!keyFn) {
230 keyFn = function keyFn() {
231 if (arguments.length === 1) {
232 return arguments[0];
233 } else if (arguments.length === 0) {
234 return 'undefined';
235 }
236
237 var args = [];
238
239 for (var i = 0; i < arguments.length; i++) {
240 args.push(arguments[i]);
241 }
242
243 return args.join('$');
244 };
245 }
246
247 var memoizedFn = function memoizedFn() {
248 var self = this;
249 var args = arguments;
250 var ret;
251 var k = keyFn.apply(self, args);
252 var cache = memoizedFn.cache;
253
254 if (!(ret = cache[k])) {
255 ret = cache[k] = fn.apply(self, args);
256 }
257
258 return ret;
259 };
260
261 memoizedFn.cache = {};
262 return memoizedFn;
263 };
264
265 var camel2dash = memoize$1(function (str) {
266 return str.replace(/([A-Z])/g, function (v) {
267 return '-' + v.toLowerCase();
268 });
269 });
270 var dash2camel = memoize$1(function (str) {
271 return str.replace(/(-\w)/g, function (v) {
272 return v[1].toUpperCase();
273 });
274 });
275 var prependCamel = memoize$1(function (prefix, str) {
276 return prefix + str[0].toUpperCase() + str.substring(1);
277 }, function (prefix, str) {
278 return prefix + '$' + str;
279 });
280 var capitalize = function capitalize(str) {
281 if (emptyString(str)) {
282 return str;
283 }
284
285 return str.charAt(0).toUpperCase() + str.substring(1);
286 };
287
288 var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
289 var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)';
290 var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)';
291 var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)';
292 var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)';
293 var hex3 = '\\#[0-9a-fA-F]{3}';
294 var hex6 = '\\#[0-9a-fA-F]{6}';
295
296 var ascending = function ascending(a, b) {
297 if (a < b) {
298 return -1;
299 } else if (a > b) {
300 return 1;
301 } else {
302 return 0;
303 }
304 };
305 var descending = function descending(a, b) {
306 return -1 * ascending(a, b);
307 };
308
309 var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
310 var args = arguments;
311
312 for (var i = 1; i < args.length; i++) {
313 var obj = args[i];
314
315 if (obj == null) {
316 continue;
317 }
318
319 var keys = Object.keys(obj);
320
321 for (var j = 0; j < keys.length; j++) {
322 var k = keys[j];
323 tgt[k] = obj[k];
324 }
325 }
326
327 return tgt;
328 };
329
330 var hex2tuple = function hex2tuple(hex) {
331 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
332 return;
333 }
334
335 var shortHex = hex.length === 4;
336 var r, g, b;
337 var base = 16;
338
339 if (shortHex) {
340 r = parseInt(hex[1] + hex[1], base);
341 g = parseInt(hex[2] + hex[2], base);
342 b = parseInt(hex[3] + hex[3], base);
343 } else {
344 r = parseInt(hex[1] + hex[2], base);
345 g = parseInt(hex[3] + hex[4], base);
346 b = parseInt(hex[5] + hex[6], base);
347 }
348
349 return [r, g, b];
350 }; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
351
352 var hsl2tuple = function hsl2tuple(hsl) {
353 var ret;
354 var h, s, l, a, r, g, b;
355
356 function hue2rgb(p, q, t) {
357 if (t < 0) t += 1;
358 if (t > 1) t -= 1;
359 if (t < 1 / 6) return p + (q - p) * 6 * t;
360 if (t < 1 / 2) return q;
361 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
362 return p;
363 }
364
365 var m = new RegExp('^' + hsla + '$').exec(hsl);
366
367 if (m) {
368 // get hue
369 h = parseInt(m[1]);
370
371 if (h < 0) {
372 h = (360 - -1 * h % 360) % 360;
373 } else if (h > 360) {
374 h = h % 360;
375 }
376
377 h /= 360; // normalise on [0, 1]
378
379 s = parseFloat(m[2]);
380
381 if (s < 0 || s > 100) {
382 return;
383 } // saturation is [0, 100]
384
385
386 s = s / 100; // normalise on [0, 1]
387
388 l = parseFloat(m[3]);
389
390 if (l < 0 || l > 100) {
391 return;
392 } // lightness is [0, 100]
393
394
395 l = l / 100; // normalise on [0, 1]
396
397 a = m[4];
398
399 if (a !== undefined) {
400 a = parseFloat(a);
401
402 if (a < 0 || a > 1) {
403 return;
404 } // alpha is [0, 1]
405
406 } // now, convert to rgb
407 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
408
409
410 if (s === 0) {
411 r = g = b = Math.round(l * 255); // achromatic
412 } else {
413 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
414 var p = 2 * l - q;
415 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
416 g = Math.round(255 * hue2rgb(p, q, h));
417 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
418 }
419
420 ret = [r, g, b, a];
421 }
422
423 return ret;
424 }; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
425
426 var rgb2tuple = function rgb2tuple(rgb) {
427 var ret;
428 var m = new RegExp('^' + rgba + '$').exec(rgb);
429
430 if (m) {
431 ret = [];
432 var isPct = [];
433
434 for (var i = 1; i <= 3; i++) {
435 var channel = m[i];
436
437 if (channel[channel.length - 1] === '%') {
438 isPct[i] = true;
439 }
440
441 channel = parseFloat(channel);
442
443 if (isPct[i]) {
444 channel = channel / 100 * 255; // normalise to [0, 255]
445 }
446
447 if (channel < 0 || channel > 255) {
448 return;
449 } // invalid channel value
450
451
452 ret.push(Math.floor(channel));
453 }
454
455 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
456 var allArePct = isPct[1] && isPct[2] && isPct[3];
457
458 if (atLeastOneIsPct && !allArePct) {
459 return;
460 } // must all be percent values if one is
461
462
463 var alpha = m[4];
464
465 if (alpha !== undefined) {
466 alpha = parseFloat(alpha);
467
468 if (alpha < 0 || alpha > 1) {
469 return;
470 } // invalid alpha value
471
472
473 ret.push(alpha);
474 }
475 }
476
477 return ret;
478 };
479 var colorname2tuple = function colorname2tuple(color) {
480 return colors[color.toLowerCase()];
481 };
482 var color2tuple = function color2tuple(color) {
483 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
484 };
485 var colors = {
486 // special colour names
487 transparent: [0, 0, 0, 0],
488 // NB alpha === 0
489 // regular colours
490 aliceblue: [240, 248, 255],
491 antiquewhite: [250, 235, 215],
492 aqua: [0, 255, 255],
493 aquamarine: [127, 255, 212],
494 azure: [240, 255, 255],
495 beige: [245, 245, 220],
496 bisque: [255, 228, 196],
497 black: [0, 0, 0],
498 blanchedalmond: [255, 235, 205],
499 blue: [0, 0, 255],
500 blueviolet: [138, 43, 226],
501 brown: [165, 42, 42],
502 burlywood: [222, 184, 135],
503 cadetblue: [95, 158, 160],
504 chartreuse: [127, 255, 0],
505 chocolate: [210, 105, 30],
506 coral: [255, 127, 80],
507 cornflowerblue: [100, 149, 237],
508 cornsilk: [255, 248, 220],
509 crimson: [220, 20, 60],
510 cyan: [0, 255, 255],
511 darkblue: [0, 0, 139],
512 darkcyan: [0, 139, 139],
513 darkgoldenrod: [184, 134, 11],
514 darkgray: [169, 169, 169],
515 darkgreen: [0, 100, 0],
516 darkgrey: [169, 169, 169],
517 darkkhaki: [189, 183, 107],
518 darkmagenta: [139, 0, 139],
519 darkolivegreen: [85, 107, 47],
520 darkorange: [255, 140, 0],
521 darkorchid: [153, 50, 204],
522 darkred: [139, 0, 0],
523 darksalmon: [233, 150, 122],
524 darkseagreen: [143, 188, 143],
525 darkslateblue: [72, 61, 139],
526 darkslategray: [47, 79, 79],
527 darkslategrey: [47, 79, 79],
528 darkturquoise: [0, 206, 209],
529 darkviolet: [148, 0, 211],
530 deeppink: [255, 20, 147],
531 deepskyblue: [0, 191, 255],
532 dimgray: [105, 105, 105],
533 dimgrey: [105, 105, 105],
534 dodgerblue: [30, 144, 255],
535 firebrick: [178, 34, 34],
536 floralwhite: [255, 250, 240],
537 forestgreen: [34, 139, 34],
538 fuchsia: [255, 0, 255],
539 gainsboro: [220, 220, 220],
540 ghostwhite: [248, 248, 255],
541 gold: [255, 215, 0],
542 goldenrod: [218, 165, 32],
543 gray: [128, 128, 128],
544 grey: [128, 128, 128],
545 green: [0, 128, 0],
546 greenyellow: [173, 255, 47],
547 honeydew: [240, 255, 240],
548 hotpink: [255, 105, 180],
549 indianred: [205, 92, 92],
550 indigo: [75, 0, 130],
551 ivory: [255, 255, 240],
552 khaki: [240, 230, 140],
553 lavender: [230, 230, 250],
554 lavenderblush: [255, 240, 245],
555 lawngreen: [124, 252, 0],
556 lemonchiffon: [255, 250, 205],
557 lightblue: [173, 216, 230],
558 lightcoral: [240, 128, 128],
559 lightcyan: [224, 255, 255],
560 lightgoldenrodyellow: [250, 250, 210],
561 lightgray: [211, 211, 211],
562 lightgreen: [144, 238, 144],
563 lightgrey: [211, 211, 211],
564 lightpink: [255, 182, 193],
565 lightsalmon: [255, 160, 122],
566 lightseagreen: [32, 178, 170],
567 lightskyblue: [135, 206, 250],
568 lightslategray: [119, 136, 153],
569 lightslategrey: [119, 136, 153],
570 lightsteelblue: [176, 196, 222],
571 lightyellow: [255, 255, 224],
572 lime: [0, 255, 0],
573 limegreen: [50, 205, 50],
574 linen: [250, 240, 230],
575 magenta: [255, 0, 255],
576 maroon: [128, 0, 0],
577 mediumaquamarine: [102, 205, 170],
578 mediumblue: [0, 0, 205],
579 mediumorchid: [186, 85, 211],
580 mediumpurple: [147, 112, 219],
581 mediumseagreen: [60, 179, 113],
582 mediumslateblue: [123, 104, 238],
583 mediumspringgreen: [0, 250, 154],
584 mediumturquoise: [72, 209, 204],
585 mediumvioletred: [199, 21, 133],
586 midnightblue: [25, 25, 112],
587 mintcream: [245, 255, 250],
588 mistyrose: [255, 228, 225],
589 moccasin: [255, 228, 181],
590 navajowhite: [255, 222, 173],
591 navy: [0, 0, 128],
592 oldlace: [253, 245, 230],
593 olive: [128, 128, 0],
594 olivedrab: [107, 142, 35],
595 orange: [255, 165, 0],
596 orangered: [255, 69, 0],
597 orchid: [218, 112, 214],
598 palegoldenrod: [238, 232, 170],
599 palegreen: [152, 251, 152],
600 paleturquoise: [175, 238, 238],
601 palevioletred: [219, 112, 147],
602 papayawhip: [255, 239, 213],
603 peachpuff: [255, 218, 185],
604 peru: [205, 133, 63],
605 pink: [255, 192, 203],
606 plum: [221, 160, 221],
607 powderblue: [176, 224, 230],
608 purple: [128, 0, 128],
609 red: [255, 0, 0],
610 rosybrown: [188, 143, 143],
611 royalblue: [65, 105, 225],
612 saddlebrown: [139, 69, 19],
613 salmon: [250, 128, 114],
614 sandybrown: [244, 164, 96],
615 seagreen: [46, 139, 87],
616 seashell: [255, 245, 238],
617 sienna: [160, 82, 45],
618 silver: [192, 192, 192],
619 skyblue: [135, 206, 235],
620 slateblue: [106, 90, 205],
621 slategray: [112, 128, 144],
622 slategrey: [112, 128, 144],
623 snow: [255, 250, 250],
624 springgreen: [0, 255, 127],
625 steelblue: [70, 130, 180],
626 tan: [210, 180, 140],
627 teal: [0, 128, 128],
628 thistle: [216, 191, 216],
629 tomato: [255, 99, 71],
630 turquoise: [64, 224, 208],
631 violet: [238, 130, 238],
632 wheat: [245, 222, 179],
633 white: [255, 255, 255],
634 whitesmoke: [245, 245, 245],
635 yellow: [255, 255, 0],
636 yellowgreen: [154, 205, 50]
637 };
638
639 var setMap = function setMap(options) {
640 var obj = options.map;
641 var keys = options.keys;
642 var l = keys.length;
643
644 for (var i = 0; i < l; i++) {
645 var key = keys[i];
646
647 if (plainObject(key)) {
648 throw Error('Tried to set map with object key');
649 }
650
651 if (i < keys.length - 1) {
652 // extend the map if necessary
653 if (obj[key] == null) {
654 obj[key] = {};
655 }
656
657 obj = obj[key];
658 } else {
659 // set the value
660 obj[key] = options.value;
661 }
662 }
663 }; // gets the value in a map even if it's not built in places
664
665 var getMap = function getMap(options) {
666 var obj = options.map;
667 var keys = options.keys;
668 var l = keys.length;
669
670 for (var i = 0; i < l; i++) {
671 var key = keys[i];
672
673 if (plainObject(key)) {
674 throw Error('Tried to get map with object key');
675 }
676
677 obj = obj[key];
678
679 if (obj == null) {
680 return obj;
681 }
682 }
683
684 return obj;
685 }; // deletes the entry in the map
686
687 /**
688 * Checks if `value` is the
689 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
690 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
691 *
692 * @static
693 * @memberOf _
694 * @since 0.1.0
695 * @category Lang
696 * @param {*} value The value to check.
697 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
698 * @example
699 *
700 * _.isObject({});
701 * // => true
702 *
703 * _.isObject([1, 2, 3]);
704 * // => true
705 *
706 * _.isObject(_.noop);
707 * // => true
708 *
709 * _.isObject(null);
710 * // => false
711 */
712 function isObject(value) {
713 var type = typeof value;
714 return value != null && (type == 'object' || type == 'function');
715 }
716
717 var isObject_1 = isObject;
718
719 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
720
721 function createCommonjsModule(fn, module) {
722 return module = { exports: {} }, fn(module, module.exports), module.exports;
723 }
724
725 /** Detect free variable `global` from Node.js. */
726 var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
727
728 var _freeGlobal = freeGlobal;
729
730 /** Detect free variable `self`. */
731 var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
732
733 /** Used as a reference to the global object. */
734 var root = _freeGlobal || freeSelf || Function('return this')();
735
736 var _root = root;
737
738 /**
739 * Gets the timestamp of the number of milliseconds that have elapsed since
740 * the Unix epoch (1 January 1970 00:00:00 UTC).
741 *
742 * @static
743 * @memberOf _
744 * @since 2.4.0
745 * @category Date
746 * @returns {number} Returns the timestamp.
747 * @example
748 *
749 * _.defer(function(stamp) {
750 * console.log(_.now() - stamp);
751 * }, _.now());
752 * // => Logs the number of milliseconds it took for the deferred invocation.
753 */
754 var now = function() {
755 return _root.Date.now();
756 };
757
758 var now_1 = now;
759
760 /** Used to match a single whitespace character. */
761 var reWhitespace = /\s/;
762
763 /**
764 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
765 * character of `string`.
766 *
767 * @private
768 * @param {string} string The string to inspect.
769 * @returns {number} Returns the index of the last non-whitespace character.
770 */
771 function trimmedEndIndex(string) {
772 var index = string.length;
773
774 while (index-- && reWhitespace.test(string.charAt(index))) {}
775 return index;
776 }
777
778 var _trimmedEndIndex = trimmedEndIndex;
779
780 /** Used to match leading whitespace. */
781 var reTrimStart = /^\s+/;
782
783 /**
784 * The base implementation of `_.trim`.
785 *
786 * @private
787 * @param {string} string The string to trim.
788 * @returns {string} Returns the trimmed string.
789 */
790 function baseTrim(string) {
791 return string
792 ? string.slice(0, _trimmedEndIndex(string) + 1).replace(reTrimStart, '')
793 : string;
794 }
795
796 var _baseTrim = baseTrim;
797
798 /** Built-in value references. */
799 var Symbol$1 = _root.Symbol;
800
801 var _Symbol = Symbol$1;
802
803 /** Used for built-in method references. */
804 var objectProto$5 = Object.prototype;
805
806 /** Used to check objects for own properties. */
807 var hasOwnProperty$4 = objectProto$5.hasOwnProperty;
808
809 /**
810 * Used to resolve the
811 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
812 * of values.
813 */
814 var nativeObjectToString$1 = objectProto$5.toString;
815
816 /** Built-in value references. */
817 var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
818
819 /**
820 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
821 *
822 * @private
823 * @param {*} value The value to query.
824 * @returns {string} Returns the raw `toStringTag`.
825 */
826 function getRawTag(value) {
827 var isOwn = hasOwnProperty$4.call(value, symToStringTag$1),
828 tag = value[symToStringTag$1];
829
830 try {
831 value[symToStringTag$1] = undefined;
832 var unmasked = true;
833 } catch (e) {}
834
835 var result = nativeObjectToString$1.call(value);
836 if (unmasked) {
837 if (isOwn) {
838 value[symToStringTag$1] = tag;
839 } else {
840 delete value[symToStringTag$1];
841 }
842 }
843 return result;
844 }
845
846 var _getRawTag = getRawTag;
847
848 /** Used for built-in method references. */
849 var objectProto$4 = Object.prototype;
850
851 /**
852 * Used to resolve the
853 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
854 * of values.
855 */
856 var nativeObjectToString = objectProto$4.toString;
857
858 /**
859 * Converts `value` to a string using `Object.prototype.toString`.
860 *
861 * @private
862 * @param {*} value The value to convert.
863 * @returns {string} Returns the converted string.
864 */
865 function objectToString(value) {
866 return nativeObjectToString.call(value);
867 }
868
869 var _objectToString = objectToString;
870
871 /** `Object#toString` result references. */
872 var nullTag = '[object Null]',
873 undefinedTag = '[object Undefined]';
874
875 /** Built-in value references. */
876 var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
877
878 /**
879 * The base implementation of `getTag` without fallbacks for buggy environments.
880 *
881 * @private
882 * @param {*} value The value to query.
883 * @returns {string} Returns the `toStringTag`.
884 */
885 function baseGetTag(value) {
886 if (value == null) {
887 return value === undefined ? undefinedTag : nullTag;
888 }
889 return (symToStringTag && symToStringTag in Object(value))
890 ? _getRawTag(value)
891 : _objectToString(value);
892 }
893
894 var _baseGetTag = baseGetTag;
895
896 /**
897 * Checks if `value` is object-like. A value is object-like if it's not `null`
898 * and has a `typeof` result of "object".
899 *
900 * @static
901 * @memberOf _
902 * @since 4.0.0
903 * @category Lang
904 * @param {*} value The value to check.
905 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
906 * @example
907 *
908 * _.isObjectLike({});
909 * // => true
910 *
911 * _.isObjectLike([1, 2, 3]);
912 * // => true
913 *
914 * _.isObjectLike(_.noop);
915 * // => false
916 *
917 * _.isObjectLike(null);
918 * // => false
919 */
920 function isObjectLike(value) {
921 return value != null && typeof value == 'object';
922 }
923
924 var isObjectLike_1 = isObjectLike;
925
926 /** `Object#toString` result references. */
927 var symbolTag = '[object Symbol]';
928
929 /**
930 * Checks if `value` is classified as a `Symbol` primitive or object.
931 *
932 * @static
933 * @memberOf _
934 * @since 4.0.0
935 * @category Lang
936 * @param {*} value The value to check.
937 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
938 * @example
939 *
940 * _.isSymbol(Symbol.iterator);
941 * // => true
942 *
943 * _.isSymbol('abc');
944 * // => false
945 */
946 function isSymbol(value) {
947 return typeof value == 'symbol' ||
948 (isObjectLike_1(value) && _baseGetTag(value) == symbolTag);
949 }
950
951 var isSymbol_1 = isSymbol;
952
953 /** Used as references for various `Number` constants. */
954 var NAN = 0 / 0;
955
956 /** Used to detect bad signed hexadecimal string values. */
957 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
958
959 /** Used to detect binary string values. */
960 var reIsBinary = /^0b[01]+$/i;
961
962 /** Used to detect octal string values. */
963 var reIsOctal = /^0o[0-7]+$/i;
964
965 /** Built-in method references without a dependency on `root`. */
966 var freeParseInt = parseInt;
967
968 /**
969 * Converts `value` to a number.
970 *
971 * @static
972 * @memberOf _
973 * @since 4.0.0
974 * @category Lang
975 * @param {*} value The value to process.
976 * @returns {number} Returns the number.
977 * @example
978 *
979 * _.toNumber(3.2);
980 * // => 3.2
981 *
982 * _.toNumber(Number.MIN_VALUE);
983 * // => 5e-324
984 *
985 * _.toNumber(Infinity);
986 * // => Infinity
987 *
988 * _.toNumber('3.2');
989 * // => 3.2
990 */
991 function toNumber(value) {
992 if (typeof value == 'number') {
993 return value;
994 }
995 if (isSymbol_1(value)) {
996 return NAN;
997 }
998 if (isObject_1(value)) {
999 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
1000 value = isObject_1(other) ? (other + '') : other;
1001 }
1002 if (typeof value != 'string') {
1003 return value === 0 ? value : +value;
1004 }
1005 value = _baseTrim(value);
1006 var isBinary = reIsBinary.test(value);
1007 return (isBinary || reIsOctal.test(value))
1008 ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
1009 : (reIsBadHex.test(value) ? NAN : +value);
1010 }
1011
1012 var toNumber_1 = toNumber;
1013
1014 /** Error message constants. */
1015 var FUNC_ERROR_TEXT$1 = 'Expected a function';
1016
1017 /* Built-in method references for those with the same name as other `lodash` methods. */
1018 var nativeMax = Math.max,
1019 nativeMin = Math.min;
1020
1021 /**
1022 * Creates a debounced function that delays invoking `func` until after `wait`
1023 * milliseconds have elapsed since the last time the debounced function was
1024 * invoked. The debounced function comes with a `cancel` method to cancel
1025 * delayed `func` invocations and a `flush` method to immediately invoke them.
1026 * Provide `options` to indicate whether `func` should be invoked on the
1027 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
1028 * with the last arguments provided to the debounced function. Subsequent
1029 * calls to the debounced function return the result of the last `func`
1030 * invocation.
1031 *
1032 * **Note:** If `leading` and `trailing` options are `true`, `func` is
1033 * invoked on the trailing edge of the timeout only if the debounced function
1034 * is invoked more than once during the `wait` timeout.
1035 *
1036 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
1037 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
1038 *
1039 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
1040 * for details over the differences between `_.debounce` and `_.throttle`.
1041 *
1042 * @static
1043 * @memberOf _
1044 * @since 0.1.0
1045 * @category Function
1046 * @param {Function} func The function to debounce.
1047 * @param {number} [wait=0] The number of milliseconds to delay.
1048 * @param {Object} [options={}] The options object.
1049 * @param {boolean} [options.leading=false]
1050 * Specify invoking on the leading edge of the timeout.
1051 * @param {number} [options.maxWait]
1052 * The maximum time `func` is allowed to be delayed before it's invoked.
1053 * @param {boolean} [options.trailing=true]
1054 * Specify invoking on the trailing edge of the timeout.
1055 * @returns {Function} Returns the new debounced function.
1056 * @example
1057 *
1058 * // Avoid costly calculations while the window size is in flux.
1059 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
1060 *
1061 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
1062 * jQuery(element).on('click', _.debounce(sendMail, 300, {
1063 * 'leading': true,
1064 * 'trailing': false
1065 * }));
1066 *
1067 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
1068 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
1069 * var source = new EventSource('/stream');
1070 * jQuery(source).on('message', debounced);
1071 *
1072 * // Cancel the trailing debounced invocation.
1073 * jQuery(window).on('popstate', debounced.cancel);
1074 */
1075 function debounce(func, wait, options) {
1076 var lastArgs,
1077 lastThis,
1078 maxWait,
1079 result,
1080 timerId,
1081 lastCallTime,
1082 lastInvokeTime = 0,
1083 leading = false,
1084 maxing = false,
1085 trailing = true;
1086
1087 if (typeof func != 'function') {
1088 throw new TypeError(FUNC_ERROR_TEXT$1);
1089 }
1090 wait = toNumber_1(wait) || 0;
1091 if (isObject_1(options)) {
1092 leading = !!options.leading;
1093 maxing = 'maxWait' in options;
1094 maxWait = maxing ? nativeMax(toNumber_1(options.maxWait) || 0, wait) : maxWait;
1095 trailing = 'trailing' in options ? !!options.trailing : trailing;
1096 }
1097
1098 function invokeFunc(time) {
1099 var args = lastArgs,
1100 thisArg = lastThis;
1101
1102 lastArgs = lastThis = undefined;
1103 lastInvokeTime = time;
1104 result = func.apply(thisArg, args);
1105 return result;
1106 }
1107
1108 function leadingEdge(time) {
1109 // Reset any `maxWait` timer.
1110 lastInvokeTime = time;
1111 // Start the timer for the trailing edge.
1112 timerId = setTimeout(timerExpired, wait);
1113 // Invoke the leading edge.
1114 return leading ? invokeFunc(time) : result;
1115 }
1116
1117 function remainingWait(time) {
1118 var timeSinceLastCall = time - lastCallTime,
1119 timeSinceLastInvoke = time - lastInvokeTime,
1120 timeWaiting = wait - timeSinceLastCall;
1121
1122 return maxing
1123 ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
1124 : timeWaiting;
1125 }
1126
1127 function shouldInvoke(time) {
1128 var timeSinceLastCall = time - lastCallTime,
1129 timeSinceLastInvoke = time - lastInvokeTime;
1130
1131 // Either this is the first call, activity has stopped and we're at the
1132 // trailing edge, the system time has gone backwards and we're treating
1133 // it as the trailing edge, or we've hit the `maxWait` limit.
1134 return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
1135 (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
1136 }
1137
1138 function timerExpired() {
1139 var time = now_1();
1140 if (shouldInvoke(time)) {
1141 return trailingEdge(time);
1142 }
1143 // Restart the timer.
1144 timerId = setTimeout(timerExpired, remainingWait(time));
1145 }
1146
1147 function trailingEdge(time) {
1148 timerId = undefined;
1149
1150 // Only invoke if we have `lastArgs` which means `func` has been
1151 // debounced at least once.
1152 if (trailing && lastArgs) {
1153 return invokeFunc(time);
1154 }
1155 lastArgs = lastThis = undefined;
1156 return result;
1157 }
1158
1159 function cancel() {
1160 if (timerId !== undefined) {
1161 clearTimeout(timerId);
1162 }
1163 lastInvokeTime = 0;
1164 lastArgs = lastCallTime = lastThis = timerId = undefined;
1165 }
1166
1167 function flush() {
1168 return timerId === undefined ? result : trailingEdge(now_1());
1169 }
1170
1171 function debounced() {
1172 var time = now_1(),
1173 isInvoking = shouldInvoke(time);
1174
1175 lastArgs = arguments;
1176 lastThis = this;
1177 lastCallTime = time;
1178
1179 if (isInvoking) {
1180 if (timerId === undefined) {
1181 return leadingEdge(lastCallTime);
1182 }
1183 if (maxing) {
1184 // Handle invocations in a tight loop.
1185 clearTimeout(timerId);
1186 timerId = setTimeout(timerExpired, wait);
1187 return invokeFunc(lastCallTime);
1188 }
1189 }
1190 if (timerId === undefined) {
1191 timerId = setTimeout(timerExpired, wait);
1192 }
1193 return result;
1194 }
1195 debounced.cancel = cancel;
1196 debounced.flush = flush;
1197 return debounced;
1198 }
1199
1200 var debounce_1 = debounce;
1201
1202 var performance = window$1 ? window$1.performance : null;
1203 var pnow = performance && performance.now ? function () {
1204 return performance.now();
1205 } : function () {
1206 return Date.now();
1207 };
1208
1209 var raf = function () {
1210 if (window$1) {
1211 if (window$1.requestAnimationFrame) {
1212 return function (fn) {
1213 window$1.requestAnimationFrame(fn);
1214 };
1215 } else if (window$1.mozRequestAnimationFrame) {
1216 return function (fn) {
1217 window$1.mozRequestAnimationFrame(fn);
1218 };
1219 } else if (window$1.webkitRequestAnimationFrame) {
1220 return function (fn) {
1221 window$1.webkitRequestAnimationFrame(fn);
1222 };
1223 } else if (window$1.msRequestAnimationFrame) {
1224 return function (fn) {
1225 window$1.msRequestAnimationFrame(fn);
1226 };
1227 }
1228 }
1229
1230 return function (fn) {
1231 if (fn) {
1232 setTimeout(function () {
1233 fn(pnow());
1234 }, 1000 / 60);
1235 }
1236 };
1237 }();
1238
1239 var requestAnimationFrame = function requestAnimationFrame(fn) {
1240 return raf(fn);
1241 };
1242 var performanceNow = pnow;
1243
1244 var DEFAULT_HASH_SEED = 9261;
1245 var K = 65599; // 37 also works pretty well
1246
1247 var DEFAULT_HASH_SEED_ALT = 5381;
1248 var hashIterableInts = function hashIterableInts(iterator) {
1249 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1250 // sdbm/string-hash
1251 var hash = seed;
1252 var entry;
1253
1254 for (;;) {
1255 entry = iterator.next();
1256
1257 if (entry.done) {
1258 break;
1259 }
1260
1261 hash = hash * K + entry.value | 0;
1262 }
1263
1264 return hash;
1265 };
1266 var hashInt = function hashInt(num) {
1267 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1268 // sdbm/string-hash
1269 return seed * K + num | 0;
1270 };
1271 var hashIntAlt = function hashIntAlt(num) {
1272 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
1273 // djb2/string-hash
1274 return (seed << 5) + seed + num | 0;
1275 };
1276 var combineHashes = function combineHashes(hash1, hash2) {
1277 return hash1 * 0x200000 + hash2;
1278 };
1279 var combineHashesArray = function combineHashesArray(hashes) {
1280 return hashes[0] * 0x200000 + hashes[1];
1281 };
1282 var hashArrays = function hashArrays(hashes1, hashes2) {
1283 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
1284 };
1285 var hashIntsArray = function hashIntsArray(ints, seed) {
1286 var entry = {
1287 value: 0,
1288 done: false
1289 };
1290 var i = 0;
1291 var length = ints.length;
1292 var iterator = {
1293 next: function next() {
1294 if (i < length) {
1295 entry.value = ints[i++];
1296 } else {
1297 entry.done = true;
1298 }
1299
1300 return entry;
1301 }
1302 };
1303 return hashIterableInts(iterator, seed);
1304 };
1305 var hashString = function hashString(str, seed) {
1306 var entry = {
1307 value: 0,
1308 done: false
1309 };
1310 var i = 0;
1311 var length = str.length;
1312 var iterator = {
1313 next: function next() {
1314 if (i < length) {
1315 entry.value = str.charCodeAt(i++);
1316 } else {
1317 entry.done = true;
1318 }
1319
1320 return entry;
1321 }
1322 };
1323 return hashIterableInts(iterator, seed);
1324 };
1325 var hashStrings = function hashStrings() {
1326 return hashStringsArray(arguments);
1327 };
1328 var hashStringsArray = function hashStringsArray(strs) {
1329 var hash;
1330
1331 for (var i = 0; i < strs.length; i++) {
1332 var str = strs[i];
1333
1334 if (i === 0) {
1335 hash = hashString(str);
1336 } else {
1337 hash = hashString(str, hash);
1338 }
1339 }
1340
1341 return hash;
1342 };
1343
1344 /*global console */
1345 var warningsEnabled = true;
1346 var warnSupported = console.warn != null; // eslint-disable-line no-console
1347
1348 var traceSupported = console.trace != null; // eslint-disable-line no-console
1349
1350 var MAX_INT$1 = Number.MAX_SAFE_INTEGER || 9007199254740991;
1351 var trueify = function trueify() {
1352 return true;
1353 };
1354 var falsify = function falsify() {
1355 return false;
1356 };
1357 var zeroify = function zeroify() {
1358 return 0;
1359 };
1360 var noop$1 = function noop() {};
1361 var error = function error(msg) {
1362 throw new Error(msg);
1363 };
1364 var warnings = function warnings(enabled) {
1365 if (enabled !== undefined) {
1366 warningsEnabled = !!enabled;
1367 } else {
1368 return warningsEnabled;
1369 }
1370 };
1371 var warn = function warn(msg) {
1372 /* eslint-disable no-console */
1373 if (!warnings()) {
1374 return;
1375 }
1376
1377 if (warnSupported) {
1378 console.warn(msg);
1379 } else {
1380 console.log(msg);
1381
1382 if (traceSupported) {
1383 console.trace();
1384 }
1385 }
1386 };
1387 /* eslint-enable */
1388
1389 var clone = function clone(obj) {
1390 return extend({}, obj);
1391 }; // gets a shallow copy of the argument
1392
1393 var copy = function copy(obj) {
1394 if (obj == null) {
1395 return obj;
1396 }
1397
1398 if (array(obj)) {
1399 return obj.slice();
1400 } else if (plainObject(obj)) {
1401 return clone(obj);
1402 } else {
1403 return obj;
1404 }
1405 };
1406 var copyArray$1 = function copyArray(arr) {
1407 return arr.slice();
1408 };
1409 var uuid = function uuid(a, b
1410 /* placeholders */
1411 ) {
1412 for ( // loop :)
1413 b = a = ''; // b - result , a - numeric letiable
1414 a++ < 36; //
1415 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
1416 ? // return a random number or 4
1417 (a ^ 15 // if "a" is not 15
1418 ? // generate a random number from 0 to 15
1419 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
1420 : 4 // otherwise 4
1421 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
1422 ) {
1423 }
1424
1425 return b;
1426 };
1427 var _staticEmptyObject = {};
1428 var staticEmptyObject = function staticEmptyObject() {
1429 return _staticEmptyObject;
1430 };
1431 var defaults$g = function defaults(_defaults) {
1432 var keys = Object.keys(_defaults);
1433 return function (opts) {
1434 var filledOpts = {};
1435
1436 for (var i = 0; i < keys.length; i++) {
1437 var key = keys[i];
1438 var optVal = opts == null ? undefined : opts[key];
1439 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
1440 }
1441
1442 return filledOpts;
1443 };
1444 };
1445 var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
1446 for (var i = arr.length - 1; i >= 0; i--) {
1447 if (arr[i] === ele) {
1448 arr.splice(i, 1);
1449
1450 if (oneCopy) {
1451 break;
1452 }
1453 }
1454 }
1455 };
1456 var clearArray = function clearArray(arr) {
1457 arr.splice(0, arr.length);
1458 };
1459 var push = function push(arr, otherArr) {
1460 for (var i = 0; i < otherArr.length; i++) {
1461 var el = otherArr[i];
1462 arr.push(el);
1463 }
1464 };
1465 var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
1466 if (prefix) {
1467 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1468 }
1469
1470 return obj[propName];
1471 };
1472 var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
1473 if (prefix) {
1474 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1475 }
1476
1477 obj[propName] = value;
1478 };
1479
1480 /* global Map */
1481 var ObjectMap = /*#__PURE__*/function () {
1482 function ObjectMap() {
1483 _classCallCheck(this, ObjectMap);
1484
1485 this._obj = {};
1486 }
1487
1488 _createClass(ObjectMap, [{
1489 key: "set",
1490 value: function set(key, val) {
1491 this._obj[key] = val;
1492 return this;
1493 }
1494 }, {
1495 key: "delete",
1496 value: function _delete(key) {
1497 this._obj[key] = undefined;
1498 return this;
1499 }
1500 }, {
1501 key: "clear",
1502 value: function clear() {
1503 this._obj = {};
1504 }
1505 }, {
1506 key: "has",
1507 value: function has(key) {
1508 return this._obj[key] !== undefined;
1509 }
1510 }, {
1511 key: "get",
1512 value: function get(key) {
1513 return this._obj[key];
1514 }
1515 }]);
1516
1517 return ObjectMap;
1518 }();
1519
1520 var Map$2 = typeof Map !== 'undefined' ? Map : ObjectMap;
1521
1522 /* global Set */
1523 var undef = "undefined" ;
1524
1525 var ObjectSet = /*#__PURE__*/function () {
1526 function ObjectSet(arrayOrObjectSet) {
1527 _classCallCheck(this, ObjectSet);
1528
1529 this._obj = Object.create(null);
1530 this.size = 0;
1531
1532 if (arrayOrObjectSet != null) {
1533 var arr;
1534
1535 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1536 arr = arrayOrObjectSet.toArray();
1537 } else {
1538 arr = arrayOrObjectSet;
1539 }
1540
1541 for (var i = 0; i < arr.length; i++) {
1542 this.add(arr[i]);
1543 }
1544 }
1545 }
1546
1547 _createClass(ObjectSet, [{
1548 key: "instanceString",
1549 value: function instanceString() {
1550 return 'set';
1551 }
1552 }, {
1553 key: "add",
1554 value: function add(val) {
1555 var o = this._obj;
1556
1557 if (o[val] !== 1) {
1558 o[val] = 1;
1559 this.size++;
1560 }
1561 }
1562 }, {
1563 key: "delete",
1564 value: function _delete(val) {
1565 var o = this._obj;
1566
1567 if (o[val] === 1) {
1568 o[val] = 0;
1569 this.size--;
1570 }
1571 }
1572 }, {
1573 key: "clear",
1574 value: function clear() {
1575 this._obj = Object.create(null);
1576 }
1577 }, {
1578 key: "has",
1579 value: function has(val) {
1580 return this._obj[val] === 1;
1581 }
1582 }, {
1583 key: "toArray",
1584 value: function toArray() {
1585 var _this = this;
1586
1587 return Object.keys(this._obj).filter(function (key) {
1588 return _this.has(key);
1589 });
1590 }
1591 }, {
1592 key: "forEach",
1593 value: function forEach(callback, thisArg) {
1594 return this.toArray().forEach(callback, thisArg);
1595 }
1596 }]);
1597
1598 return ObjectSet;
1599 }();
1600
1601 var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1602
1603 var Element = function Element(cy, params) {
1604 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1605
1606 if (cy === undefined || params === undefined || !core(cy)) {
1607 error('An element must have a core reference and parameters set');
1608 return;
1609 }
1610
1611 var group = params.group; // try to automatically infer the group if unspecified
1612
1613 if (group == null) {
1614 if (params.data && params.data.source != null && params.data.target != null) {
1615 group = 'edges';
1616 } else {
1617 group = 'nodes';
1618 }
1619 } // validate group
1620
1621
1622 if (group !== 'nodes' && group !== 'edges') {
1623 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1624 return;
1625 } // make the element array-like, just like a collection
1626
1627
1628 this.length = 1;
1629 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1630
1631 var _p = this._private = {
1632 cy: cy,
1633 single: true,
1634 // indicates this is an element
1635 data: params.data || {},
1636 // data object
1637 position: params.position || {
1638 x: 0,
1639 y: 0
1640 },
1641 // (x, y) position pair
1642 autoWidth: undefined,
1643 // width and height of nodes calculated by the renderer when set to special 'auto' value
1644 autoHeight: undefined,
1645 autoPadding: undefined,
1646 compoundBoundsClean: false,
1647 // whether the compound dimensions need to be recalculated the next time dimensions are read
1648 listeners: [],
1649 // array of bound listeners
1650 group: group,
1651 // string; 'nodes' or 'edges'
1652 style: {},
1653 // properties as set by the style
1654 rstyle: {},
1655 // properties for style sent from the renderer to the core
1656 styleCxts: [],
1657 // applied style contexts from the styler
1658 styleKeys: {},
1659 // per-group keys of style property values
1660 removed: true,
1661 // whether it's inside the vis; true if removed (set true here since we call restore)
1662 selected: params.selected ? true : false,
1663 // whether it's selected
1664 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1665 // whether it's selectable
1666 locked: params.locked ? true : false,
1667 // whether the element is locked (cannot be moved)
1668 grabbed: false,
1669 // whether the element is grabbed by the mouse; renderer sets this privately
1670 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1671 // whether the element can be grabbed
1672 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1673 // whether the element has passthrough panning enabled
1674 active: false,
1675 // whether the element is active from user interaction
1676 classes: new Set$1(),
1677 // map ( className => true )
1678 animation: {
1679 // object for currently-running animations
1680 current: [],
1681 queue: []
1682 },
1683 rscratch: {},
1684 // object in which the renderer can store information
1685 scratch: params.scratch || {},
1686 // scratch objects
1687 edges: [],
1688 // array of connected edges
1689 children: [],
1690 // array of children
1691 parent: params.parent && params.parent.isNode() ? params.parent : null,
1692 // parent ref
1693 traversalCache: {},
1694 // cache of output of traversal functions
1695 backgrounding: false,
1696 // whether background images are loading
1697 bbCache: null,
1698 // cache of the current bounding box
1699 bbCacheShift: {
1700 x: 0,
1701 y: 0
1702 },
1703 // shift applied to cached bb to be applied on next get
1704 bodyBounds: null,
1705 // bounds cache of element body, w/o overlay
1706 overlayBounds: null,
1707 // bounds cache of element body, including overlay
1708 labelBounds: {
1709 // bounds cache of labels
1710 all: null,
1711 source: null,
1712 target: null,
1713 main: null
1714 },
1715 arrowBounds: {
1716 // bounds cache of edge arrows
1717 source: null,
1718 target: null,
1719 'mid-source': null,
1720 'mid-target': null
1721 }
1722 };
1723
1724 if (_p.position.x == null) {
1725 _p.position.x = 0;
1726 }
1727
1728 if (_p.position.y == null) {
1729 _p.position.y = 0;
1730 } // renderedPosition overrides if specified
1731
1732
1733 if (params.renderedPosition) {
1734 var rpos = params.renderedPosition;
1735 var pan = cy.pan();
1736 var zoom = cy.zoom();
1737 _p.position = {
1738 x: (rpos.x - pan.x) / zoom,
1739 y: (rpos.y - pan.y) / zoom
1740 };
1741 }
1742
1743 var classes = [];
1744
1745 if (array(params.classes)) {
1746 classes = params.classes;
1747 } else if (string(params.classes)) {
1748 classes = params.classes.split(/\s+/);
1749 }
1750
1751 for (var i = 0, l = classes.length; i < l; i++) {
1752 var cls = classes[i];
1753
1754 if (!cls || cls === '') {
1755 continue;
1756 }
1757
1758 _p.classes.add(cls);
1759 }
1760
1761 this.createEmitter();
1762 var bypass = params.style || params.css;
1763
1764 if (bypass) {
1765 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1766 this.style(bypass);
1767 }
1768
1769 if (restore === undefined || restore) {
1770 this.restore();
1771 }
1772 };
1773
1774 var defineSearch = function defineSearch(params) {
1775 params = {
1776 bfs: params.bfs || !params.dfs,
1777 dfs: params.dfs || !params.bfs
1778 }; // from pseudocode on wikipedia
1779
1780 return function searchFn(roots, fn, directed) {
1781 var options;
1782
1783 if (plainObject(roots) && !elementOrCollection(roots)) {
1784 options = roots;
1785 roots = options.roots || options.root;
1786 fn = options.visit;
1787 directed = options.directed;
1788 }
1789
1790 directed = arguments.length === 2 && !fn$6(fn) ? fn : directed;
1791 fn = fn$6(fn) ? fn : function () {};
1792 var cy = this._private.cy;
1793 var v = roots = string(roots) ? this.filter(roots) : roots;
1794 var Q = [];
1795 var connectedNodes = [];
1796 var connectedBy = {};
1797 var id2depth = {};
1798 var V = {};
1799 var j = 0;
1800 var found;
1801
1802 var _this$byGroup = this.byGroup(),
1803 nodes = _this$byGroup.nodes,
1804 edges = _this$byGroup.edges; // enqueue v
1805
1806
1807 for (var i = 0; i < v.length; i++) {
1808 var vi = v[i];
1809 var viId = vi.id();
1810
1811 if (vi.isNode()) {
1812 Q.unshift(vi);
1813
1814 if (params.bfs) {
1815 V[viId] = true;
1816 connectedNodes.push(vi);
1817 }
1818
1819 id2depth[viId] = 0;
1820 }
1821 }
1822
1823 var _loop = function _loop() {
1824 var v = params.bfs ? Q.shift() : Q.pop();
1825 var vId = v.id();
1826
1827 if (params.dfs) {
1828 if (V[vId]) {
1829 return "continue";
1830 }
1831
1832 V[vId] = true;
1833 connectedNodes.push(v);
1834 }
1835
1836 var depth = id2depth[vId];
1837 var prevEdge = connectedBy[vId];
1838 var src = prevEdge != null ? prevEdge.source() : null;
1839 var tgt = prevEdge != null ? prevEdge.target() : null;
1840 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1841 var ret = void 0;
1842 ret = fn(v, prevEdge, prevNode, j++, depth);
1843
1844 if (ret === true) {
1845 found = v;
1846 return "break";
1847 }
1848
1849 if (ret === false) {
1850 return "break";
1851 }
1852
1853 var vwEdges = v.connectedEdges().filter(function (e) {
1854 return (!directed || e.source().same(v)) && edges.has(e);
1855 });
1856
1857 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1858 var e = vwEdges[_i2];
1859 var w = e.connectedNodes().filter(function (n) {
1860 return !n.same(v) && nodes.has(n);
1861 });
1862 var wId = w.id();
1863
1864 if (w.length !== 0 && !V[wId]) {
1865 w = w[0];
1866 Q.push(w);
1867
1868 if (params.bfs) {
1869 V[wId] = true;
1870 connectedNodes.push(w);
1871 }
1872
1873 connectedBy[wId] = e;
1874 id2depth[wId] = id2depth[vId] + 1;
1875 }
1876 }
1877 };
1878
1879 while (Q.length !== 0) {
1880 var _ret = _loop();
1881
1882 if (_ret === "continue") continue;
1883 if (_ret === "break") break;
1884 }
1885
1886 var connectedEles = cy.collection();
1887
1888 for (var _i = 0; _i < connectedNodes.length; _i++) {
1889 var node = connectedNodes[_i];
1890 var edge = connectedBy[node.id()];
1891
1892 if (edge != null) {
1893 connectedEles.push(edge);
1894 }
1895
1896 connectedEles.push(node);
1897 }
1898
1899 return {
1900 path: cy.collection(connectedEles),
1901 found: cy.collection(found)
1902 };
1903 };
1904 }; // search, spanning trees, etc
1905
1906
1907 var elesfn$v = {
1908 breadthFirstSearch: defineSearch({
1909 bfs: true
1910 }),
1911 depthFirstSearch: defineSearch({
1912 dfs: true
1913 })
1914 }; // nice, short mathematical alias
1915
1916 elesfn$v.bfs = elesfn$v.breadthFirstSearch;
1917 elesfn$v.dfs = elesfn$v.depthFirstSearch;
1918
1919 var heap$1 = createCommonjsModule(function (module, exports) {
1920 // Generated by CoffeeScript 1.8.0
1921 (function() {
1922 var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
1923
1924 floor = Math.floor, min = Math.min;
1925
1926
1927 /*
1928 Default comparison function to be used
1929 */
1930
1931 defaultCmp = function(x, y) {
1932 if (x < y) {
1933 return -1;
1934 }
1935 if (x > y) {
1936 return 1;
1937 }
1938 return 0;
1939 };
1940
1941
1942 /*
1943 Insert item x in list a, and keep it sorted assuming a is sorted.
1944
1945 If x is already in a, insert it to the right of the rightmost x.
1946
1947 Optional args lo (default 0) and hi (default a.length) bound the slice
1948 of a to be searched.
1949 */
1950
1951 insort = function(a, x, lo, hi, cmp) {
1952 var mid;
1953 if (lo == null) {
1954 lo = 0;
1955 }
1956 if (cmp == null) {
1957 cmp = defaultCmp;
1958 }
1959 if (lo < 0) {
1960 throw new Error('lo must be non-negative');
1961 }
1962 if (hi == null) {
1963 hi = a.length;
1964 }
1965 while (lo < hi) {
1966 mid = floor((lo + hi) / 2);
1967 if (cmp(x, a[mid]) < 0) {
1968 hi = mid;
1969 } else {
1970 lo = mid + 1;
1971 }
1972 }
1973 return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
1974 };
1975
1976
1977 /*
1978 Push item onto heap, maintaining the heap invariant.
1979 */
1980
1981 heappush = function(array, item, cmp) {
1982 if (cmp == null) {
1983 cmp = defaultCmp;
1984 }
1985 array.push(item);
1986 return _siftdown(array, 0, array.length - 1, cmp);
1987 };
1988
1989
1990 /*
1991 Pop the smallest item off the heap, maintaining the heap invariant.
1992 */
1993
1994 heappop = function(array, cmp) {
1995 var lastelt, returnitem;
1996 if (cmp == null) {
1997 cmp = defaultCmp;
1998 }
1999 lastelt = array.pop();
2000 if (array.length) {
2001 returnitem = array[0];
2002 array[0] = lastelt;
2003 _siftup(array, 0, cmp);
2004 } else {
2005 returnitem = lastelt;
2006 }
2007 return returnitem;
2008 };
2009
2010
2011 /*
2012 Pop and return the current smallest value, and add the new item.
2013
2014 This is more efficient than heappop() followed by heappush(), and can be
2015 more appropriate when using a fixed size heap. Note that the value
2016 returned may be larger than item! That constrains reasonable use of
2017 this routine unless written as part of a conditional replacement:
2018 if item > array[0]
2019 item = heapreplace(array, item)
2020 */
2021
2022 heapreplace = function(array, item, cmp) {
2023 var returnitem;
2024 if (cmp == null) {
2025 cmp = defaultCmp;
2026 }
2027 returnitem = array[0];
2028 array[0] = item;
2029 _siftup(array, 0, cmp);
2030 return returnitem;
2031 };
2032
2033
2034 /*
2035 Fast version of a heappush followed by a heappop.
2036 */
2037
2038 heappushpop = function(array, item, cmp) {
2039 var _ref;
2040 if (cmp == null) {
2041 cmp = defaultCmp;
2042 }
2043 if (array.length && cmp(array[0], item) < 0) {
2044 _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
2045 _siftup(array, 0, cmp);
2046 }
2047 return item;
2048 };
2049
2050
2051 /*
2052 Transform list into a heap, in-place, in O(array.length) time.
2053 */
2054
2055 heapify = function(array, cmp) {
2056 var i, _i, _len, _ref1, _results, _results1;
2057 if (cmp == null) {
2058 cmp = defaultCmp;
2059 }
2060 _ref1 = (function() {
2061 _results1 = [];
2062 for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
2063 return _results1;
2064 }).apply(this).reverse();
2065 _results = [];
2066 for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
2067 i = _ref1[_i];
2068 _results.push(_siftup(array, i, cmp));
2069 }
2070 return _results;
2071 };
2072
2073
2074 /*
2075 Update the position of the given item in the heap.
2076 This function should be called every time the item is being modified.
2077 */
2078
2079 updateItem = function(array, item, cmp) {
2080 var pos;
2081 if (cmp == null) {
2082 cmp = defaultCmp;
2083 }
2084 pos = array.indexOf(item);
2085 if (pos === -1) {
2086 return;
2087 }
2088 _siftdown(array, 0, pos, cmp);
2089 return _siftup(array, pos, cmp);
2090 };
2091
2092
2093 /*
2094 Find the n largest elements in a dataset.
2095 */
2096
2097 nlargest = function(array, n, cmp) {
2098 var elem, result, _i, _len, _ref;
2099 if (cmp == null) {
2100 cmp = defaultCmp;
2101 }
2102 result = array.slice(0, n);
2103 if (!result.length) {
2104 return result;
2105 }
2106 heapify(result, cmp);
2107 _ref = array.slice(n);
2108 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2109 elem = _ref[_i];
2110 heappushpop(result, elem, cmp);
2111 }
2112 return result.sort(cmp).reverse();
2113 };
2114
2115
2116 /*
2117 Find the n smallest elements in a dataset.
2118 */
2119
2120 nsmallest = function(array, n, cmp) {
2121 var elem, los, result, _i, _j, _len, _ref, _ref1, _results;
2122 if (cmp == null) {
2123 cmp = defaultCmp;
2124 }
2125 if (n * 10 <= array.length) {
2126 result = array.slice(0, n).sort(cmp);
2127 if (!result.length) {
2128 return result;
2129 }
2130 los = result[result.length - 1];
2131 _ref = array.slice(n);
2132 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2133 elem = _ref[_i];
2134 if (cmp(elem, los) < 0) {
2135 insort(result, elem, 0, null, cmp);
2136 result.pop();
2137 los = result[result.length - 1];
2138 }
2139 }
2140 return result;
2141 }
2142 heapify(array, cmp);
2143 _results = [];
2144 for (_j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; 0 <= _ref1 ? ++_j : --_j) {
2145 _results.push(heappop(array, cmp));
2146 }
2147 return _results;
2148 };
2149
2150 _siftdown = function(array, startpos, pos, cmp) {
2151 var newitem, parent, parentpos;
2152 if (cmp == null) {
2153 cmp = defaultCmp;
2154 }
2155 newitem = array[pos];
2156 while (pos > startpos) {
2157 parentpos = (pos - 1) >> 1;
2158 parent = array[parentpos];
2159 if (cmp(newitem, parent) < 0) {
2160 array[pos] = parent;
2161 pos = parentpos;
2162 continue;
2163 }
2164 break;
2165 }
2166 return array[pos] = newitem;
2167 };
2168
2169 _siftup = function(array, pos, cmp) {
2170 var childpos, endpos, newitem, rightpos, startpos;
2171 if (cmp == null) {
2172 cmp = defaultCmp;
2173 }
2174 endpos = array.length;
2175 startpos = pos;
2176 newitem = array[pos];
2177 childpos = 2 * pos + 1;
2178 while (childpos < endpos) {
2179 rightpos = childpos + 1;
2180 if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
2181 childpos = rightpos;
2182 }
2183 array[pos] = array[childpos];
2184 pos = childpos;
2185 childpos = 2 * pos + 1;
2186 }
2187 array[pos] = newitem;
2188 return _siftdown(array, startpos, pos, cmp);
2189 };
2190
2191 Heap = (function() {
2192 Heap.push = heappush;
2193
2194 Heap.pop = heappop;
2195
2196 Heap.replace = heapreplace;
2197
2198 Heap.pushpop = heappushpop;
2199
2200 Heap.heapify = heapify;
2201
2202 Heap.updateItem = updateItem;
2203
2204 Heap.nlargest = nlargest;
2205
2206 Heap.nsmallest = nsmallest;
2207
2208 function Heap(cmp) {
2209 this.cmp = cmp != null ? cmp : defaultCmp;
2210 this.nodes = [];
2211 }
2212
2213 Heap.prototype.push = function(x) {
2214 return heappush(this.nodes, x, this.cmp);
2215 };
2216
2217 Heap.prototype.pop = function() {
2218 return heappop(this.nodes, this.cmp);
2219 };
2220
2221 Heap.prototype.peek = function() {
2222 return this.nodes[0];
2223 };
2224
2225 Heap.prototype.contains = function(x) {
2226 return this.nodes.indexOf(x) !== -1;
2227 };
2228
2229 Heap.prototype.replace = function(x) {
2230 return heapreplace(this.nodes, x, this.cmp);
2231 };
2232
2233 Heap.prototype.pushpop = function(x) {
2234 return heappushpop(this.nodes, x, this.cmp);
2235 };
2236
2237 Heap.prototype.heapify = function() {
2238 return heapify(this.nodes, this.cmp);
2239 };
2240
2241 Heap.prototype.updateItem = function(x) {
2242 return updateItem(this.nodes, x, this.cmp);
2243 };
2244
2245 Heap.prototype.clear = function() {
2246 return this.nodes = [];
2247 };
2248
2249 Heap.prototype.empty = function() {
2250 return this.nodes.length === 0;
2251 };
2252
2253 Heap.prototype.size = function() {
2254 return this.nodes.length;
2255 };
2256
2257 Heap.prototype.clone = function() {
2258 var heap;
2259 heap = new Heap();
2260 heap.nodes = this.nodes.slice(0);
2261 return heap;
2262 };
2263
2264 Heap.prototype.toArray = function() {
2265 return this.nodes.slice(0);
2266 };
2267
2268 Heap.prototype.insert = Heap.prototype.push;
2269
2270 Heap.prototype.top = Heap.prototype.peek;
2271
2272 Heap.prototype.front = Heap.prototype.peek;
2273
2274 Heap.prototype.has = Heap.prototype.contains;
2275
2276 Heap.prototype.copy = Heap.prototype.clone;
2277
2278 return Heap;
2279
2280 })();
2281
2282 (function(root, factory) {
2283 {
2284 return module.exports = factory();
2285 }
2286 })(this, function() {
2287 return Heap;
2288 });
2289
2290 }).call(commonjsGlobal);
2291 });
2292
2293 var heap = heap$1;
2294
2295 var dijkstraDefaults = defaults$g({
2296 root: null,
2297 weight: function weight(edge) {
2298 return 1;
2299 },
2300 directed: false
2301 });
2302 var elesfn$u = {
2303 dijkstra: function dijkstra(options) {
2304 if (!plainObject(options)) {
2305 var args = arguments;
2306 options = {
2307 root: args[0],
2308 weight: args[1],
2309 directed: args[2]
2310 };
2311 }
2312
2313 var _dijkstraDefaults = dijkstraDefaults(options),
2314 root = _dijkstraDefaults.root,
2315 weight = _dijkstraDefaults.weight,
2316 directed = _dijkstraDefaults.directed;
2317
2318 var eles = this;
2319 var weightFn = weight;
2320 var source = string(root) ? this.filter(root)[0] : root[0];
2321 var dist = {};
2322 var prev = {};
2323 var knownDist = {};
2324
2325 var _this$byGroup = this.byGroup(),
2326 nodes = _this$byGroup.nodes,
2327 edges = _this$byGroup.edges;
2328
2329 edges.unmergeBy(function (ele) {
2330 return ele.isLoop();
2331 });
2332
2333 var getDist = function getDist(node) {
2334 return dist[node.id()];
2335 };
2336
2337 var setDist = function setDist(node, d) {
2338 dist[node.id()] = d;
2339 Q.updateItem(node);
2340 };
2341
2342 var Q = new heap(function (a, b) {
2343 return getDist(a) - getDist(b);
2344 });
2345
2346 for (var i = 0; i < nodes.length; i++) {
2347 var node = nodes[i];
2348 dist[node.id()] = node.same(source) ? 0 : Infinity;
2349 Q.push(node);
2350 }
2351
2352 var distBetween = function distBetween(u, v) {
2353 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
2354 var smallestDistance = Infinity;
2355 var smallestEdge;
2356
2357 for (var _i = 0; _i < uvs.length; _i++) {
2358 var edge = uvs[_i];
2359
2360 var _weight = weightFn(edge);
2361
2362 if (_weight < smallestDistance || !smallestEdge) {
2363 smallestDistance = _weight;
2364 smallestEdge = edge;
2365 }
2366 }
2367
2368 return {
2369 edge: smallestEdge,
2370 dist: smallestDistance
2371 };
2372 };
2373
2374 while (Q.size() > 0) {
2375 var u = Q.pop();
2376 var smalletsDist = getDist(u);
2377 var uid = u.id();
2378 knownDist[uid] = smalletsDist;
2379
2380 if (smalletsDist === Infinity) {
2381 continue;
2382 }
2383
2384 var neighbors = u.neighborhood().intersect(nodes);
2385
2386 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
2387 var v = neighbors[_i2];
2388 var vid = v.id();
2389 var vDist = distBetween(u, v);
2390 var alt = smalletsDist + vDist.dist;
2391
2392 if (alt < getDist(v)) {
2393 setDist(v, alt);
2394 prev[vid] = {
2395 node: u,
2396 edge: vDist.edge
2397 };
2398 }
2399 } // for
2400
2401 } // while
2402
2403
2404 return {
2405 distanceTo: function distanceTo(node) {
2406 var target = string(node) ? nodes.filter(node)[0] : node[0];
2407 return knownDist[target.id()];
2408 },
2409 pathTo: function pathTo(node) {
2410 var target = string(node) ? nodes.filter(node)[0] : node[0];
2411 var S = [];
2412 var u = target;
2413 var uid = u.id();
2414
2415 if (target.length > 0) {
2416 S.unshift(target);
2417
2418 while (prev[uid]) {
2419 var p = prev[uid];
2420 S.unshift(p.edge);
2421 S.unshift(p.node);
2422 u = p.node;
2423 uid = u.id();
2424 }
2425 }
2426
2427 return eles.spawn(S);
2428 }
2429 };
2430 }
2431 };
2432
2433 var elesfn$t = {
2434 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
2435 // implemented from pseudocode from wikipedia
2436 kruskal: function kruskal(weightFn) {
2437 weightFn = weightFn || function (edge) {
2438 return 1;
2439 };
2440
2441 var _this$byGroup = this.byGroup(),
2442 nodes = _this$byGroup.nodes,
2443 edges = _this$byGroup.edges;
2444
2445 var numNodes = nodes.length;
2446 var forest = new Array(numNodes);
2447 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
2448
2449 var findSetIndex = function findSetIndex(ele) {
2450 for (var i = 0; i < forest.length; i++) {
2451 var eles = forest[i];
2452
2453 if (eles.has(ele)) {
2454 return i;
2455 }
2456 }
2457 }; // start with one forest per node
2458
2459
2460 for (var i = 0; i < numNodes; i++) {
2461 forest[i] = this.spawn(nodes[i]);
2462 }
2463
2464 var S = edges.sort(function (a, b) {
2465 return weightFn(a) - weightFn(b);
2466 });
2467
2468 for (var _i = 0; _i < S.length; _i++) {
2469 var edge = S[_i];
2470 var u = edge.source()[0];
2471 var v = edge.target()[0];
2472 var setUIndex = findSetIndex(u);
2473 var setVIndex = findSetIndex(v);
2474 var setU = forest[setUIndex];
2475 var setV = forest[setVIndex];
2476
2477 if (setUIndex !== setVIndex) {
2478 A.merge(edge); // combine forests for u and v
2479
2480 setU.merge(setV);
2481 forest.splice(setVIndex, 1);
2482 }
2483 }
2484
2485 return A;
2486 }
2487 };
2488
2489 var aStarDefaults = defaults$g({
2490 root: null,
2491 goal: null,
2492 weight: function weight(edge) {
2493 return 1;
2494 },
2495 heuristic: function heuristic(edge) {
2496 return 0;
2497 },
2498 directed: false
2499 });
2500 var elesfn$s = {
2501 // Implemented from pseudocode from wikipedia
2502 aStar: function aStar(options) {
2503 var cy = this.cy();
2504
2505 var _aStarDefaults = aStarDefaults(options),
2506 root = _aStarDefaults.root,
2507 goal = _aStarDefaults.goal,
2508 heuristic = _aStarDefaults.heuristic,
2509 directed = _aStarDefaults.directed,
2510 weight = _aStarDefaults.weight;
2511
2512 root = cy.collection(root)[0];
2513 goal = cy.collection(goal)[0];
2514 var sid = root.id();
2515 var tid = goal.id();
2516 var gScore = {};
2517 var fScore = {};
2518 var closedSetIds = {};
2519 var openSet = new heap(function (a, b) {
2520 return fScore[a.id()] - fScore[b.id()];
2521 });
2522 var openSetIds = new Set$1();
2523 var cameFrom = {};
2524 var cameFromEdge = {};
2525
2526 var addToOpenSet = function addToOpenSet(ele, id) {
2527 openSet.push(ele);
2528 openSetIds.add(id);
2529 };
2530
2531 var cMin, cMinId;
2532
2533 var popFromOpenSet = function popFromOpenSet() {
2534 cMin = openSet.pop();
2535 cMinId = cMin.id();
2536 openSetIds["delete"](cMinId);
2537 };
2538
2539 var isInOpenSet = function isInOpenSet(id) {
2540 return openSetIds.has(id);
2541 };
2542
2543 addToOpenSet(root, sid);
2544 gScore[sid] = 0;
2545 fScore[sid] = heuristic(root); // Counter
2546
2547 var steps = 0; // Main loop
2548
2549 while (openSet.size() > 0) {
2550 popFromOpenSet();
2551 steps++; // If we've found our goal, then we are done
2552
2553 if (cMinId === tid) {
2554 var path = [];
2555 var pathNode = goal;
2556 var pathNodeId = tid;
2557 var pathEdge = cameFromEdge[pathNodeId];
2558
2559 for (;;) {
2560 path.unshift(pathNode);
2561
2562 if (pathEdge != null) {
2563 path.unshift(pathEdge);
2564 }
2565
2566 pathNode = cameFrom[pathNodeId];
2567
2568 if (pathNode == null) {
2569 break;
2570 }
2571
2572 pathNodeId = pathNode.id();
2573 pathEdge = cameFromEdge[pathNodeId];
2574 }
2575
2576 return {
2577 found: true,
2578 distance: gScore[cMinId],
2579 path: this.spawn(path),
2580 steps: steps
2581 };
2582 } // Add cMin to processed nodes
2583
2584
2585 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
2586 // Take into account if graph is directed or not
2587
2588 var vwEdges = cMin._private.edges;
2589
2590 for (var i = 0; i < vwEdges.length; i++) {
2591 var e = vwEdges[i]; // edge must be in set of calling eles
2592
2593 if (!this.hasElementWithId(e.id())) {
2594 continue;
2595 } // cMin must be the source of edge if directed
2596
2597
2598 if (directed && e.data('source') !== cMinId) {
2599 continue;
2600 }
2601
2602 var wSrc = e.source();
2603 var wTgt = e.target();
2604 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
2605 var wid = w.id(); // node must be in set of calling eles
2606
2607 if (!this.hasElementWithId(wid)) {
2608 continue;
2609 } // if node is in closedSet, ignore it
2610
2611
2612 if (closedSetIds[wid]) {
2613 continue;
2614 } // New tentative score for node w
2615
2616
2617 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
2618 // w not present in openSet
2619 // OR
2620 // tentative gScore is less than previous value
2621 // w not in openSet
2622
2623 if (!isInOpenSet(wid)) {
2624 gScore[wid] = tempScore;
2625 fScore[wid] = tempScore + heuristic(w);
2626 addToOpenSet(w, wid);
2627 cameFrom[wid] = cMin;
2628 cameFromEdge[wid] = e;
2629 continue;
2630 } // w already in openSet, but with greater gScore
2631
2632
2633 if (tempScore < gScore[wid]) {
2634 gScore[wid] = tempScore;
2635 fScore[wid] = tempScore + heuristic(w);
2636 cameFrom[wid] = cMin;
2637 cameFromEdge[wid] = e;
2638 }
2639 } // End of neighbors update
2640
2641 } // End of main loop
2642 // If we've reached here, then we've not reached our goal
2643
2644
2645 return {
2646 found: false,
2647 distance: undefined,
2648 path: undefined,
2649 steps: steps
2650 };
2651 }
2652 }; // elesfn
2653
2654 var floydWarshallDefaults = defaults$g({
2655 weight: function weight(edge) {
2656 return 1;
2657 },
2658 directed: false
2659 });
2660 var elesfn$r = {
2661 // Implemented from pseudocode from wikipedia
2662 floydWarshall: function floydWarshall(options) {
2663 var cy = this.cy();
2664
2665 var _floydWarshallDefault = floydWarshallDefaults(options),
2666 weight = _floydWarshallDefault.weight,
2667 directed = _floydWarshallDefault.directed;
2668
2669 var weightFn = weight;
2670
2671 var _this$byGroup = this.byGroup(),
2672 nodes = _this$byGroup.nodes,
2673 edges = _this$byGroup.edges;
2674
2675 var N = nodes.length;
2676 var Nsq = N * N;
2677
2678 var indexOf = function indexOf(node) {
2679 return nodes.indexOf(node);
2680 };
2681
2682 var atIndex = function atIndex(i) {
2683 return nodes[i];
2684 }; // Initialize distance matrix
2685
2686
2687 var dist = new Array(Nsq);
2688
2689 for (var n = 0; n < Nsq; n++) {
2690 var j = n % N;
2691 var i = (n - j) / N;
2692
2693 if (i === j) {
2694 dist[n] = 0;
2695 } else {
2696 dist[n] = Infinity;
2697 }
2698 } // Initialize matrix used for path reconstruction
2699 // Initialize distance matrix
2700
2701
2702 var next = new Array(Nsq);
2703 var edgeNext = new Array(Nsq); // Process edges
2704
2705 for (var _i = 0; _i < edges.length; _i++) {
2706 var edge = edges[_i];
2707 var src = edge.source()[0];
2708 var tgt = edge.target()[0];
2709
2710 if (src === tgt) {
2711 continue;
2712 } // exclude loops
2713
2714
2715 var s = indexOf(src);
2716 var t = indexOf(tgt);
2717 var st = s * N + t; // source to target index
2718
2719 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
2720
2721
2722 if (dist[st] > _weight) {
2723 dist[st] = _weight;
2724 next[st] = t;
2725 edgeNext[st] = edge;
2726 } // If undirected graph, process 'reversed' edge
2727
2728
2729 if (!directed) {
2730 var ts = t * N + s; // target to source index
2731
2732 if (!directed && dist[ts] > _weight) {
2733 dist[ts] = _weight;
2734 next[ts] = s;
2735 edgeNext[ts] = edge;
2736 }
2737 }
2738 } // Main loop
2739
2740
2741 for (var k = 0; k < N; k++) {
2742 for (var _i2 = 0; _i2 < N; _i2++) {
2743 var ik = _i2 * N + k;
2744
2745 for (var _j = 0; _j < N; _j++) {
2746 var ij = _i2 * N + _j;
2747 var kj = k * N + _j;
2748
2749 if (dist[ik] + dist[kj] < dist[ij]) {
2750 dist[ij] = dist[ik] + dist[kj];
2751 next[ij] = next[ik];
2752 }
2753 }
2754 }
2755 }
2756
2757 var getArgEle = function getArgEle(ele) {
2758 return (string(ele) ? cy.filter(ele) : ele)[0];
2759 };
2760
2761 var indexOfArgEle = function indexOfArgEle(ele) {
2762 return indexOf(getArgEle(ele));
2763 };
2764
2765 var res = {
2766 distance: function distance(from, to) {
2767 var i = indexOfArgEle(from);
2768 var j = indexOfArgEle(to);
2769 return dist[i * N + j];
2770 },
2771 path: function path(from, to) {
2772 var i = indexOfArgEle(from);
2773 var j = indexOfArgEle(to);
2774 var fromNode = atIndex(i);
2775
2776 if (i === j) {
2777 return fromNode.collection();
2778 }
2779
2780 if (next[i * N + j] == null) {
2781 return cy.collection();
2782 }
2783
2784 var path = cy.collection();
2785 var prev = i;
2786 var edge;
2787 path.merge(fromNode);
2788
2789 while (i !== j) {
2790 prev = i;
2791 i = next[i * N + j];
2792 edge = edgeNext[prev * N + i];
2793 path.merge(edge);
2794 path.merge(atIndex(i));
2795 }
2796
2797 return path;
2798 }
2799 };
2800 return res;
2801 } // floydWarshall
2802
2803 }; // elesfn
2804
2805 var bellmanFordDefaults = defaults$g({
2806 weight: function weight(edge) {
2807 return 1;
2808 },
2809 directed: false,
2810 root: null
2811 });
2812 var elesfn$q = {
2813 // Implemented from pseudocode from wikipedia
2814 bellmanFord: function bellmanFord(options) {
2815 var _this = this;
2816
2817 var _bellmanFordDefaults = bellmanFordDefaults(options),
2818 weight = _bellmanFordDefaults.weight,
2819 directed = _bellmanFordDefaults.directed,
2820 root = _bellmanFordDefaults.root;
2821
2822 var weightFn = weight;
2823 var eles = this;
2824 var cy = this.cy();
2825
2826 var _this$byGroup = this.byGroup(),
2827 edges = _this$byGroup.edges,
2828 nodes = _this$byGroup.nodes;
2829
2830 var numNodes = nodes.length;
2831 var infoMap = new Map$2();
2832 var hasNegativeWeightCycle = false;
2833 var negativeWeightCycles = [];
2834 root = cy.collection(root)[0]; // in case selector passed
2835
2836 edges.unmergeBy(function (edge) {
2837 return edge.isLoop();
2838 });
2839 var numEdges = edges.length;
2840
2841 var getInfo = function getInfo(node) {
2842 var obj = infoMap.get(node.id());
2843
2844 if (!obj) {
2845 obj = {};
2846 infoMap.set(node.id(), obj);
2847 }
2848
2849 return obj;
2850 };
2851
2852 var getNodeFromTo = function getNodeFromTo(to) {
2853 return (string(to) ? cy.$(to) : to)[0];
2854 };
2855
2856 var distanceTo = function distanceTo(to) {
2857 return getInfo(getNodeFromTo(to)).dist;
2858 };
2859
2860 var pathTo = function pathTo(to) {
2861 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
2862 var end = getNodeFromTo(to);
2863 var path = [];
2864 var node = end;
2865
2866 for (;;) {
2867 if (node == null) {
2868 return _this.spawn();
2869 }
2870
2871 var _getInfo = getInfo(node),
2872 edge = _getInfo.edge,
2873 pred = _getInfo.pred;
2874
2875 path.unshift(node[0]);
2876
2877 if (node.same(thisStart) && path.length > 0) {
2878 break;
2879 }
2880
2881 if (edge != null) {
2882 path.unshift(edge);
2883 }
2884
2885 node = pred;
2886 }
2887
2888 return eles.spawn(path);
2889 }; // Initializations { dist, pred, edge }
2890
2891
2892 for (var i = 0; i < numNodes; i++) {
2893 var node = nodes[i];
2894 var info = getInfo(node);
2895
2896 if (node.same(root)) {
2897 info.dist = 0;
2898 } else {
2899 info.dist = Infinity;
2900 }
2901
2902 info.pred = null;
2903 info.edge = null;
2904 } // Edges relaxation
2905
2906
2907 var replacedEdge = false;
2908
2909 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2910 var dist = info1.dist + weight;
2911
2912 if (dist < info2.dist && !edge.same(info1.edge)) {
2913 info2.dist = dist;
2914 info2.pred = node1;
2915 info2.edge = edge;
2916 replacedEdge = true;
2917 }
2918 };
2919
2920 for (var _i = 1; _i < numNodes; _i++) {
2921 replacedEdge = false;
2922
2923 for (var e = 0; e < numEdges; e++) {
2924 var edge = edges[e];
2925 var src = edge.source();
2926 var tgt = edge.target();
2927
2928 var _weight = weightFn(edge);
2929
2930 var srcInfo = getInfo(src);
2931 var tgtInfo = getInfo(tgt);
2932 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2933
2934 if (!directed) {
2935 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2936 }
2937 }
2938
2939 if (!replacedEdge) {
2940 break;
2941 }
2942 }
2943
2944 if (replacedEdge) {
2945 // Check for negative weight cycles
2946 var negativeWeightCycleIds = [];
2947
2948 for (var _e = 0; _e < numEdges; _e++) {
2949 var _edge = edges[_e];
2950
2951 var _src = _edge.source();
2952
2953 var _tgt = _edge.target();
2954
2955 var _weight2 = weightFn(_edge);
2956
2957 var srcDist = getInfo(_src).dist;
2958 var tgtDist = getInfo(_tgt).dist;
2959
2960 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2961 if (!hasNegativeWeightCycle) {
2962 warn('Graph contains a negative weight cycle for Bellman-Ford');
2963 hasNegativeWeightCycle = true;
2964 }
2965
2966 if (options.findNegativeWeightCycles !== false) {
2967 var negativeNodes = [];
2968
2969 if (srcDist + _weight2 < tgtDist) {
2970 negativeNodes.push(_src);
2971 }
2972
2973 if (!directed && tgtDist + _weight2 < srcDist) {
2974 negativeNodes.push(_tgt);
2975 }
2976
2977 var numNegativeNodes = negativeNodes.length;
2978
2979 for (var n = 0; n < numNegativeNodes; n++) {
2980 var start = negativeNodes[n];
2981 var cycle = [start];
2982 cycle.push(getInfo(start).edge);
2983 var _node = getInfo(start).pred;
2984
2985 while (cycle.indexOf(_node) === -1) {
2986 cycle.push(_node);
2987 cycle.push(getInfo(_node).edge);
2988 _node = getInfo(_node).pred;
2989 }
2990
2991 cycle = cycle.slice(cycle.indexOf(_node));
2992 var smallestId = cycle[0].id();
2993 var smallestIndex = 0;
2994
2995 for (var c = 2; c < cycle.length; c += 2) {
2996 if (cycle[c].id() < smallestId) {
2997 smallestId = cycle[c].id();
2998 smallestIndex = c;
2999 }
3000 }
3001
3002 cycle = cycle.slice(smallestIndex).concat(cycle.slice(0, smallestIndex));
3003 cycle.push(cycle[0]);
3004 var cycleId = cycle.map(function (el) {
3005 return el.id();
3006 }).join(",");
3007
3008 if (negativeWeightCycleIds.indexOf(cycleId) === -1) {
3009 negativeWeightCycles.push(eles.spawn(cycle));
3010 negativeWeightCycleIds.push(cycleId);
3011 }
3012 }
3013 } else {
3014 break;
3015 }
3016 }
3017 }
3018 }
3019
3020 return {
3021 distanceTo: distanceTo,
3022 pathTo: pathTo,
3023 hasNegativeWeightCycle: hasNegativeWeightCycle,
3024 negativeWeightCycles: negativeWeightCycles
3025 };
3026 } // bellmanFord
3027
3028 }; // elesfn
3029
3030 var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
3031 // Updates the remaining edge lists
3032 // Receives as a paramater the edge which causes the collapse
3033
3034 var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
3035 if (remainingEdges.length === 0) {
3036 error("Karger-Stein must be run on a connected (sub)graph");
3037 }
3038
3039 var edgeInfo = remainingEdges[edgeIndex];
3040 var sourceIn = edgeInfo[1];
3041 var targetIn = edgeInfo[2];
3042 var partition1 = nodeMap[sourceIn];
3043 var partition2 = nodeMap[targetIn];
3044 var newEdges = remainingEdges; // re-use array
3045 // Delete all edges between partition1 and partition2
3046
3047 for (var i = newEdges.length - 1; i >= 0; i--) {
3048 var edge = newEdges[i];
3049 var src = edge[1];
3050 var tgt = edge[2];
3051
3052 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
3053 newEdges.splice(i, 1);
3054 }
3055 } // All edges pointing to partition2 should now point to partition1
3056
3057
3058 for (var _i = 0; _i < newEdges.length; _i++) {
3059 var _edge = newEdges[_i];
3060
3061 if (_edge[1] === partition2) {
3062 // Check source
3063 newEdges[_i] = _edge.slice(); // copy
3064
3065 newEdges[_i][1] = partition1;
3066 } else if (_edge[2] === partition2) {
3067 // Check target
3068 newEdges[_i] = _edge.slice(); // copy
3069
3070 newEdges[_i][2] = partition1;
3071 }
3072 } // Move all nodes from partition2 to partition1
3073
3074
3075 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
3076 if (nodeMap[_i2] === partition2) {
3077 nodeMap[_i2] = partition1;
3078 }
3079 }
3080
3081 return newEdges;
3082 }; // Contracts a graph until we reach a certain number of meta nodes
3083
3084
3085 var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
3086 while (size > sizeLimit) {
3087 // Choose an edge randomly
3088 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
3089
3090 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
3091 size--;
3092 }
3093
3094 return remainingEdges;
3095 };
3096
3097 var elesfn$p = {
3098 // Computes the minimum cut of an undirected graph
3099 // Returns the correct answer with high probability
3100 kargerStein: function kargerStein() {
3101 var _this = this;
3102
3103 var _this$byGroup = this.byGroup(),
3104 nodes = _this$byGroup.nodes,
3105 edges = _this$byGroup.edges;
3106
3107 edges.unmergeBy(function (edge) {
3108 return edge.isLoop();
3109 });
3110 var numNodes = nodes.length;
3111 var numEdges = edges.length;
3112 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
3113 var stopSize = Math.floor(numNodes / sqrt2);
3114
3115 if (numNodes < 2) {
3116 error('At least 2 nodes are required for Karger-Stein algorithm');
3117 return undefined;
3118 } // Now store edge destination as indexes
3119 // Format for each edge (edge index, source node index, target node index)
3120
3121
3122 var edgeIndexes = [];
3123
3124 for (var i = 0; i < numEdges; i++) {
3125 var e = edges[i];
3126 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
3127 } // We will store the best cut found here
3128
3129
3130 var minCutSize = Infinity;
3131 var minCutEdgeIndexes = [];
3132 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
3133
3134 var metaNodeMap = new Array(numNodes);
3135 var metaNodeMap2 = new Array(numNodes);
3136
3137 var copyNodesMap = function copyNodesMap(from, to) {
3138 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3139 to[_i3] = from[_i3];
3140 }
3141 }; // Main loop
3142
3143
3144 for (var iter = 0; iter <= numIter; iter++) {
3145 // Reset meta node partition
3146 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3147 metaNodeMap[_i4] = _i4;
3148 } // Contract until stop point (stopSize nodes)
3149
3150
3151 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
3152 var edgesState2 = edgesState.slice(); // copy
3153 // Create a copy of the colapsed nodes state
3154
3155 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
3156
3157 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
3158 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
3159
3160 if (res1.length <= res2.length && res1.length < minCutSize) {
3161 minCutSize = res1.length;
3162 minCutEdgeIndexes = res1;
3163 copyNodesMap(metaNodeMap, minCutNodeMap);
3164 } else if (res2.length <= res1.length && res2.length < minCutSize) {
3165 minCutSize = res2.length;
3166 minCutEdgeIndexes = res2;
3167 copyNodesMap(metaNodeMap2, minCutNodeMap);
3168 }
3169 } // end of main loop
3170 // Construct result
3171
3172
3173 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
3174 return edges[e[0]];
3175 }));
3176 var partition1 = this.spawn();
3177 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
3178
3179 var witnessNodePartition = minCutNodeMap[0];
3180
3181 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
3182 var partitionId = minCutNodeMap[_i5];
3183 var node = nodes[_i5];
3184
3185 if (partitionId === witnessNodePartition) {
3186 partition1.merge(node);
3187 } else {
3188 partition2.merge(node);
3189 }
3190 } // construct components corresponding to each disjoint subset of nodes
3191
3192
3193 var constructComponent = function constructComponent(subset) {
3194 var component = _this.spawn();
3195
3196 subset.forEach(function (node) {
3197 component.merge(node);
3198 node.connectedEdges().forEach(function (edge) {
3199 // ensure edge is within calling collection and edge is not in cut
3200 if (_this.contains(edge) && !cut.contains(edge)) {
3201 component.merge(edge);
3202 }
3203 });
3204 });
3205 return component;
3206 };
3207
3208 var components = [constructComponent(partition1), constructComponent(partition2)];
3209 var ret = {
3210 cut: cut,
3211 components: components,
3212 // n.b. partitions are included to be compatible with the old api spec
3213 // (could be removed in a future major version)
3214 partition1: partition1,
3215 partition2: partition2
3216 };
3217 return ret;
3218 }
3219 }; // elesfn
3220
3221 var copyPosition = function copyPosition(p) {
3222 return {
3223 x: p.x,
3224 y: p.y
3225 };
3226 };
3227 var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
3228 return {
3229 x: p.x * zoom + pan.x,
3230 y: p.y * zoom + pan.y
3231 };
3232 };
3233 var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
3234 return {
3235 x: (p.x - pan.x) / zoom,
3236 y: (p.y - pan.y) / zoom
3237 };
3238 };
3239 var array2point = function array2point(arr) {
3240 return {
3241 x: arr[0],
3242 y: arr[1]
3243 };
3244 };
3245 var min = function min(arr) {
3246 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3247 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3248 var min = Infinity;
3249
3250 for (var i = begin; i < end; i++) {
3251 var val = arr[i];
3252
3253 if (isFinite(val)) {
3254 min = Math.min(val, min);
3255 }
3256 }
3257
3258 return min;
3259 };
3260 var max = function max(arr) {
3261 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3262 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3263 var max = -Infinity;
3264
3265 for (var i = begin; i < end; i++) {
3266 var val = arr[i];
3267
3268 if (isFinite(val)) {
3269 max = Math.max(val, max);
3270 }
3271 }
3272
3273 return max;
3274 };
3275 var mean = function mean(arr) {
3276 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3277 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3278 var total = 0;
3279 var n = 0;
3280
3281 for (var i = begin; i < end; i++) {
3282 var val = arr[i];
3283
3284 if (isFinite(val)) {
3285 total += val;
3286 n++;
3287 }
3288 }
3289
3290 return total / n;
3291 };
3292 var median = function median(arr) {
3293 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3294 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3295 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
3296 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
3297 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
3298
3299 if (copy) {
3300 arr = arr.slice(begin, end);
3301 } else {
3302 if (end < arr.length) {
3303 arr.splice(end, arr.length - end);
3304 }
3305
3306 if (begin > 0) {
3307 arr.splice(0, begin);
3308 }
3309 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
3310
3311
3312 var off = 0; // offset from non-finite values
3313
3314 for (var i = arr.length - 1; i >= 0; i--) {
3315 var v = arr[i];
3316
3317 if (includeHoles) {
3318 if (!isFinite(v)) {
3319 arr[i] = -Infinity;
3320 off++;
3321 }
3322 } else {
3323 // just remove it if we don't want to consider holes
3324 arr.splice(i, 1);
3325 }
3326 }
3327
3328 if (sort) {
3329 arr.sort(function (a, b) {
3330 return a - b;
3331 }); // requires copy = true if you don't want to change the orig
3332 }
3333
3334 var len = arr.length;
3335 var mid = Math.floor(len / 2);
3336
3337 if (len % 2 !== 0) {
3338 return arr[mid + 1 + off];
3339 } else {
3340 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
3341 }
3342 };
3343 var deg2rad = function deg2rad(deg) {
3344 return Math.PI * deg / 180;
3345 };
3346 var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
3347 return Math.atan2(dispY, dispX) - Math.PI / 2;
3348 };
3349 var log2 = Math.log2 || function (n) {
3350 return Math.log(n) / Math.log(2);
3351 };
3352 var signum = function signum(x) {
3353 if (x > 0) {
3354 return 1;
3355 } else if (x < 0) {
3356 return -1;
3357 } else {
3358 return 0;
3359 }
3360 };
3361 var dist = function dist(p1, p2) {
3362 return Math.sqrt(sqdist(p1, p2));
3363 };
3364 var sqdist = function sqdist(p1, p2) {
3365 var dx = p2.x - p1.x;
3366 var dy = p2.y - p1.y;
3367 return dx * dx + dy * dy;
3368 };
3369 var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
3370 var length = v.length; // First, get sum of all elements
3371
3372 var total = 0;
3373
3374 for (var i = 0; i < length; i++) {
3375 total += v[i];
3376 } // Now, divide each by the sum of all elements
3377
3378
3379 for (var _i = 0; _i < length; _i++) {
3380 v[_i] = v[_i] / total;
3381 }
3382
3383 return v;
3384 };
3385
3386 var qbezierAt = function qbezierAt(p0, p1, p2, t) {
3387 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
3388 };
3389 var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
3390 return {
3391 x: qbezierAt(p0.x, p1.x, p2.x, t),
3392 y: qbezierAt(p0.y, p1.y, p2.y, t)
3393 };
3394 };
3395 var lineAt = function lineAt(p0, p1, t, d) {
3396 var vec = {
3397 x: p1.x - p0.x,
3398 y: p1.y - p0.y
3399 };
3400 var vecDist = dist(p0, p1);
3401 var normVec = {
3402 x: vec.x / vecDist,
3403 y: vec.y / vecDist
3404 };
3405 t = t == null ? 0 : t;
3406 d = d != null ? d : t * vecDist;
3407 return {
3408 x: p0.x + normVec.x * d,
3409 y: p0.y + normVec.y * d
3410 };
3411 };
3412 var bound = function bound(min, val, max) {
3413 return Math.max(min, Math.min(max, val));
3414 }; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
3415
3416 var makeBoundingBox = function makeBoundingBox(bb) {
3417 if (bb == null) {
3418 return {
3419 x1: Infinity,
3420 y1: Infinity,
3421 x2: -Infinity,
3422 y2: -Infinity,
3423 w: 0,
3424 h: 0
3425 };
3426 } else if (bb.x1 != null && bb.y1 != null) {
3427 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
3428 return {
3429 x1: bb.x1,
3430 y1: bb.y1,
3431 x2: bb.x2,
3432 y2: bb.y2,
3433 w: bb.x2 - bb.x1,
3434 h: bb.y2 - bb.y1
3435 };
3436 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
3437 return {
3438 x1: bb.x1,
3439 y1: bb.y1,
3440 x2: bb.x1 + bb.w,
3441 y2: bb.y1 + bb.h,
3442 w: bb.w,
3443 h: bb.h
3444 };
3445 }
3446 }
3447 };
3448 var copyBoundingBox = function copyBoundingBox(bb) {
3449 return {
3450 x1: bb.x1,
3451 x2: bb.x2,
3452 w: bb.w,
3453 y1: bb.y1,
3454 y2: bb.y2,
3455 h: bb.h
3456 };
3457 };
3458 var clearBoundingBox = function clearBoundingBox(bb) {
3459 bb.x1 = Infinity;
3460 bb.y1 = Infinity;
3461 bb.x2 = -Infinity;
3462 bb.y2 = -Infinity;
3463 bb.w = 0;
3464 bb.h = 0;
3465 };
3466 var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
3467 // update bb1 with bb2 bounds
3468 bb1.x1 = Math.min(bb1.x1, bb2.x1);
3469 bb1.x2 = Math.max(bb1.x2, bb2.x2);
3470 bb1.w = bb1.x2 - bb1.x1;
3471 bb1.y1 = Math.min(bb1.y1, bb2.y1);
3472 bb1.y2 = Math.max(bb1.y2, bb2.y2);
3473 bb1.h = bb1.y2 - bb1.y1;
3474 };
3475 var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
3476 bb.x1 = Math.min(bb.x1, x);
3477 bb.x2 = Math.max(bb.x2, x);
3478 bb.w = bb.x2 - bb.x1;
3479 bb.y1 = Math.min(bb.y1, y);
3480 bb.y2 = Math.max(bb.y2, y);
3481 bb.h = bb.y2 - bb.y1;
3482 };
3483 var expandBoundingBox = function expandBoundingBox(bb) {
3484 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3485 bb.x1 -= padding;
3486 bb.x2 += padding;
3487 bb.y1 -= padding;
3488 bb.y2 += padding;
3489 bb.w = bb.x2 - bb.x1;
3490 bb.h = bb.y2 - bb.y1;
3491 return bb;
3492 };
3493 var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
3494 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
3495 var top, right, bottom, left;
3496
3497 if (padding.length === 1) {
3498 top = right = bottom = left = padding[0];
3499 } else if (padding.length === 2) {
3500 top = bottom = padding[0];
3501 left = right = padding[1];
3502 } else if (padding.length === 4) {
3503 var _padding = _slicedToArray(padding, 4);
3504
3505 top = _padding[0];
3506 right = _padding[1];
3507 bottom = _padding[2];
3508 left = _padding[3];
3509 }
3510
3511 bb.x1 -= left;
3512 bb.x2 += right;
3513 bb.y1 -= top;
3514 bb.y2 += bottom;
3515 bb.w = bb.x2 - bb.x1;
3516 bb.h = bb.y2 - bb.y1;
3517 return bb;
3518 };
3519
3520 var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
3521 bb1.x1 = bb2.x1;
3522 bb1.y1 = bb2.y1;
3523 bb1.x2 = bb2.x2;
3524 bb1.y2 = bb2.y2;
3525 bb1.w = bb1.x2 - bb1.x1;
3526 bb1.h = bb1.y2 - bb1.y1;
3527 };
3528 var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
3529 // case: one bb to right of other
3530 if (bb1.x1 > bb2.x2) {
3531 return false;
3532 }
3533
3534 if (bb2.x1 > bb1.x2) {
3535 return false;
3536 } // case: one bb to left of other
3537
3538
3539 if (bb1.x2 < bb2.x1) {
3540 return false;
3541 }
3542
3543 if (bb2.x2 < bb1.x1) {
3544 return false;
3545 } // case: one bb above other
3546
3547
3548 if (bb1.y2 < bb2.y1) {
3549 return false;
3550 }
3551
3552 if (bb2.y2 < bb1.y1) {
3553 return false;
3554 } // case: one bb below other
3555
3556
3557 if (bb1.y1 > bb2.y2) {
3558 return false;
3559 }
3560
3561 if (bb2.y1 > bb1.y2) {
3562 return false;
3563 } // otherwise, must have some overlap
3564
3565
3566 return true;
3567 };
3568 var inBoundingBox = function inBoundingBox(bb, x, y) {
3569 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
3570 };
3571 var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
3572 return inBoundingBox(bb, pt.x, pt.y);
3573 };
3574 var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
3575 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
3576 };
3577 var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
3578 var cornerRadius = getRoundRectangleRadius(width, height);
3579 var halfWidth = width / 2;
3580 var halfHeight = height / 2; // Check intersections with straight line segments
3581
3582 var straightLineIntersections; // Top segment, left to right
3583
3584 {
3585 var topStartX = nodeX - halfWidth + cornerRadius - padding;
3586 var topStartY = nodeY - halfHeight - padding;
3587 var topEndX = nodeX + halfWidth - cornerRadius + padding;
3588 var topEndY = topStartY;
3589 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
3590
3591 if (straightLineIntersections.length > 0) {
3592 return straightLineIntersections;
3593 }
3594 } // Right segment, top to bottom
3595
3596 {
3597 var rightStartX = nodeX + halfWidth + padding;
3598 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
3599 var rightEndX = rightStartX;
3600 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
3601 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
3602
3603 if (straightLineIntersections.length > 0) {
3604 return straightLineIntersections;
3605 }
3606 } // Bottom segment, left to right
3607
3608 {
3609 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
3610 var bottomStartY = nodeY + halfHeight + padding;
3611 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
3612 var bottomEndY = bottomStartY;
3613 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
3614
3615 if (straightLineIntersections.length > 0) {
3616 return straightLineIntersections;
3617 }
3618 } // Left segment, top to bottom
3619
3620 {
3621 var leftStartX = nodeX - halfWidth - padding;
3622 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
3623 var leftEndX = leftStartX;
3624 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
3625 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
3626
3627 if (straightLineIntersections.length > 0) {
3628 return straightLineIntersections;
3629 }
3630 } // Check intersections with arc segments
3631
3632 var arcIntersections; // Top Left
3633
3634 {
3635 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
3636 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
3637 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3638
3639 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
3640 return [arcIntersections[0], arcIntersections[1]];
3641 }
3642 } // Top Right
3643
3644 {
3645 var topRightCenterX = nodeX + halfWidth - cornerRadius;
3646 var topRightCenterY = nodeY - halfHeight + cornerRadius;
3647 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3648
3649 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
3650 return [arcIntersections[0], arcIntersections[1]];
3651 }
3652 } // Bottom Right
3653
3654 {
3655 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
3656 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
3657 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3658
3659 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
3660 return [arcIntersections[0], arcIntersections[1]];
3661 }
3662 } // Bottom Left
3663
3664 {
3665 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
3666 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
3667 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3668
3669 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
3670 return [arcIntersections[0], arcIntersections[1]];
3671 }
3672 }
3673 return []; // if nothing
3674 };
3675 var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
3676 var t = tolerance;
3677 var x1 = Math.min(lx1, lx2);
3678 var x2 = Math.max(lx1, lx2);
3679 var y1 = Math.min(ly1, ly2);
3680 var y2 = Math.max(ly1, ly2);
3681 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
3682 };
3683 var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
3684 var bb = {
3685 x1: Math.min(x1, x3, x2) - tolerance,
3686 x2: Math.max(x1, x3, x2) + tolerance,
3687 y1: Math.min(y1, y3, y2) - tolerance,
3688 y2: Math.max(y1, y3, y2) + tolerance
3689 }; // if outside the rough bounding box for the bezier, then it can't be a hit
3690
3691 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
3692 // console.log('bezier out of rough bb')
3693 return false;
3694 } else {
3695 // console.log('do more expensive check');
3696 return true;
3697 }
3698 };
3699 var solveQuadratic = function solveQuadratic(a, b, c, val) {
3700 c -= val;
3701 var r = b * b - 4 * a * c;
3702
3703 if (r < 0) {
3704 return [];
3705 }
3706
3707 var sqrtR = Math.sqrt(r);
3708 var denom = 2 * a;
3709 var root1 = (-b + sqrtR) / denom;
3710 var root2 = (-b - sqrtR) / denom;
3711 return [root1, root2];
3712 };
3713 var solveCubic = function solveCubic(a, b, c, d, result) {
3714 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
3715 // r is the real component, i is the imaginary component
3716 // An implementation of the Cardano method from the year 1545
3717 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
3718 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
3719
3720 if (a === 0) {
3721 a = epsilon;
3722 }
3723
3724 b /= a;
3725 c /= a;
3726 d /= a;
3727 var discriminant, q, r, dum1, s, t, term1, r13;
3728 q = (3.0 * c - b * b) / 9.0;
3729 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
3730 r /= 54.0;
3731 discriminant = q * q * q + r * r;
3732 result[1] = 0;
3733 term1 = b / 3.0;
3734
3735 if (discriminant > 0) {
3736 s = r + Math.sqrt(discriminant);
3737 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
3738 t = r - Math.sqrt(discriminant);
3739 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
3740 result[0] = -term1 + s + t;
3741 term1 += (s + t) / 2.0;
3742 result[4] = result[2] = -term1;
3743 term1 = Math.sqrt(3.0) * (-t + s) / 2;
3744 result[3] = term1;
3745 result[5] = -term1;
3746 return;
3747 }
3748
3749 result[5] = result[3] = 0;
3750
3751 if (discriminant === 0) {
3752 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
3753 result[0] = -term1 + 2.0 * r13;
3754 result[4] = result[2] = -(r13 + term1);
3755 return;
3756 }
3757
3758 q = -q;
3759 dum1 = q * q * q;
3760 dum1 = Math.acos(r / Math.sqrt(dum1));
3761 r13 = 2.0 * Math.sqrt(q);
3762 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
3763 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
3764 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
3765 return;
3766 };
3767 var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
3768 // Find minimum distance by using the minimum of the distance
3769 // function between the given point and the curve
3770 // This gives the coefficients of the resulting cubic equation
3771 // whose roots tell us where a possible minimum is
3772 // (Coefficients are divided by 4)
3773 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;
3774 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;
3775 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;
3776 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);
3777
3778 var roots = []; // Use the cubic solving algorithm
3779
3780 solveCubic(a, b, c, d, roots);
3781 var zeroThreshold = 0.0000001;
3782 var params = [];
3783
3784 for (var index = 0; index < 6; index += 2) {
3785 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
3786 params.push(roots[index]);
3787 }
3788 }
3789
3790 params.push(1.0);
3791 params.push(0.0);
3792 var minDistanceSquared = -1;
3793 var curX, curY, distSquared;
3794
3795 for (var i = 0; i < params.length; i++) {
3796 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
3797 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
3798 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
3799
3800 if (minDistanceSquared >= 0) {
3801 if (distSquared < minDistanceSquared) {
3802 minDistanceSquared = distSquared;
3803 }
3804 } else {
3805 minDistanceSquared = distSquared;
3806 }
3807 }
3808
3809 return minDistanceSquared;
3810 };
3811 var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
3812 var offset = [x - x1, y - y1];
3813 var line = [x2 - x1, y2 - y1];
3814 var lineSq = line[0] * line[0] + line[1] * line[1];
3815 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
3816 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
3817 var adjSq = dotProduct * dotProduct / lineSq;
3818
3819 if (dotProduct < 0) {
3820 return hypSq;
3821 }
3822
3823 if (adjSq > lineSq) {
3824 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
3825 }
3826
3827 return hypSq - adjSq;
3828 };
3829 var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
3830 var x1, y1, x2, y2;
3831 var y3; // Intersect with vertical line through (x, y)
3832
3833 var up = 0; // let down = 0;
3834
3835 for (var i = 0; i < points.length / 2; i++) {
3836 x1 = points[i * 2];
3837 y1 = points[i * 2 + 1];
3838
3839 if (i + 1 < points.length / 2) {
3840 x2 = points[(i + 1) * 2];
3841 y2 = points[(i + 1) * 2 + 1];
3842 } else {
3843 x2 = points[(i + 1 - points.length / 2) * 2];
3844 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
3845 }
3846
3847 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
3848 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
3849
3850 if (y3 > y) {
3851 up++;
3852 } // if( y3 < y ){
3853 // down++;
3854 // }
3855
3856 } else {
3857 continue;
3858 }
3859 }
3860
3861 if (up % 2 === 0) {
3862 return false;
3863 } else {
3864 return true;
3865 }
3866 };
3867 var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
3868 var transformedPoints = new Array(basePoints.length); // Gives negative angle
3869
3870 var angle;
3871
3872 if (direction[0] != null) {
3873 angle = Math.atan(direction[1] / direction[0]);
3874
3875 if (direction[0] < 0) {
3876 angle = angle + Math.PI / 2;
3877 } else {
3878 angle = -angle - Math.PI / 2;
3879 }
3880 } else {
3881 angle = direction;
3882 }
3883
3884 var cos = Math.cos(-angle);
3885 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
3886
3887 for (var i = 0; i < transformedPoints.length / 2; i++) {
3888 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
3889 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
3890 transformedPoints[i * 2] += centerX;
3891 transformedPoints[i * 2 + 1] += centerY;
3892 }
3893
3894 var points;
3895
3896 if (padding > 0) {
3897 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3898 points = joinLines(expandedLineSet);
3899 } else {
3900 points = transformedPoints;
3901 }
3902
3903 return pointInsidePolygonPoints(x, y, points);
3904 };
3905 var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
3906 var cutPolygonPoints = new Array(basePoints.length);
3907 var halfW = width / 2;
3908 var halfH = height / 2;
3909 var cornerRadius = getRoundPolygonRadius(width, height);
3910 var squaredCornerRadius = cornerRadius * cornerRadius;
3911
3912 for (var i = 0; i < basePoints.length / 4; i++) {
3913 var sourceUv = void 0,
3914 destUv = void 0;
3915
3916 if (i === 0) {
3917 sourceUv = basePoints.length - 2;
3918 } else {
3919 sourceUv = i * 4 - 2;
3920 }
3921
3922 destUv = i * 4 + 2;
3923 var px = centerX + halfW * basePoints[i * 4];
3924 var py = centerY + halfH * basePoints[i * 4 + 1];
3925 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3926 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3927 var cp0x = px - offset * basePoints[sourceUv];
3928 var cp0y = py - offset * basePoints[sourceUv + 1];
3929 var cp1x = px + offset * basePoints[destUv];
3930 var cp1y = py + offset * basePoints[destUv + 1];
3931 cutPolygonPoints[i * 4] = cp0x;
3932 cutPolygonPoints[i * 4 + 1] = cp0y;
3933 cutPolygonPoints[i * 4 + 2] = cp1x;
3934 cutPolygonPoints[i * 4 + 3] = cp1y;
3935 var orthx = basePoints[sourceUv + 1];
3936 var orthy = -basePoints[sourceUv];
3937 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3938
3939 if (cosAlpha < 0) {
3940 orthx *= -1;
3941 orthy *= -1;
3942 }
3943
3944 var cx = cp0x + orthx * cornerRadius;
3945 var cy = cp0y + orthy * cornerRadius;
3946 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
3947
3948 if (squaredDistance <= squaredCornerRadius) {
3949 return true;
3950 }
3951 }
3952
3953 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3954 };
3955 var joinLines = function joinLines(lineSet) {
3956 var vertices = new Array(lineSet.length / 2);
3957 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3958 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3959
3960 for (var i = 0; i < lineSet.length / 4; i++) {
3961 currentLineStartX = lineSet[i * 4];
3962 currentLineStartY = lineSet[i * 4 + 1];
3963 currentLineEndX = lineSet[i * 4 + 2];
3964 currentLineEndY = lineSet[i * 4 + 3];
3965
3966 if (i < lineSet.length / 4 - 1) {
3967 nextLineStartX = lineSet[(i + 1) * 4];
3968 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3969 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3970 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3971 } else {
3972 nextLineStartX = lineSet[0];
3973 nextLineStartY = lineSet[1];
3974 nextLineEndX = lineSet[2];
3975 nextLineEndY = lineSet[3];
3976 }
3977
3978 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3979 vertices[i * 2] = intersection[0];
3980 vertices[i * 2 + 1] = intersection[1];
3981 }
3982
3983 return vertices;
3984 };
3985 var expandPolygon = function expandPolygon(points, pad) {
3986 var expandedLineSet = new Array(points.length * 2);
3987 var currentPointX, currentPointY, nextPointX, nextPointY;
3988
3989 for (var i = 0; i < points.length / 2; i++) {
3990 currentPointX = points[i * 2];
3991 currentPointY = points[i * 2 + 1];
3992
3993 if (i < points.length / 2 - 1) {
3994 nextPointX = points[(i + 1) * 2];
3995 nextPointY = points[(i + 1) * 2 + 1];
3996 } else {
3997 nextPointX = points[0];
3998 nextPointY = points[1];
3999 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
4000 // Assume CCW polygon winding
4001
4002
4003 var offsetX = nextPointY - currentPointY;
4004 var offsetY = -(nextPointX - currentPointX); // Normalize
4005
4006 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
4007 var normalizedOffsetX = offsetX / offsetLength;
4008 var normalizedOffsetY = offsetY / offsetLength;
4009 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
4010 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
4011 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
4012 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
4013 }
4014
4015 return expandedLineSet;
4016 };
4017 var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
4018 var dispX = centerX - x;
4019 var dispY = centerY - y;
4020 dispX /= ellipseWradius;
4021 dispY /= ellipseHradius;
4022 var len = Math.sqrt(dispX * dispX + dispY * dispY);
4023 var newLength = len - 1;
4024
4025 if (newLength < 0) {
4026 return [];
4027 }
4028
4029 var lenProportion = newLength / len;
4030 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
4031 };
4032 var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
4033 x -= centerX;
4034 y -= centerY;
4035 x /= width / 2 + padding;
4036 y /= height / 2 + padding;
4037 return x * x + y * y <= 1;
4038 }; // Returns intersections of increasing distance from line's start point
4039
4040 var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
4041 // Calculate d, direction vector of line
4042 var d = [x2 - x1, y2 - y1]; // Direction vector of line
4043
4044 var f = [x1 - centerX, y1 - centerY];
4045 var a = d[0] * d[0] + d[1] * d[1];
4046 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
4047 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
4048 var discriminant = b * b - 4 * a * c;
4049
4050 if (discriminant < 0) {
4051 return [];
4052 }
4053
4054 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
4055 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
4056 var tMin = Math.min(t1, t2);
4057 var tMax = Math.max(t1, t2);
4058 var inRangeParams = [];
4059
4060 if (tMin >= 0 && tMin <= 1) {
4061 inRangeParams.push(tMin);
4062 }
4063
4064 if (tMax >= 0 && tMax <= 1) {
4065 inRangeParams.push(tMax);
4066 }
4067
4068 if (inRangeParams.length === 0) {
4069 return [];
4070 }
4071
4072 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
4073 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
4074
4075 if (inRangeParams.length > 1) {
4076 if (inRangeParams[0] == inRangeParams[1]) {
4077 return [nearIntersectionX, nearIntersectionY];
4078 } else {
4079 var farIntersectionX = inRangeParams[1] * d[0] + x1;
4080 var farIntersectionY = inRangeParams[1] * d[1] + y1;
4081 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
4082 }
4083 } else {
4084 return [nearIntersectionX, nearIntersectionY];
4085 }
4086 };
4087 var midOfThree = function midOfThree(a, b, c) {
4088 if (b <= a && a <= c || c <= a && a <= b) {
4089 return a;
4090 } else if (a <= b && b <= c || c <= b && b <= a) {
4091 return b;
4092 } else {
4093 return c;
4094 }
4095 }; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
4096
4097 var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
4098 var dx13 = x1 - x3;
4099 var dx21 = x2 - x1;
4100 var dx43 = x4 - x3;
4101 var dy13 = y1 - y3;
4102 var dy21 = y2 - y1;
4103 var dy43 = y4 - y3;
4104 var ua_t = dx43 * dy13 - dy43 * dx13;
4105 var ub_t = dx21 * dy13 - dy21 * dx13;
4106 var u_b = dy43 * dx21 - dx43 * dy21;
4107
4108 if (u_b !== 0) {
4109 var ua = ua_t / u_b;
4110 var ub = ub_t / u_b;
4111 var flptThreshold = 0.001;
4112
4113 var _min = 0 - flptThreshold;
4114
4115 var _max = 1 + flptThreshold;
4116
4117 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
4118 return [x1 + ua * dx21, y1 + ua * dy21];
4119 } else {
4120 if (!infiniteLines) {
4121 return [];
4122 } else {
4123 return [x1 + ua * dx21, y1 + ua * dy21];
4124 }
4125 }
4126 } else {
4127 if (ua_t === 0 || ub_t === 0) {
4128 // Parallel, coincident lines. Check if overlap
4129 // Check endpoint of second line
4130 if (midOfThree(x1, x2, x4) === x4) {
4131 return [x4, y4];
4132 } // Check start point of second line
4133
4134
4135 if (midOfThree(x1, x2, x3) === x3) {
4136 return [x3, y3];
4137 } // Endpoint of first line
4138
4139
4140 if (midOfThree(x3, x4, x2) === x2) {
4141 return [x2, y2];
4142 }
4143
4144 return [];
4145 } else {
4146 // Parallel, non-coincident
4147 return [];
4148 }
4149 }
4150 }; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
4151 // intersect a node polygon (pts transformed)
4152 //
4153 // math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
4154 // intersect the points (no transform)
4155
4156 var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
4157 var intersections = [];
4158 var intersection;
4159 var transformedPoints = new Array(basePoints.length);
4160 var doTransform = true;
4161
4162 if (width == null) {
4163 doTransform = false;
4164 }
4165
4166 var points;
4167
4168 if (doTransform) {
4169 for (var i = 0; i < transformedPoints.length / 2; i++) {
4170 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
4171 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
4172 }
4173
4174 if (padding > 0) {
4175 var expandedLineSet = expandPolygon(transformedPoints, -padding);
4176 points = joinLines(expandedLineSet);
4177 } else {
4178 points = transformedPoints;
4179 }
4180 } else {
4181 points = basePoints;
4182 }
4183
4184 var currentX, currentY, nextX, nextY;
4185
4186 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
4187 currentX = points[_i2 * 2];
4188 currentY = points[_i2 * 2 + 1];
4189
4190 if (_i2 < points.length / 2 - 1) {
4191 nextX = points[(_i2 + 1) * 2];
4192 nextY = points[(_i2 + 1) * 2 + 1];
4193 } else {
4194 nextX = points[0];
4195 nextY = points[1];
4196 }
4197
4198 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
4199
4200 if (intersection.length !== 0) {
4201 intersections.push(intersection[0], intersection[1]);
4202 }
4203 }
4204
4205 return intersections;
4206 };
4207 var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
4208 var intersections = [];
4209 var intersection;
4210 var lines = new Array(basePoints.length);
4211 var halfW = width / 2;
4212 var halfH = height / 2;
4213 var cornerRadius = getRoundPolygonRadius(width, height);
4214
4215 for (var i = 0; i < basePoints.length / 4; i++) {
4216 var sourceUv = void 0,
4217 destUv = void 0;
4218
4219 if (i === 0) {
4220 sourceUv = basePoints.length - 2;
4221 } else {
4222 sourceUv = i * 4 - 2;
4223 }
4224
4225 destUv = i * 4 + 2;
4226 var px = centerX + halfW * basePoints[i * 4];
4227 var py = centerY + halfH * basePoints[i * 4 + 1];
4228 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
4229 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
4230 var cp0x = px - offset * basePoints[sourceUv];
4231 var cp0y = py - offset * basePoints[sourceUv + 1];
4232 var cp1x = px + offset * basePoints[destUv];
4233 var cp1y = py + offset * basePoints[destUv + 1];
4234
4235 if (i === 0) {
4236 lines[basePoints.length - 2] = cp0x;
4237 lines[basePoints.length - 1] = cp0y;
4238 } else {
4239 lines[i * 4 - 2] = cp0x;
4240 lines[i * 4 - 1] = cp0y;
4241 }
4242
4243 lines[i * 4] = cp1x;
4244 lines[i * 4 + 1] = cp1y;
4245 var orthx = basePoints[sourceUv + 1];
4246 var orthy = -basePoints[sourceUv];
4247 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
4248
4249 if (cosAlpha < 0) {
4250 orthx *= -1;
4251 orthy *= -1;
4252 }
4253
4254 var cx = cp0x + orthx * cornerRadius;
4255 var cy = cp0y + orthy * cornerRadius;
4256 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
4257
4258 if (intersection.length !== 0) {
4259 intersections.push(intersection[0], intersection[1]);
4260 }
4261 }
4262
4263 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
4264 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
4265
4266 if (intersection.length !== 0) {
4267 intersections.push(intersection[0], intersection[1]);
4268 }
4269 }
4270
4271 if (intersections.length > 2) {
4272 var lowestIntersection = [intersections[0], intersections[1]];
4273 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
4274
4275 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
4276 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
4277
4278 if (squaredDistance <= lowestSquaredDistance) {
4279 lowestIntersection[0] = intersections[_i4 * 2];
4280 lowestIntersection[1] = intersections[_i4 * 2 + 1];
4281 lowestSquaredDistance = squaredDistance;
4282 }
4283 }
4284
4285 return lowestIntersection;
4286 }
4287
4288 return intersections;
4289 };
4290 var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
4291 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
4292 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
4293 var lenRatio = (length - amount) / length;
4294
4295 if (lenRatio < 0) {
4296 lenRatio = 0.00001;
4297 }
4298
4299 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
4300 };
4301 var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
4302 var points = generateUnitNgonPoints(sides, rotationRadians);
4303 points = fitPolygonToSquare(points);
4304 return points;
4305 };
4306 var fitPolygonToSquare = function fitPolygonToSquare(points) {
4307 var x, y;
4308 var sides = points.length / 2;
4309 var minX = Infinity,
4310 minY = Infinity,
4311 maxX = -Infinity,
4312 maxY = -Infinity;
4313
4314 for (var i = 0; i < sides; i++) {
4315 x = points[2 * i];
4316 y = points[2 * i + 1];
4317 minX = Math.min(minX, x);
4318 maxX = Math.max(maxX, x);
4319 minY = Math.min(minY, y);
4320 maxY = Math.max(maxY, y);
4321 } // stretch factors
4322
4323
4324 var sx = 2 / (maxX - minX);
4325 var sy = 2 / (maxY - minY);
4326
4327 for (var _i5 = 0; _i5 < sides; _i5++) {
4328 x = points[2 * _i5] = points[2 * _i5] * sx;
4329 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
4330 minX = Math.min(minX, x);
4331 maxX = Math.max(maxX, x);
4332 minY = Math.min(minY, y);
4333 maxY = Math.max(maxY, y);
4334 }
4335
4336 if (minY < -1) {
4337 for (var _i6 = 0; _i6 < sides; _i6++) {
4338 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
4339 }
4340 }
4341
4342 return points;
4343 };
4344 var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
4345 var increment = 1.0 / sides * 2 * Math.PI;
4346 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
4347 startAngle += rotationRadians;
4348 var points = new Array(sides * 2);
4349 var currentAngle;
4350
4351 for (var i = 0; i < sides; i++) {
4352 currentAngle = i * increment + startAngle;
4353 points[2 * i] = Math.cos(currentAngle); // x
4354
4355 points[2 * i + 1] = Math.sin(-currentAngle); // y
4356 }
4357
4358 return points;
4359 }; // Set the default radius, unless half of width or height is smaller than default
4360
4361 var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
4362 return Math.min(width / 4, height / 4, 8);
4363 }; // Set the default radius
4364
4365 var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
4366 return Math.min(width / 10, height / 10, 8);
4367 };
4368 var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
4369 return 8;
4370 };
4371 var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
4372 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
4373 }; // get curve width, height, and control point position offsets as a percentage of node height / width
4374
4375 var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
4376 return {
4377 heightOffset: Math.min(15, 0.05 * height),
4378 widthOffset: Math.min(100, 0.25 * width),
4379 ctrlPtOffsetPct: 0.05
4380 };
4381 };
4382
4383 var pageRankDefaults = defaults$g({
4384 dampingFactor: 0.8,
4385 precision: 0.000001,
4386 iterations: 200,
4387 weight: function weight(edge) {
4388 return 1;
4389 }
4390 });
4391 var elesfn$o = {
4392 pageRank: function pageRank(options) {
4393 var _pageRankDefaults = pageRankDefaults(options),
4394 dampingFactor = _pageRankDefaults.dampingFactor,
4395 precision = _pageRankDefaults.precision,
4396 iterations = _pageRankDefaults.iterations,
4397 weight = _pageRankDefaults.weight;
4398
4399 var cy = this._private.cy;
4400
4401 var _this$byGroup = this.byGroup(),
4402 nodes = _this$byGroup.nodes,
4403 edges = _this$byGroup.edges;
4404
4405 var numNodes = nodes.length;
4406 var numNodesSqd = numNodes * numNodes;
4407 var numEdges = edges.length; // Construct transposed adjacency matrix
4408 // First lets have a zeroed matrix of the right size
4409 // We'll also keep track of the sum of each column
4410
4411 var matrix = new Array(numNodesSqd);
4412 var columnSum = new Array(numNodes);
4413 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
4414
4415 for (var i = 0; i < numNodes; i++) {
4416 for (var j = 0; j < numNodes; j++) {
4417 var n = i * numNodes + j;
4418 matrix[n] = 0;
4419 }
4420
4421 columnSum[i] = 0;
4422 } // Now, process edges
4423
4424
4425 for (var _i = 0; _i < numEdges; _i++) {
4426 var edge = edges[_i];
4427 var srcId = edge.data('source');
4428 var tgtId = edge.data('target'); // Don't include loops in the matrix
4429
4430 if (srcId === tgtId) {
4431 continue;
4432 }
4433
4434 var s = nodes.indexOfId(srcId);
4435 var t = nodes.indexOfId(tgtId);
4436 var w = weight(edge);
4437
4438 var _n = t * numNodes + s; // Update matrix
4439
4440
4441 matrix[_n] += w; // Update column sum
4442
4443 columnSum[s] += w;
4444 } // Add additional probability based on damping factor
4445 // Also, take into account columns that have sum = 0
4446
4447
4448 var p = 1.0 / numNodes + additionalProb; // Shorthand
4449 // Traverse matrix, column by column
4450
4451 for (var _j = 0; _j < numNodes; _j++) {
4452 if (columnSum[_j] === 0) {
4453 // No 'links' out from node jth, assume equal probability for each possible node
4454 for (var _i2 = 0; _i2 < numNodes; _i2++) {
4455 var _n2 = _i2 * numNodes + _j;
4456
4457 matrix[_n2] = p;
4458 }
4459 } else {
4460 // Node jth has outgoing link, compute normalized probabilities
4461 for (var _i3 = 0; _i3 < numNodes; _i3++) {
4462 var _n3 = _i3 * numNodes + _j;
4463
4464 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
4465 }
4466 }
4467 } // Compute dominant eigenvector using power method
4468
4469
4470 var eigenvector = new Array(numNodes);
4471 var temp = new Array(numNodes);
4472 var previous; // Start with a vector of all 1's
4473 // Also, initialize a null vector which will be used as shorthand
4474
4475 for (var _i4 = 0; _i4 < numNodes; _i4++) {
4476 eigenvector[_i4] = 1;
4477 }
4478
4479 for (var iter = 0; iter < iterations; iter++) {
4480 // Temp array with all 0's
4481 for (var _i5 = 0; _i5 < numNodes; _i5++) {
4482 temp[_i5] = 0;
4483 } // Multiply matrix with previous result
4484
4485
4486 for (var _i6 = 0; _i6 < numNodes; _i6++) {
4487 for (var _j2 = 0; _j2 < numNodes; _j2++) {
4488 var _n4 = _i6 * numNodes + _j2;
4489
4490 temp[_i6] += matrix[_n4] * eigenvector[_j2];
4491 }
4492 }
4493
4494 inPlaceSumNormalize(temp);
4495 previous = eigenvector;
4496 eigenvector = temp;
4497 temp = previous;
4498 var diff = 0; // Compute difference (squared module) of both vectors
4499
4500 for (var _i7 = 0; _i7 < numNodes; _i7++) {
4501 var delta = previous[_i7] - eigenvector[_i7];
4502 diff += delta * delta;
4503 } // If difference is less than the desired threshold, stop iterating
4504
4505
4506 if (diff < precision) {
4507 break;
4508 }
4509 } // Construct result
4510
4511
4512 var res = {
4513 rank: function rank(node) {
4514 node = cy.collection(node)[0];
4515 return eigenvector[nodes.indexOf(node)];
4516 }
4517 };
4518 return res;
4519 } // pageRank
4520
4521 }; // elesfn
4522
4523 var defaults$f = defaults$g({
4524 root: null,
4525 weight: function weight(edge) {
4526 return 1;
4527 },
4528 directed: false,
4529 alpha: 0
4530 });
4531 var elesfn$n = {
4532 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
4533 options = defaults$f(options);
4534 var cy = this.cy();
4535 var nodes = this.nodes();
4536 var numNodes = nodes.length;
4537
4538 if (!options.directed) {
4539 var degrees = {};
4540 var maxDegree = 0;
4541
4542 for (var i = 0; i < numNodes; i++) {
4543 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
4544
4545 options.root = node;
4546 var currDegree = this.degreeCentrality(options);
4547
4548 if (maxDegree < currDegree.degree) {
4549 maxDegree = currDegree.degree;
4550 }
4551
4552 degrees[node.id()] = currDegree.degree;
4553 }
4554
4555 return {
4556 degree: function degree(node) {
4557 if (maxDegree === 0) {
4558 return 0;
4559 }
4560
4561 if (string(node)) {
4562 // from is a selector string
4563 node = cy.filter(node);
4564 }
4565
4566 return degrees[node.id()] / maxDegree;
4567 }
4568 };
4569 } else {
4570 var indegrees = {};
4571 var outdegrees = {};
4572 var maxIndegree = 0;
4573 var maxOutdegree = 0;
4574
4575 for (var _i = 0; _i < numNodes; _i++) {
4576 var _node = nodes[_i];
4577
4578 var id = _node.id(); // add current node to the current options object and call degreeCentrality
4579
4580
4581 options.root = _node;
4582
4583 var _currDegree = this.degreeCentrality(options);
4584
4585 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
4586 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
4587 indegrees[id] = _currDegree.indegree;
4588 outdegrees[id] = _currDegree.outdegree;
4589 }
4590
4591 return {
4592 indegree: function indegree(node) {
4593 if (maxIndegree == 0) {
4594 return 0;
4595 }
4596
4597 if (string(node)) {
4598 // from is a selector string
4599 node = cy.filter(node);
4600 }
4601
4602 return indegrees[node.id()] / maxIndegree;
4603 },
4604 outdegree: function outdegree(node) {
4605 if (maxOutdegree === 0) {
4606 return 0;
4607 }
4608
4609 if (string(node)) {
4610 // from is a selector string
4611 node = cy.filter(node);
4612 }
4613
4614 return outdegrees[node.id()] / maxOutdegree;
4615 }
4616 };
4617 }
4618 },
4619 // degreeCentralityNormalized
4620 // Implemented from the algorithm in Opsahl's paper
4621 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
4622 // check the heading 2 "Degree"
4623 degreeCentrality: function degreeCentrality(options) {
4624 options = defaults$f(options);
4625 var cy = this.cy();
4626 var callingEles = this;
4627 var _options = options,
4628 root = _options.root,
4629 weight = _options.weight,
4630 directed = _options.directed,
4631 alpha = _options.alpha;
4632 root = cy.collection(root)[0];
4633
4634 if (!directed) {
4635 var connEdges = root.connectedEdges().intersection(callingEles);
4636 var k = connEdges.length;
4637 var s = 0; // Now, sum edge weights
4638
4639 for (var i = 0; i < connEdges.length; i++) {
4640 s += weight(connEdges[i]);
4641 }
4642
4643 return {
4644 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
4645 };
4646 } else {
4647 var edges = root.connectedEdges();
4648 var incoming = edges.filter(function (edge) {
4649 return edge.target().same(root) && callingEles.has(edge);
4650 });
4651 var outgoing = edges.filter(function (edge) {
4652 return edge.source().same(root) && callingEles.has(edge);
4653 });
4654 var k_in = incoming.length;
4655 var k_out = outgoing.length;
4656 var s_in = 0;
4657 var s_out = 0; // Now, sum incoming edge weights
4658
4659 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
4660 s_in += weight(incoming[_i2]);
4661 } // Now, sum outgoing edge weights
4662
4663
4664 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
4665 s_out += weight(outgoing[_i3]);
4666 }
4667
4668 return {
4669 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
4670 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
4671 };
4672 }
4673 } // degreeCentrality
4674
4675 }; // elesfn
4676 // nice, short mathematical alias
4677
4678 elesfn$n.dc = elesfn$n.degreeCentrality;
4679 elesfn$n.dcn = elesfn$n.degreeCentralityNormalised = elesfn$n.degreeCentralityNormalized;
4680
4681 var defaults$e = defaults$g({
4682 harmonic: true,
4683 weight: function weight() {
4684 return 1;
4685 },
4686 directed: false,
4687 root: null
4688 });
4689 var elesfn$m = {
4690 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
4691 var _defaults = defaults$e(options),
4692 harmonic = _defaults.harmonic,
4693 weight = _defaults.weight,
4694 directed = _defaults.directed;
4695
4696 var cy = this.cy();
4697 var closenesses = {};
4698 var maxCloseness = 0;
4699 var nodes = this.nodes();
4700 var fw = this.floydWarshall({
4701 weight: weight,
4702 directed: directed
4703 }); // Compute closeness for every node and find the maximum closeness
4704
4705 for (var i = 0; i < nodes.length; i++) {
4706 var currCloseness = 0;
4707 var node_i = nodes[i];
4708
4709 for (var j = 0; j < nodes.length; j++) {
4710 if (i !== j) {
4711 var d = fw.distance(node_i, nodes[j]);
4712
4713 if (harmonic) {
4714 currCloseness += 1 / d;
4715 } else {
4716 currCloseness += d;
4717 }
4718 }
4719 }
4720
4721 if (!harmonic) {
4722 currCloseness = 1 / currCloseness;
4723 }
4724
4725 if (maxCloseness < currCloseness) {
4726 maxCloseness = currCloseness;
4727 }
4728
4729 closenesses[node_i.id()] = currCloseness;
4730 }
4731
4732 return {
4733 closeness: function closeness(node) {
4734 if (maxCloseness == 0) {
4735 return 0;
4736 }
4737
4738 if (string(node)) {
4739 // from is a selector string
4740 node = cy.filter(node)[0].id();
4741 } else {
4742 // from is a node
4743 node = node.id();
4744 }
4745
4746 return closenesses[node] / maxCloseness;
4747 }
4748 };
4749 },
4750 // Implemented from pseudocode from wikipedia
4751 closenessCentrality: function closenessCentrality(options) {
4752 var _defaults2 = defaults$e(options),
4753 root = _defaults2.root,
4754 weight = _defaults2.weight,
4755 directed = _defaults2.directed,
4756 harmonic = _defaults2.harmonic;
4757
4758 root = this.filter(root)[0]; // we need distance from this node to every other node
4759
4760 var dijkstra = this.dijkstra({
4761 root: root,
4762 weight: weight,
4763 directed: directed
4764 });
4765 var totalDistance = 0;
4766 var nodes = this.nodes();
4767
4768 for (var i = 0; i < nodes.length; i++) {
4769 var n = nodes[i];
4770
4771 if (!n.same(root)) {
4772 var d = dijkstra.distanceTo(n);
4773
4774 if (harmonic) {
4775 totalDistance += 1 / d;
4776 } else {
4777 totalDistance += d;
4778 }
4779 }
4780 }
4781
4782 return harmonic ? totalDistance : 1 / totalDistance;
4783 } // closenessCentrality
4784
4785 }; // elesfn
4786 // nice, short mathematical alias
4787
4788 elesfn$m.cc = elesfn$m.closenessCentrality;
4789 elesfn$m.ccn = elesfn$m.closenessCentralityNormalised = elesfn$m.closenessCentralityNormalized;
4790
4791 var defaults$d = defaults$g({
4792 weight: null,
4793 directed: false
4794 });
4795 var elesfn$l = {
4796 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
4797 betweennessCentrality: function betweennessCentrality(options) {
4798 var _defaults = defaults$d(options),
4799 directed = _defaults.directed,
4800 weight = _defaults.weight;
4801
4802 var weighted = weight != null;
4803 var cy = this.cy(); // starting
4804
4805 var V = this.nodes();
4806 var A = {};
4807 var _C = {};
4808 var max = 0;
4809 var C = {
4810 set: function set(key, val) {
4811 _C[key] = val;
4812
4813 if (val > max) {
4814 max = val;
4815 }
4816 },
4817 get: function get(key) {
4818 return _C[key];
4819 }
4820 }; // A contains the neighborhoods of every node
4821
4822 for (var i = 0; i < V.length; i++) {
4823 var v = V[i];
4824 var vid = v.id();
4825
4826 if (directed) {
4827 A[vid] = v.outgoers().nodes(); // get outgoers of every node
4828 } else {
4829 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
4830 }
4831
4832 C.set(vid, 0);
4833 }
4834
4835 var _loop = function _loop(s) {
4836 var sid = V[s].id();
4837 var S = []; // stack
4838
4839 var P = {};
4840 var g = {};
4841 var d = {};
4842 var Q = new heap(function (a, b) {
4843 return d[a] - d[b];
4844 }); // queue
4845 // init dictionaries
4846
4847 for (var _i = 0; _i < V.length; _i++) {
4848 var _vid = V[_i].id();
4849
4850 P[_vid] = [];
4851 g[_vid] = 0;
4852 d[_vid] = Infinity;
4853 }
4854
4855 g[sid] = 1; // sigma
4856
4857 d[sid] = 0; // distance to s
4858
4859 Q.push(sid);
4860
4861 while (!Q.empty()) {
4862 var _v = Q.pop();
4863
4864 S.push(_v);
4865
4866 if (weighted) {
4867 for (var j = 0; j < A[_v].length; j++) {
4868 var w = A[_v][j];
4869 var vEle = cy.getElementById(_v);
4870 var edge = void 0;
4871
4872 if (vEle.edgesTo(w).length > 0) {
4873 edge = vEle.edgesTo(w)[0];
4874 } else {
4875 edge = w.edgesTo(vEle)[0];
4876 }
4877
4878 var edgeWeight = weight(edge);
4879 w = w.id();
4880
4881 if (d[w] > d[_v] + edgeWeight) {
4882 d[w] = d[_v] + edgeWeight;
4883
4884 if (Q.nodes.indexOf(w) < 0) {
4885 //if w is not in Q
4886 Q.push(w);
4887 } else {
4888 // update position if w is in Q
4889 Q.updateItem(w);
4890 }
4891
4892 g[w] = 0;
4893 P[w] = [];
4894 }
4895
4896 if (d[w] == d[_v] + edgeWeight) {
4897 g[w] = g[w] + g[_v];
4898 P[w].push(_v);
4899 }
4900 }
4901 } else {
4902 for (var _j = 0; _j < A[_v].length; _j++) {
4903 var _w = A[_v][_j].id();
4904
4905 if (d[_w] == Infinity) {
4906 Q.push(_w);
4907 d[_w] = d[_v] + 1;
4908 }
4909
4910 if (d[_w] == d[_v] + 1) {
4911 g[_w] = g[_w] + g[_v];
4912
4913 P[_w].push(_v);
4914 }
4915 }
4916 }
4917 }
4918
4919 var e = {};
4920
4921 for (var _i2 = 0; _i2 < V.length; _i2++) {
4922 e[V[_i2].id()] = 0;
4923 }
4924
4925 while (S.length > 0) {
4926 var _w2 = S.pop();
4927
4928 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4929 var _v2 = P[_w2][_j2];
4930 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4931 }
4932
4933 if (_w2 != V[s].id()) {
4934 C.set(_w2, C.get(_w2) + e[_w2]);
4935 }
4936 }
4937 };
4938
4939 for (var s = 0; s < V.length; s++) {
4940 _loop(s);
4941 }
4942
4943 var ret = {
4944 betweenness: function betweenness(node) {
4945 var id = cy.collection(node).id();
4946 return C.get(id);
4947 },
4948 betweennessNormalized: function betweennessNormalized(node) {
4949 if (max == 0) {
4950 return 0;
4951 }
4952
4953 var id = cy.collection(node).id();
4954 return C.get(id) / max;
4955 }
4956 }; // alias
4957
4958 ret.betweennessNormalised = ret.betweennessNormalized;
4959 return ret;
4960 } // betweennessCentrality
4961
4962 }; // elesfn
4963 // nice, short mathematical alias
4964
4965 elesfn$l.bc = elesfn$l.betweennessCentrality;
4966
4967 // Implemented by Zoe Xi @zoexi for GSOC 2016
4968 /* eslint-disable no-unused-vars */
4969
4970 var defaults$c = defaults$g({
4971 expandFactor: 2,
4972 // affects time of computation and cluster granularity to some extent: M * M
4973 inflateFactor: 2,
4974 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4975 multFactor: 1,
4976 // optional self loops for each node. Use a neutral value to improve cluster computations.
4977 maxIterations: 20,
4978 // maximum number of iterations of the MCL algorithm in a single run
4979 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4980 function (edge) {
4981 return 1;
4982 }]
4983 });
4984 /* eslint-enable */
4985
4986 var setOptions$3 = function setOptions(options) {
4987 return defaults$c(options);
4988 };
4989 /* eslint-enable */
4990
4991
4992 var getSimilarity$1 = function getSimilarity(edge, attributes) {
4993 var total = 0;
4994
4995 for (var i = 0; i < attributes.length; i++) {
4996 total += attributes[i](edge);
4997 }
4998
4999 return total;
5000 };
5001
5002 var addLoops = function addLoops(M, n, val) {
5003 for (var i = 0; i < n; i++) {
5004 M[i * n + i] = val;
5005 }
5006 };
5007
5008 var normalize = function normalize(M, n) {
5009 var sum;
5010
5011 for (var col = 0; col < n; col++) {
5012 sum = 0;
5013
5014 for (var row = 0; row < n; row++) {
5015 sum += M[row * n + col];
5016 }
5017
5018 for (var _row = 0; _row < n; _row++) {
5019 M[_row * n + col] = M[_row * n + col] / sum;
5020 }
5021 }
5022 }; // TODO: blocked matrix multiplication?
5023
5024
5025 var mmult = function mmult(A, B, n) {
5026 var C = new Array(n * n);
5027
5028 for (var i = 0; i < n; i++) {
5029 for (var j = 0; j < n; j++) {
5030 C[i * n + j] = 0;
5031 }
5032
5033 for (var k = 0; k < n; k++) {
5034 for (var _j = 0; _j < n; _j++) {
5035 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
5036 }
5037 }
5038 }
5039
5040 return C;
5041 };
5042
5043 var expand = function expand(M, n, expandFactor
5044 /** power **/
5045 ) {
5046 var _M = M.slice(0);
5047
5048 for (var p = 1; p < expandFactor; p++) {
5049 M = mmult(M, _M, n);
5050 }
5051
5052 return M;
5053 };
5054
5055 var inflate = function inflate(M, n, inflateFactor
5056 /** r **/
5057 ) {
5058 var _M = new Array(n * n); // M(i,j) ^ inflatePower
5059
5060
5061 for (var i = 0; i < n * n; i++) {
5062 _M[i] = Math.pow(M[i], inflateFactor);
5063 }
5064
5065 normalize(_M, n);
5066 return _M;
5067 };
5068
5069 var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
5070 // Check that both matrices have the same elements (i,j)
5071 for (var i = 0; i < n2; i++) {
5072 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
5073
5074 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
5075
5076 if (v1 !== v2) {
5077 return false;
5078 }
5079 }
5080
5081 return true;
5082 };
5083
5084 var assign$2 = function assign(M, n, nodes, cy) {
5085 var clusters = [];
5086
5087 for (var i = 0; i < n; i++) {
5088 var cluster = [];
5089
5090 for (var j = 0; j < n; j++) {
5091 // Row-wise attractors and elements that they attract belong in same cluster
5092 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
5093 cluster.push(nodes[j]);
5094 }
5095 }
5096
5097 if (cluster.length !== 0) {
5098 clusters.push(cy.collection(cluster));
5099 }
5100 }
5101
5102 return clusters;
5103 };
5104
5105 var isDuplicate = function isDuplicate(c1, c2) {
5106 for (var i = 0; i < c1.length; i++) {
5107 if (!c2[i] || c1[i].id() !== c2[i].id()) {
5108 return false;
5109 }
5110 }
5111
5112 return true;
5113 };
5114
5115 var removeDuplicates = function removeDuplicates(clusters) {
5116 for (var i = 0; i < clusters.length; i++) {
5117 for (var j = 0; j < clusters.length; j++) {
5118 if (i != j && isDuplicate(clusters[i], clusters[j])) {
5119 clusters.splice(j, 1);
5120 }
5121 }
5122 }
5123
5124 return clusters;
5125 };
5126
5127 var markovClustering = function markovClustering(options) {
5128 var nodes = this.nodes();
5129 var edges = this.edges();
5130 var cy = this.cy(); // Set parameters of algorithm:
5131
5132 var opts = setOptions$3(options); // Map each node to its position in node array
5133
5134 var id2position = {};
5135
5136 for (var i = 0; i < nodes.length; i++) {
5137 id2position[nodes[i].id()] = i;
5138 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
5139
5140
5141 var n = nodes.length,
5142 n2 = n * n;
5143
5144 var M = new Array(n2),
5145 _M;
5146
5147 for (var _i = 0; _i < n2; _i++) {
5148 M[_i] = 0;
5149 }
5150
5151 for (var e = 0; e < edges.length; e++) {
5152 var edge = edges[e];
5153 var _i2 = id2position[edge.source().id()];
5154 var j = id2position[edge.target().id()];
5155 var sim = getSimilarity$1(edge, opts.attributes);
5156 M[_i2 * n + j] += sim; // G should be symmetric and undirected
5157
5158 M[j * n + _i2] += sim;
5159 } // Begin Markov cluster algorithm
5160 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
5161
5162
5163 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
5164
5165 normalize(M, n);
5166 var isStillMoving = true;
5167 var iterations = 0;
5168
5169 while (isStillMoving && iterations < opts.maxIterations) {
5170 isStillMoving = false; // Step 3:
5171
5172 _M = expand(M, n, opts.expandFactor); // Step 4:
5173
5174 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
5175
5176 if (!hasConverged(M, _M, n2, 4)) {
5177 isStillMoving = true;
5178 }
5179
5180 iterations++;
5181 } // Build clusters from matrix
5182
5183
5184 var clusters = assign$2(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
5185
5186 clusters = removeDuplicates(clusters);
5187 return clusters;
5188 };
5189
5190 var markovClustering$1 = {
5191 markovClustering: markovClustering,
5192 mcl: markovClustering
5193 };
5194
5195 // Common distance metrics for clustering algorithms
5196
5197 var identity = function identity(x) {
5198 return x;
5199 };
5200
5201 var absDiff = function absDiff(p, q) {
5202 return Math.abs(q - p);
5203 };
5204
5205 var addAbsDiff = function addAbsDiff(total, p, q) {
5206 return total + absDiff(p, q);
5207 };
5208
5209 var addSquaredDiff = function addSquaredDiff(total, p, q) {
5210 return total + Math.pow(q - p, 2);
5211 };
5212
5213 var sqrt = function sqrt(x) {
5214 return Math.sqrt(x);
5215 };
5216
5217 var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
5218 return Math.max(currentMax, absDiff(p, q));
5219 };
5220
5221 var getDistance = function getDistance(length, getP, getQ, init, visit) {
5222 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
5223 var ret = init;
5224 var p, q;
5225
5226 for (var dim = 0; dim < length; dim++) {
5227 p = getP(dim);
5228 q = getQ(dim);
5229 ret = visit(ret, p, q);
5230 }
5231
5232 return post(ret);
5233 };
5234
5235 var distances = {
5236 euclidean: function euclidean(length, getP, getQ) {
5237 if (length >= 2) {
5238 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
5239 } else {
5240 // for single attr case, more efficient to avoid sqrt
5241 return getDistance(length, getP, getQ, 0, addAbsDiff);
5242 }
5243 },
5244 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
5245 return getDistance(length, getP, getQ, 0, addSquaredDiff);
5246 },
5247 manhattan: function manhattan(length, getP, getQ) {
5248 return getDistance(length, getP, getQ, 0, addAbsDiff);
5249 },
5250 max: function max(length, getP, getQ) {
5251 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
5252 }
5253 }; // in case the user accidentally doesn't use camel case
5254
5255 distances['squared-euclidean'] = distances['squaredEuclidean'];
5256 distances['squaredeuclidean'] = distances['squaredEuclidean'];
5257 function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
5258 var impl;
5259
5260 if (fn$6(method)) {
5261 impl = method;
5262 } else {
5263 impl = distances[method] || distances.euclidean;
5264 }
5265
5266 if (length === 0 && fn$6(method)) {
5267 return impl(nodeP, nodeQ);
5268 } else {
5269 return impl(length, getP, getQ, nodeP, nodeQ);
5270 }
5271 }
5272
5273 var defaults$b = defaults$g({
5274 k: 2,
5275 m: 2,
5276 sensitivityThreshold: 0.0001,
5277 distance: 'euclidean',
5278 maxIterations: 10,
5279 attributes: [],
5280 testMode: false,
5281 testCentroids: null
5282 });
5283
5284 var setOptions$2 = function setOptions(options) {
5285 return defaults$b(options);
5286 };
5287 /* eslint-enable */
5288
5289
5290 var getDist = function getDist(type, node, centroid, attributes, mode) {
5291 var noNodeP = mode !== 'kMedoids';
5292 var getP = noNodeP ? function (i) {
5293 return centroid[i];
5294 } : function (i) {
5295 return attributes[i](centroid);
5296 };
5297
5298 var getQ = function getQ(i) {
5299 return attributes[i](node);
5300 };
5301
5302 var nodeP = centroid;
5303 var nodeQ = node;
5304 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
5305 };
5306
5307 var randomCentroids = function randomCentroids(nodes, k, attributes) {
5308 var ndim = attributes.length;
5309 var min = new Array(ndim);
5310 var max = new Array(ndim);
5311 var centroids = new Array(k);
5312 var centroid = null; // Find min, max values for each attribute dimension
5313
5314 for (var i = 0; i < ndim; i++) {
5315 min[i] = nodes.min(attributes[i]).value;
5316 max[i] = nodes.max(attributes[i]).value;
5317 } // Build k centroids, each represented as an n-dim feature vector
5318
5319
5320 for (var c = 0; c < k; c++) {
5321 centroid = [];
5322
5323 for (var _i = 0; _i < ndim; _i++) {
5324 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
5325 }
5326
5327 centroids[c] = centroid;
5328 }
5329
5330 return centroids;
5331 };
5332
5333 var classify = function classify(node, centroids, distance, attributes, type) {
5334 var min = Infinity;
5335 var index = 0;
5336
5337 for (var i = 0; i < centroids.length; i++) {
5338 var dist = getDist(distance, node, centroids[i], attributes, type);
5339
5340 if (dist < min) {
5341 min = dist;
5342 index = i;
5343 }
5344 }
5345
5346 return index;
5347 };
5348
5349 var buildCluster = function buildCluster(centroid, nodes, assignment) {
5350 var cluster = [];
5351 var node = null;
5352
5353 for (var n = 0; n < nodes.length; n++) {
5354 node = nodes[n];
5355
5356 if (assignment[node.id()] === centroid) {
5357 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
5358 cluster.push(node);
5359 }
5360 }
5361
5362 return cluster;
5363 };
5364
5365 var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
5366 return Math.abs(v2 - v1) <= sensitivityThreshold;
5367 };
5368
5369 var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
5370 for (var i = 0; i < v1.length; i++) {
5371 for (var j = 0; j < v1[i].length; j++) {
5372 var diff = Math.abs(v1[i][j] - v2[i][j]);
5373
5374 if (diff > sensitivityThreshold) {
5375 return false;
5376 }
5377 }
5378 }
5379
5380 return true;
5381 };
5382
5383 var seenBefore = function seenBefore(node, medoids, n) {
5384 for (var i = 0; i < n; i++) {
5385 if (node === medoids[i]) return true;
5386 }
5387
5388 return false;
5389 };
5390
5391 var randomMedoids = function randomMedoids(nodes, k) {
5392 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
5393 // so we need to check to see if we've already seen or chose this node before.
5394
5395 if (nodes.length < 50) {
5396 // Randomly select k medoids from the n nodes
5397 for (var i = 0; i < k; i++) {
5398 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).
5399 // Instead choose a different random node.
5400
5401 while (seenBefore(node, medoids, i)) {
5402 node = nodes[Math.floor(Math.random() * nodes.length)];
5403 }
5404
5405 medoids[i] = node;
5406 }
5407 } else {
5408 // Relatively large data set, so pretty safe to not check and just select random nodes
5409 for (var _i2 = 0; _i2 < k; _i2++) {
5410 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
5411 }
5412 }
5413
5414 return medoids;
5415 };
5416
5417 var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
5418 var cost = 0;
5419
5420 for (var n = 0; n < cluster.length; n++) {
5421 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
5422 }
5423
5424 return cost;
5425 };
5426
5427 var kMeans = function kMeans(options) {
5428 var cy = this.cy();
5429 var nodes = this.nodes();
5430 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
5431
5432 var opts = setOptions$2(options); // Begin k-means algorithm
5433
5434 var clusters = new Array(opts.k);
5435 var assignment = {};
5436 var centroids; // Step 1: Initialize centroid positions
5437
5438 if (opts.testMode) {
5439 if (typeof opts.testCentroids === 'number') {
5440 // TODO: implement a seeded random number generator.
5441 opts.testCentroids;
5442 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5443 } else if (_typeof(opts.testCentroids) === 'object') {
5444 centroids = opts.testCentroids;
5445 } else {
5446 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5447 }
5448 } else {
5449 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5450 }
5451
5452 var isStillMoving = true;
5453 var iterations = 0;
5454
5455 while (isStillMoving && iterations < opts.maxIterations) {
5456 // Step 2: Assign nodes to the nearest centroid
5457 for (var n = 0; n < nodes.length; n++) {
5458 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
5459
5460 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
5461 } // Step 3: For each of the k clusters, update its centroid
5462
5463
5464 isStillMoving = false;
5465
5466 for (var c = 0; c < opts.k; c++) {
5467 // Get all nodes that belong to this cluster
5468 var cluster = buildCluster(c, nodes, assignment);
5469
5470 if (cluster.length === 0) {
5471 // If cluster is empty, break out early & move to next cluster
5472 continue;
5473 } // Update centroids by calculating avg of all nodes within the cluster.
5474
5475
5476 var ndim = opts.attributes.length;
5477 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
5478
5479 var newCentroid = new Array(ndim);
5480 var sum = new Array(ndim);
5481
5482 for (var d = 0; d < ndim; d++) {
5483 sum[d] = 0.0;
5484
5485 for (var i = 0; i < cluster.length; i++) {
5486 node = cluster[i];
5487 sum[d] += opts.attributes[d](node);
5488 }
5489
5490 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
5491
5492 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
5493 isStillMoving = true;
5494 }
5495 }
5496
5497 centroids[c] = newCentroid;
5498 clusters[c] = cy.collection(cluster);
5499 }
5500
5501 iterations++;
5502 }
5503
5504 return clusters;
5505 };
5506
5507 var kMedoids = function kMedoids(options) {
5508 var cy = this.cy();
5509 var nodes = this.nodes();
5510 var node = null;
5511 var opts = setOptions$2(options); // Begin k-medoids algorithm
5512
5513 var clusters = new Array(opts.k);
5514 var medoids;
5515 var assignment = {};
5516 var curCost;
5517 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
5518 // Step 1: Initialize k medoids
5519
5520 if (opts.testMode) {
5521 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
5522 medoids = opts.testCentroids;
5523 } else {
5524 medoids = randomMedoids(nodes, opts.k);
5525 }
5526 } else {
5527 medoids = randomMedoids(nodes, opts.k);
5528 }
5529
5530 var isStillMoving = true;
5531 var iterations = 0;
5532
5533 while (isStillMoving && iterations < opts.maxIterations) {
5534 // Step 2: Assign nodes to the nearest medoid
5535 for (var n = 0; n < nodes.length; n++) {
5536 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
5537
5538 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
5539 }
5540
5541 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
5542 // select the node with the lowest configuration cost as new medoid.
5543
5544 for (var m = 0; m < medoids.length; m++) {
5545 // Get all nodes that belong to this medoid
5546 var cluster = buildCluster(m, nodes, assignment);
5547
5548 if (cluster.length === 0) {
5549 // If cluster is empty, break out early & move to next cluster
5550 continue;
5551 }
5552
5553 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
5554 // Select different medoid if its configuration has the lowest cost
5555
5556 for (var _n = 0; _n < cluster.length; _n++) {
5557 curCost = findCost(cluster[_n], cluster, opts.attributes);
5558
5559 if (curCost < minCosts[m]) {
5560 minCosts[m] = curCost;
5561 medoids[m] = cluster[_n];
5562 isStillMoving = true;
5563 }
5564 }
5565
5566 clusters[m] = cy.collection(cluster);
5567 }
5568
5569 iterations++;
5570 }
5571
5572 return clusters;
5573 };
5574
5575 var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
5576 var numerator, denominator;
5577
5578 for (var n = 0; n < nodes.length; n++) {
5579 for (var c = 0; c < centroids.length; c++) {
5580 weight[n][c] = Math.pow(U[n][c], opts.m);
5581 }
5582 }
5583
5584 for (var _c = 0; _c < centroids.length; _c++) {
5585 for (var dim = 0; dim < opts.attributes.length; dim++) {
5586 numerator = 0;
5587 denominator = 0;
5588
5589 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
5590 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
5591 denominator += weight[_n2][_c];
5592 }
5593
5594 centroids[_c][dim] = numerator / denominator;
5595 }
5596 }
5597 };
5598
5599 var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
5600 // Save previous step
5601 for (var i = 0; i < U.length; i++) {
5602 _U[i] = U[i].slice();
5603 }
5604
5605 var sum, numerator, denominator;
5606 var pow = 2 / (opts.m - 1);
5607
5608 for (var c = 0; c < centroids.length; c++) {
5609 for (var n = 0; n < nodes.length; n++) {
5610 sum = 0;
5611
5612 for (var k = 0; k < centroids.length; k++) {
5613 // against all other centroids
5614 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
5615 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
5616 sum += Math.pow(numerator / denominator, pow);
5617 }
5618
5619 U[n][c] = 1 / sum;
5620 }
5621 }
5622 };
5623
5624 var assign$1 = function assign(nodes, U, opts, cy) {
5625 var clusters = new Array(opts.k);
5626
5627 for (var c = 0; c < clusters.length; c++) {
5628 clusters[c] = [];
5629 }
5630
5631 var max;
5632 var index;
5633
5634 for (var n = 0; n < U.length; n++) {
5635 // for each node (U is N x C matrix)
5636 max = -Infinity;
5637 index = -1; // Determine which cluster the node is most likely to belong in
5638
5639 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
5640 if (U[n][_c2] > max) {
5641 max = U[n][_c2];
5642 index = _c2;
5643 }
5644 }
5645
5646 clusters[index].push(nodes[n]);
5647 } // Turn every array into a collection of nodes
5648
5649
5650 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
5651 clusters[_c3] = cy.collection(clusters[_c3]);
5652 }
5653
5654 return clusters;
5655 };
5656
5657 var fuzzyCMeans = function fuzzyCMeans(options) {
5658 var cy = this.cy();
5659 var nodes = this.nodes();
5660 var opts = setOptions$2(options); // Begin fuzzy c-means algorithm
5661
5662 var clusters;
5663 var centroids;
5664 var U;
5665
5666 var _U;
5667
5668 var weight; // Step 1: Initialize letiables.
5669
5670 _U = new Array(nodes.length);
5671
5672 for (var i = 0; i < nodes.length; i++) {
5673 // N x C matrix
5674 _U[i] = new Array(opts.k);
5675 }
5676
5677 U = new Array(nodes.length);
5678
5679 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
5680 // N x C matrix
5681 U[_i3] = new Array(opts.k);
5682 }
5683
5684 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
5685 var total = 0;
5686
5687 for (var j = 0; j < opts.k; j++) {
5688 U[_i4][j] = Math.random();
5689 total += U[_i4][j];
5690 }
5691
5692 for (var _j = 0; _j < opts.k; _j++) {
5693 U[_i4][_j] = U[_i4][_j] / total;
5694 }
5695 }
5696
5697 centroids = new Array(opts.k);
5698
5699 for (var _i5 = 0; _i5 < opts.k; _i5++) {
5700 centroids[_i5] = new Array(opts.attributes.length);
5701 }
5702
5703 weight = new Array(nodes.length);
5704
5705 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
5706 // N x C matrix
5707 weight[_i6] = new Array(opts.k);
5708 } // end init FCM
5709
5710
5711 var isStillMoving = true;
5712 var iterations = 0;
5713
5714 while (isStillMoving && iterations < opts.maxIterations) {
5715 isStillMoving = false; // Step 2: Calculate the centroids for each step.
5716
5717 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
5718
5719 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
5720
5721 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
5722 isStillMoving = true;
5723 }
5724
5725 iterations++;
5726 } // Assign nodes to clusters with highest probability.
5727
5728
5729 clusters = assign$1(nodes, U, opts, cy);
5730 return {
5731 clusters: clusters,
5732 degreeOfMembership: U
5733 };
5734 };
5735
5736 var kClustering = {
5737 kMeans: kMeans,
5738 kMedoids: kMedoids,
5739 fuzzyCMeans: fuzzyCMeans,
5740 fcm: fuzzyCMeans
5741 };
5742
5743 // Implemented by Zoe Xi @zoexi for GSOC 2016
5744 var defaults$a = defaults$g({
5745 distance: 'euclidean',
5746 // distance metric to compare nodes
5747 linkage: 'min',
5748 // linkage criterion : how to determine the distance between clusters of nodes
5749 mode: 'threshold',
5750 // mode:'threshold' => clusters must be threshold distance apart
5751 threshold: Infinity,
5752 // the distance threshold
5753 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
5754 addDendrogram: false,
5755 // whether to add the dendrogram to the graph for viz
5756 dendrogramDepth: 0,
5757 // depth at which dendrogram branches are merged into the returned clusters
5758 attributes: [] // array of attr functions
5759
5760 });
5761 var linkageAliases = {
5762 'single': 'min',
5763 'complete': 'max'
5764 };
5765
5766 var setOptions$1 = function setOptions(options) {
5767 var opts = defaults$a(options);
5768 var preferredAlias = linkageAliases[opts.linkage];
5769
5770 if (preferredAlias != null) {
5771 opts.linkage = preferredAlias;
5772 }
5773
5774 return opts;
5775 };
5776
5777 var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
5778 // Find two closest clusters from cached mins
5779 var minKey = 0;
5780 var min = Infinity;
5781 var dist;
5782 var attrs = opts.attributes;
5783
5784 var getDist = function getDist(n1, n2) {
5785 return clusteringDistance(opts.distance, attrs.length, function (i) {
5786 return attrs[i](n1);
5787 }, function (i) {
5788 return attrs[i](n2);
5789 }, n1, n2);
5790 };
5791
5792 for (var i = 0; i < clusters.length; i++) {
5793 var key = clusters[i].key;
5794 var _dist = dists[key][mins[key]];
5795
5796 if (_dist < min) {
5797 minKey = key;
5798 min = _dist;
5799 }
5800 }
5801
5802 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
5803 return false;
5804 }
5805
5806 var c1 = index[minKey];
5807 var c2 = index[mins[minKey]];
5808 var merged; // Merge two closest clusters
5809
5810 if (opts.mode === 'dendrogram') {
5811 merged = {
5812 left: c1,
5813 right: c2,
5814 key: c1.key
5815 };
5816 } else {
5817 merged = {
5818 value: c1.value.concat(c2.value),
5819 key: c1.key
5820 };
5821 }
5822
5823 clusters[c1.index] = merged;
5824 clusters.splice(c2.index, 1);
5825 index[c1.key] = merged; // Update distances with new merged cluster
5826
5827 for (var _i = 0; _i < clusters.length; _i++) {
5828 var cur = clusters[_i];
5829
5830 if (c1.key === cur.key) {
5831 dist = Infinity;
5832 } else if (opts.linkage === 'min') {
5833 dist = dists[c1.key][cur.key];
5834
5835 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
5836 dist = dists[c2.key][cur.key];
5837 }
5838 } else if (opts.linkage === 'max') {
5839 dist = dists[c1.key][cur.key];
5840
5841 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
5842 dist = dists[c2.key][cur.key];
5843 }
5844 } else if (opts.linkage === 'mean') {
5845 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
5846 } else {
5847 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
5848 }
5849
5850 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
5851 } // Update cached mins
5852
5853
5854 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
5855 var key1 = clusters[_i2].key;
5856
5857 if (mins[key1] === c1.key || mins[key1] === c2.key) {
5858 var _min = key1;
5859
5860 for (var j = 0; j < clusters.length; j++) {
5861 var key2 = clusters[j].key;
5862
5863 if (dists[key1][key2] < dists[key1][_min]) {
5864 _min = key2;
5865 }
5866 }
5867
5868 mins[key1] = _min;
5869 }
5870
5871 clusters[_i2].index = _i2;
5872 } // Clean up meta data used for clustering
5873
5874
5875 c1.key = c2.key = c1.index = c2.index = null;
5876 return true;
5877 };
5878
5879 var getAllChildren = function getAllChildren(root, arr, cy) {
5880 if (!root) return;
5881
5882 if (root.value) {
5883 arr.push(root.value);
5884 } else {
5885 if (root.left) getAllChildren(root.left, arr);
5886 if (root.right) getAllChildren(root.right, arr);
5887 }
5888 };
5889
5890 var buildDendrogram = function buildDendrogram(root, cy) {
5891 if (!root) return '';
5892
5893 if (root.left && root.right) {
5894 var leftStr = buildDendrogram(root.left, cy);
5895 var rightStr = buildDendrogram(root.right, cy);
5896 var node = cy.add({
5897 group: 'nodes',
5898 data: {
5899 id: leftStr + ',' + rightStr
5900 }
5901 });
5902 cy.add({
5903 group: 'edges',
5904 data: {
5905 source: leftStr,
5906 target: node.id()
5907 }
5908 });
5909 cy.add({
5910 group: 'edges',
5911 data: {
5912 source: rightStr,
5913 target: node.id()
5914 }
5915 });
5916 return node.id();
5917 } else if (root.value) {
5918 return root.value.id();
5919 }
5920 };
5921
5922 var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5923 if (!root) return [];
5924 var left = [],
5925 right = [],
5926 leaves = [];
5927
5928 if (k === 0) {
5929 // don't cut tree, simply return all nodes as 1 single cluster
5930 if (root.left) getAllChildren(root.left, left);
5931 if (root.right) getAllChildren(root.right, right);
5932 leaves = left.concat(right);
5933 return [cy.collection(leaves)];
5934 } else if (k === 1) {
5935 // cut at root
5936 if (root.value) {
5937 // leaf node
5938 return [cy.collection(root.value)];
5939 } else {
5940 if (root.left) getAllChildren(root.left, left);
5941 if (root.right) getAllChildren(root.right, right);
5942 return [cy.collection(left), cy.collection(right)];
5943 }
5944 } else {
5945 if (root.value) {
5946 return [cy.collection(root.value)];
5947 } else {
5948 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5949 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5950 return left.concat(right);
5951 }
5952 }
5953 };
5954 /* eslint-enable */
5955
5956
5957 var hierarchicalClustering = function hierarchicalClustering(options) {
5958 var cy = this.cy();
5959 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5960
5961 var opts = setOptions$1(options);
5962 var attrs = opts.attributes;
5963
5964 var getDist = function getDist(n1, n2) {
5965 return clusteringDistance(opts.distance, attrs.length, function (i) {
5966 return attrs[i](n1);
5967 }, function (i) {
5968 return attrs[i](n2);
5969 }, n1, n2);
5970 }; // Begin hierarchical algorithm
5971
5972
5973 var clusters = [];
5974 var dists = []; // distances between each pair of clusters
5975
5976 var mins = []; // closest cluster for each cluster
5977
5978 var index = []; // hash of all clusters by key
5979 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5980
5981 for (var n = 0; n < nodes.length; n++) {
5982 var cluster = {
5983 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5984 key: n,
5985 index: n
5986 };
5987 clusters[n] = cluster;
5988 index[n] = cluster;
5989 dists[n] = [];
5990 mins[n] = 0;
5991 } // Calculate the distance between each pair of clusters
5992
5993
5994 for (var i = 0; i < clusters.length; i++) {
5995 for (var j = 0; j <= i; j++) {
5996 var dist = void 0;
5997
5998 if (opts.mode === 'dendrogram') {
5999 // modes store cluster values differently
6000 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
6001 } else {
6002 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
6003 }
6004
6005 dists[i][j] = dist;
6006 dists[j][i] = dist;
6007
6008 if (dist < dists[i][mins[i]]) {
6009 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
6010 }
6011 }
6012 } // Find the closest pair of clusters and merge them into a single cluster.
6013 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
6014
6015
6016 var merged = mergeClosest(clusters, index, dists, mins, opts);
6017
6018 while (merged) {
6019 merged = mergeClosest(clusters, index, dists, mins, opts);
6020 }
6021
6022 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
6023 // in addition to returning the clusters.
6024
6025 if (opts.mode === 'dendrogram') {
6026 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
6027 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
6028 } else {
6029 // Regular mode simply returns the clusters
6030 retClusters = new Array(clusters.length);
6031 clusters.forEach(function (cluster, i) {
6032 // Clean up meta data used for clustering
6033 cluster.key = cluster.index = null;
6034 retClusters[i] = cy.collection(cluster.value);
6035 });
6036 }
6037
6038 return retClusters;
6039 };
6040
6041 var hierarchicalClustering$1 = {
6042 hierarchicalClustering: hierarchicalClustering,
6043 hca: hierarchicalClustering
6044 };
6045
6046 // Implemented by Zoe Xi @zoexi for GSOC 2016
6047 var defaults$9 = defaults$g({
6048 distance: 'euclidean',
6049 // distance metric to compare attributes between two nodes
6050 preference: 'median',
6051 // suitability of a data point to serve as an exemplar
6052 damping: 0.8,
6053 // damping factor between [0.5, 1)
6054 maxIterations: 1000,
6055 // max number of iterations to run
6056 minIterations: 100,
6057 // min number of iterations to run in order for clustering to stop
6058 attributes: [// functions to quantify the similarity between any two points
6059 // e.g. node => node.data('weight')
6060 ]
6061 });
6062
6063 var setOptions = function setOptions(options) {
6064 var dmp = options.damping;
6065 var pref = options.preference;
6066
6067 if (!(0.5 <= dmp && dmp < 1)) {
6068 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
6069 }
6070
6071 var validPrefs = ['median', 'mean', 'min', 'max'];
6072
6073 if (!(validPrefs.some(function (v) {
6074 return v === pref;
6075 }) || number$1(pref))) {
6076 error("Preference must be one of [".concat(validPrefs.map(function (p) {
6077 return "'".concat(p, "'");
6078 }).join(', '), "] or a number. Got: ").concat(pref));
6079 }
6080
6081 return defaults$9(options);
6082 };
6083 /* eslint-enable */
6084
6085
6086 var getSimilarity = function getSimilarity(type, n1, n2, attributes) {
6087 var attr = function attr(n, i) {
6088 return attributes[i](n);
6089 }; // nb negative because similarity should have an inverse relationship to distance
6090
6091
6092 return -clusteringDistance(type, attributes.length, function (i) {
6093 return attr(n1, i);
6094 }, function (i) {
6095 return attr(n2, i);
6096 }, n1, n2);
6097 };
6098
6099 var getPreference = function getPreference(S, preference) {
6100 // larger preference = greater # of clusters
6101 var p = null;
6102
6103 if (preference === 'median') {
6104 p = median(S);
6105 } else if (preference === 'mean') {
6106 p = mean(S);
6107 } else if (preference === 'min') {
6108 p = min(S);
6109 } else if (preference === 'max') {
6110 p = max(S);
6111 } else {
6112 // Custom preference number, as set by user
6113 p = preference;
6114 }
6115
6116 return p;
6117 };
6118
6119 var findExemplars = function findExemplars(n, R, A) {
6120 var indices = [];
6121
6122 for (var i = 0; i < n; i++) {
6123 if (R[i * n + i] + A[i * n + i] > 0) {
6124 indices.push(i);
6125 }
6126 }
6127
6128 return indices;
6129 };
6130
6131 var assignClusters = function assignClusters(n, S, exemplars) {
6132 var clusters = [];
6133
6134 for (var i = 0; i < n; i++) {
6135 var index = -1;
6136 var max = -Infinity;
6137
6138 for (var ei = 0; ei < exemplars.length; ei++) {
6139 var e = exemplars[ei];
6140
6141 if (S[i * n + e] > max) {
6142 index = e;
6143 max = S[i * n + e];
6144 }
6145 }
6146
6147 if (index > 0) {
6148 clusters.push(index);
6149 }
6150 }
6151
6152 for (var _ei = 0; _ei < exemplars.length; _ei++) {
6153 clusters[exemplars[_ei]] = exemplars[_ei];
6154 }
6155
6156 return clusters;
6157 };
6158
6159 var assign = function assign(n, S, exemplars) {
6160 var clusters = assignClusters(n, S, exemplars);
6161
6162 for (var ei = 0; ei < exemplars.length; ei++) {
6163 var ii = [];
6164
6165 for (var c = 0; c < clusters.length; c++) {
6166 if (clusters[c] === exemplars[ei]) {
6167 ii.push(c);
6168 }
6169 }
6170
6171 var maxI = -1;
6172 var maxSum = -Infinity;
6173
6174 for (var i = 0; i < ii.length; i++) {
6175 var sum = 0;
6176
6177 for (var j = 0; j < ii.length; j++) {
6178 sum += S[ii[j] * n + ii[i]];
6179 }
6180
6181 if (sum > maxSum) {
6182 maxI = i;
6183 maxSum = sum;
6184 }
6185 }
6186
6187 exemplars[ei] = ii[maxI];
6188 }
6189
6190 clusters = assignClusters(n, S, exemplars);
6191 return clusters;
6192 };
6193
6194 var affinityPropagation = function affinityPropagation(options) {
6195 var cy = this.cy();
6196 var nodes = this.nodes();
6197 var opts = setOptions(options); // Map each node to its position in node array
6198
6199 var id2position = {};
6200
6201 for (var i = 0; i < nodes.length; i++) {
6202 id2position[nodes[i].id()] = i;
6203 } // Begin affinity propagation algorithm
6204
6205
6206 var n; // number of data points
6207
6208 var n2; // size of matrices
6209
6210 var S; // similarity matrix (1D array)
6211
6212 var p; // preference/suitability of a data point to serve as an exemplar
6213
6214 var R; // responsibility matrix (1D array)
6215
6216 var A; // availability matrix (1D array)
6217
6218 n = nodes.length;
6219 n2 = n * n; // Initialize and build S similarity matrix
6220
6221 S = new Array(n2);
6222
6223 for (var _i = 0; _i < n2; _i++) {
6224 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
6225 }
6226
6227 for (var _i2 = 0; _i2 < n; _i2++) {
6228 for (var j = 0; j < n; j++) {
6229 if (_i2 !== j) {
6230 S[_i2 * n + j] = getSimilarity(opts.distance, nodes[_i2], nodes[j], opts.attributes);
6231 }
6232 }
6233 } // Place preferences on the diagonal of S
6234
6235
6236 p = getPreference(S, opts.preference);
6237
6238 for (var _i3 = 0; _i3 < n; _i3++) {
6239 S[_i3 * n + _i3] = p;
6240 } // Initialize R responsibility matrix
6241
6242
6243 R = new Array(n2);
6244
6245 for (var _i4 = 0; _i4 < n2; _i4++) {
6246 R[_i4] = 0.0;
6247 } // Initialize A availability matrix
6248
6249
6250 A = new Array(n2);
6251
6252 for (var _i5 = 0; _i5 < n2; _i5++) {
6253 A[_i5] = 0.0;
6254 }
6255
6256 var old = new Array(n);
6257 var Rp = new Array(n);
6258 var se = new Array(n);
6259
6260 for (var _i6 = 0; _i6 < n; _i6++) {
6261 old[_i6] = 0.0;
6262 Rp[_i6] = 0.0;
6263 se[_i6] = 0;
6264 }
6265
6266 var e = new Array(n * opts.minIterations);
6267
6268 for (var _i7 = 0; _i7 < e.length; _i7++) {
6269 e[_i7] = 0;
6270 }
6271
6272 var iter;
6273
6274 for (iter = 0; iter < opts.maxIterations; iter++) {
6275 // main algorithmic loop
6276 // Update R responsibility matrix
6277 for (var _i8 = 0; _i8 < n; _i8++) {
6278 var max = -Infinity,
6279 max2 = -Infinity,
6280 maxI = -1,
6281 AS = 0.0;
6282
6283 for (var _j = 0; _j < n; _j++) {
6284 old[_j] = R[_i8 * n + _j];
6285 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
6286
6287 if (AS >= max) {
6288 max2 = max;
6289 max = AS;
6290 maxI = _j;
6291 } else if (AS > max2) {
6292 max2 = AS;
6293 }
6294 }
6295
6296 for (var _j2 = 0; _j2 < n; _j2++) {
6297 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
6298 }
6299
6300 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
6301 } // Update A availability matrix
6302
6303
6304 for (var _i9 = 0; _i9 < n; _i9++) {
6305 var sum = 0;
6306
6307 for (var _j3 = 0; _j3 < n; _j3++) {
6308 old[_j3] = A[_j3 * n + _i9];
6309 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
6310 sum += Rp[_j3];
6311 }
6312
6313 sum -= Rp[_i9];
6314 Rp[_i9] = R[_i9 * n + _i9];
6315 sum += Rp[_i9];
6316
6317 for (var _j4 = 0; _j4 < n; _j4++) {
6318 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
6319 }
6320
6321 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
6322 } // Check for convergence
6323
6324
6325 var K = 0;
6326
6327 for (var _i10 = 0; _i10 < n; _i10++) {
6328 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
6329 e[iter % opts.minIterations * n + _i10] = E;
6330 K += E;
6331 }
6332
6333 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
6334 var _sum = 0;
6335
6336 for (var _i11 = 0; _i11 < n; _i11++) {
6337 se[_i11] = 0;
6338
6339 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
6340 se[_i11] += e[_j5 * n + _i11];
6341 }
6342
6343 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
6344 _sum++;
6345 }
6346 }
6347
6348 if (_sum === n) {
6349 // then we have convergence
6350 break;
6351 }
6352 }
6353 } // Identify exemplars (cluster centers)
6354
6355
6356 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
6357
6358 var clusterIndices = assign(n, S, exemplarsIndices);
6359 var clusters = {};
6360
6361 for (var c = 0; c < exemplarsIndices.length; c++) {
6362 clusters[exemplarsIndices[c]] = [];
6363 }
6364
6365 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
6366 var pos = id2position[nodes[_i12].id()];
6367
6368 var clusterIndex = clusterIndices[pos];
6369
6370 if (clusterIndex != null) {
6371 // the node may have not been assigned a cluster if no valid attributes were specified
6372 clusters[clusterIndex].push(nodes[_i12]);
6373 }
6374 }
6375
6376 var retClusters = new Array(exemplarsIndices.length);
6377
6378 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
6379 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
6380 }
6381
6382 return retClusters;
6383 };
6384
6385 var affinityPropagation$1 = {
6386 affinityPropagation: affinityPropagation,
6387 ap: affinityPropagation
6388 };
6389
6390 var hierholzerDefaults = defaults$g({
6391 root: undefined,
6392 directed: false
6393 });
6394 var elesfn$k = {
6395 hierholzer: function hierholzer(options) {
6396 if (!plainObject(options)) {
6397 var args = arguments;
6398 options = {
6399 root: args[0],
6400 directed: args[1]
6401 };
6402 }
6403
6404 var _hierholzerDefaults = hierholzerDefaults(options),
6405 root = _hierholzerDefaults.root,
6406 directed = _hierholzerDefaults.directed;
6407
6408 var eles = this;
6409 var dflag = false;
6410 var oddIn;
6411 var oddOut;
6412 var startVertex;
6413 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
6414 var nodes = {};
6415 var edges = {};
6416
6417 if (directed) {
6418 eles.forEach(function (ele) {
6419 var id = ele.id();
6420
6421 if (ele.isNode()) {
6422 var ind = ele.indegree(true);
6423 var outd = ele.outdegree(true);
6424 var d1 = ind - outd;
6425 var d2 = outd - ind;
6426
6427 if (d1 == 1) {
6428 if (oddIn) dflag = true;else oddIn = id;
6429 } else if (d2 == 1) {
6430 if (oddOut) dflag = true;else oddOut = id;
6431 } else if (d2 > 1 || d1 > 1) {
6432 dflag = true;
6433 }
6434
6435 nodes[id] = [];
6436 ele.outgoers().forEach(function (e) {
6437 if (e.isEdge()) nodes[id].push(e.id());
6438 });
6439 } else {
6440 edges[id] = [undefined, ele.target().id()];
6441 }
6442 });
6443 } else {
6444 eles.forEach(function (ele) {
6445 var id = ele.id();
6446
6447 if (ele.isNode()) {
6448 var d = ele.degree(true);
6449
6450 if (d % 2) {
6451 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
6452 }
6453
6454 nodes[id] = [];
6455 ele.connectedEdges().forEach(function (e) {
6456 return nodes[id].push(e.id());
6457 });
6458 } else {
6459 edges[id] = [ele.source().id(), ele.target().id()];
6460 }
6461 });
6462 }
6463
6464 var result = {
6465 found: false,
6466 trail: undefined
6467 };
6468 if (dflag) return result;else if (oddOut && oddIn) {
6469 if (directed) {
6470 if (startVertex && oddOut != startVertex) {
6471 return result;
6472 }
6473
6474 startVertex = oddOut;
6475 } else {
6476 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
6477 return result;
6478 } else if (!startVertex) {
6479 startVertex = oddOut;
6480 }
6481 }
6482 } else {
6483 if (!startVertex) startVertex = eles[0].id();
6484 }
6485
6486 var walk = function walk(v) {
6487 var currentNode = v;
6488 var subtour = [v];
6489 var adj, adjTail, adjHead;
6490
6491 while (nodes[currentNode].length) {
6492 adj = nodes[currentNode].shift();
6493 adjTail = edges[adj][0];
6494 adjHead = edges[adj][1];
6495
6496 if (currentNode != adjHead) {
6497 nodes[adjHead] = nodes[adjHead].filter(function (e) {
6498 return e != adj;
6499 });
6500 currentNode = adjHead;
6501 } else if (!directed && currentNode != adjTail) {
6502 nodes[adjTail] = nodes[adjTail].filter(function (e) {
6503 return e != adj;
6504 });
6505 currentNode = adjTail;
6506 }
6507
6508 subtour.unshift(adj);
6509 subtour.unshift(currentNode);
6510 }
6511
6512 return subtour;
6513 };
6514
6515 var trail = [];
6516 var subtour = [];
6517 subtour = walk(startVertex);
6518
6519 while (subtour.length != 1) {
6520 if (nodes[subtour[0]].length == 0) {
6521 trail.unshift(eles.getElementById(subtour.shift()));
6522 trail.unshift(eles.getElementById(subtour.shift()));
6523 } else {
6524 subtour = walk(subtour.shift()).concat(subtour);
6525 }
6526 }
6527
6528 trail.unshift(eles.getElementById(subtour.shift())); // final node
6529
6530 for (var d in nodes) {
6531 if (nodes[d].length) {
6532 return result;
6533 }
6534 }
6535
6536 result.found = true;
6537 result.trail = this.spawn(trail, true);
6538 return result;
6539 }
6540 };
6541
6542 var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
6543 var eles = this;
6544 var nodes = {};
6545 var id = 0;
6546 var edgeCount = 0;
6547 var components = [];
6548 var stack = [];
6549 var visitedEdges = {};
6550
6551 var buildComponent = function buildComponent(x, y) {
6552 var i = stack.length - 1;
6553 var cutset = [];
6554 var component = eles.spawn();
6555
6556 while (stack[i].x != x || stack[i].y != y) {
6557 cutset.push(stack.pop().edge);
6558 i--;
6559 }
6560
6561 cutset.push(stack.pop().edge);
6562 cutset.forEach(function (edge) {
6563 var connectedNodes = edge.connectedNodes().intersection(eles);
6564 component.merge(edge);
6565 connectedNodes.forEach(function (node) {
6566 var nodeId = node.id();
6567 var connectedEdges = node.connectedEdges().intersection(eles);
6568 component.merge(node);
6569
6570 if (!nodes[nodeId].cutVertex) {
6571 component.merge(connectedEdges);
6572 } else {
6573 component.merge(connectedEdges.filter(function (edge) {
6574 return edge.isLoop();
6575 }));
6576 }
6577 });
6578 });
6579 components.push(component);
6580 };
6581
6582 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
6583 if (root === parent) edgeCount += 1;
6584 nodes[currentNode] = {
6585 id: id,
6586 low: id++,
6587 cutVertex: false
6588 };
6589 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
6590
6591 if (edges.size() === 0) {
6592 components.push(eles.spawn(eles.getElementById(currentNode)));
6593 } else {
6594 var sourceId, targetId, otherNodeId, edgeId;
6595 edges.forEach(function (edge) {
6596 sourceId = edge.source().id();
6597 targetId = edge.target().id();
6598 otherNodeId = sourceId === currentNode ? targetId : sourceId;
6599
6600 if (otherNodeId !== parent) {
6601 edgeId = edge.id();
6602
6603 if (!visitedEdges[edgeId]) {
6604 visitedEdges[edgeId] = true;
6605 stack.push({
6606 x: currentNode,
6607 y: otherNodeId,
6608 edge: edge
6609 });
6610 }
6611
6612 if (!(otherNodeId in nodes)) {
6613 biconnectedSearch(root, otherNodeId, currentNode);
6614 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
6615
6616 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
6617 nodes[currentNode].cutVertex = true;
6618 buildComponent(currentNode, otherNodeId);
6619 }
6620 } else {
6621 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
6622 }
6623 }
6624 });
6625 }
6626 };
6627
6628 eles.forEach(function (ele) {
6629 if (ele.isNode()) {
6630 var nodeId = ele.id();
6631
6632 if (!(nodeId in nodes)) {
6633 edgeCount = 0;
6634 biconnectedSearch(nodeId, nodeId);
6635 nodes[nodeId].cutVertex = edgeCount > 1;
6636 }
6637 }
6638 });
6639 var cutVertices = Object.keys(nodes).filter(function (id) {
6640 return nodes[id].cutVertex;
6641 }).map(function (id) {
6642 return eles.getElementById(id);
6643 });
6644 return {
6645 cut: eles.spawn(cutVertices),
6646 components: components
6647 };
6648 };
6649
6650 var hopcroftTarjanBiconnected$1 = {
6651 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
6652 htbc: hopcroftTarjanBiconnected,
6653 htb: hopcroftTarjanBiconnected,
6654 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
6655 };
6656
6657 var tarjanStronglyConnected = function tarjanStronglyConnected() {
6658 var eles = this;
6659 var nodes = {};
6660 var index = 0;
6661 var components = [];
6662 var stack = [];
6663 var cut = eles.spawn(eles);
6664
6665 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
6666 stack.push(sourceNodeId);
6667 nodes[sourceNodeId] = {
6668 index: index,
6669 low: index++,
6670 explored: false
6671 };
6672 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
6673 connectedEdges.forEach(function (edge) {
6674 var targetNodeId = edge.target().id();
6675
6676 if (targetNodeId !== sourceNodeId) {
6677 if (!(targetNodeId in nodes)) {
6678 stronglyConnectedSearch(targetNodeId);
6679 }
6680
6681 if (!nodes[targetNodeId].explored) {
6682 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
6683 }
6684 }
6685 });
6686
6687 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
6688 var componentNodes = eles.spawn();
6689
6690 for (;;) {
6691 var nodeId = stack.pop();
6692 componentNodes.merge(eles.getElementById(nodeId));
6693 nodes[nodeId].low = nodes[sourceNodeId].index;
6694 nodes[nodeId].explored = true;
6695
6696 if (nodeId === sourceNodeId) {
6697 break;
6698 }
6699 }
6700
6701 var componentEdges = componentNodes.edgesWith(componentNodes);
6702 var component = componentNodes.merge(componentEdges);
6703 components.push(component);
6704 cut = cut.difference(component);
6705 }
6706 };
6707
6708 eles.forEach(function (ele) {
6709 if (ele.isNode()) {
6710 var nodeId = ele.id();
6711
6712 if (!(nodeId in nodes)) {
6713 stronglyConnectedSearch(nodeId);
6714 }
6715 }
6716 });
6717 return {
6718 cut: cut,
6719 components: components
6720 };
6721 };
6722
6723 var tarjanStronglyConnected$1 = {
6724 tarjanStronglyConnected: tarjanStronglyConnected,
6725 tsc: tarjanStronglyConnected,
6726 tscc: tarjanStronglyConnected,
6727 tarjanStronglyConnectedComponents: tarjanStronglyConnected
6728 };
6729
6730 var elesfn$j = {};
6731 [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) {
6732 extend(elesfn$j, props);
6733 });
6734
6735 /*!
6736 Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
6737 Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
6738 Licensed under The MIT License (http://opensource.org/licenses/MIT)
6739 */
6740
6741 /* promise states [Promises/A+ 2.1] */
6742 var STATE_PENDING = 0;
6743 /* [Promises/A+ 2.1.1] */
6744
6745 var STATE_FULFILLED = 1;
6746 /* [Promises/A+ 2.1.2] */
6747
6748 var STATE_REJECTED = 2;
6749 /* [Promises/A+ 2.1.3] */
6750
6751 /* promise object constructor */
6752
6753 var api = function api(executor) {
6754 /* optionally support non-constructor/plain-function call */
6755 if (!(this instanceof api)) return new api(executor);
6756 /* initialize object */
6757
6758 this.id = 'Thenable/1.0.7';
6759 this.state = STATE_PENDING;
6760 /* initial state */
6761
6762 this.fulfillValue = undefined;
6763 /* initial value */
6764
6765 /* [Promises/A+ 1.3, 2.1.2.2] */
6766
6767 this.rejectReason = undefined;
6768 /* initial reason */
6769
6770 /* [Promises/A+ 1.5, 2.1.3.2] */
6771
6772 this.onFulfilled = [];
6773 /* initial handlers */
6774
6775 this.onRejected = [];
6776 /* initial handlers */
6777
6778 /* provide optional information-hiding proxy */
6779
6780 this.proxy = {
6781 then: this.then.bind(this)
6782 };
6783 /* support optional executor function */
6784
6785 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
6786 };
6787 /* promise API methods */
6788
6789
6790 api.prototype = {
6791 /* promise resolving methods */
6792 fulfill: function fulfill(value) {
6793 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
6794 },
6795 reject: function reject(value) {
6796 return deliver(this, STATE_REJECTED, 'rejectReason', value);
6797 },
6798
6799 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
6800 then: function then(onFulfilled, onRejected) {
6801 var curr = this;
6802 var next = new api();
6803 /* [Promises/A+ 2.2.7] */
6804
6805 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
6806 /* [Promises/A+ 2.2.2/2.2.6] */
6807
6808 curr.onRejected.push(resolver(onRejected, next, 'reject'));
6809 /* [Promises/A+ 2.2.3/2.2.6] */
6810
6811 execute(curr);
6812 return next.proxy;
6813 /* [Promises/A+ 2.2.7, 3.3] */
6814 }
6815 };
6816 /* deliver an action */
6817
6818 var deliver = function deliver(curr, state, name, value) {
6819 if (curr.state === STATE_PENDING) {
6820 curr.state = state;
6821 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
6822
6823 curr[name] = value;
6824 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
6825
6826 execute(curr);
6827 }
6828
6829 return curr;
6830 };
6831 /* execute all handlers */
6832
6833
6834 var execute = function execute(curr) {
6835 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
6836 };
6837 /* execute particular set of handlers */
6838
6839
6840 var execute_handlers = function execute_handlers(curr, name, value) {
6841 /* global setImmediate: true */
6842
6843 /* global setTimeout: true */
6844
6845 /* short-circuit processing */
6846 if (curr[name].length === 0) return;
6847 /* iterate over all handlers, exactly once */
6848
6849 var handlers = curr[name];
6850 curr[name] = [];
6851 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
6852
6853 var func = function func() {
6854 for (var i = 0; i < handlers.length; i++) {
6855 handlers[i](value);
6856 }
6857 /* [Promises/A+ 2.2.5] */
6858
6859 };
6860 /* execute procedure asynchronously */
6861
6862 /* [Promises/A+ 2.2.4, 3.1] */
6863
6864
6865 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
6866 };
6867 /* generate a resolver function */
6868
6869
6870 var resolver = function resolver(cb, next, method) {
6871 return function (value) {
6872 if (typeof cb !== 'function')
6873 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
6874 next[method].call(next, value);
6875 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
6876 else {
6877 var result;
6878
6879 try {
6880 result = cb(value);
6881 }
6882 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
6883 catch (e) {
6884 next.reject(e);
6885 /* [Promises/A+ 2.2.7.2] */
6886
6887 return;
6888 }
6889
6890 resolve(next, result);
6891 /* [Promises/A+ 2.2.7.1] */
6892 }
6893 };
6894 };
6895 /* "Promise Resolution Procedure" */
6896
6897 /* [Promises/A+ 2.3] */
6898
6899
6900 var resolve = function resolve(promise, x) {
6901 /* sanity check arguments */
6902
6903 /* [Promises/A+ 2.3.1] */
6904 if (promise === x || promise.proxy === x) {
6905 promise.reject(new TypeError('cannot resolve promise with itself'));
6906 return;
6907 }
6908 /* surgically check for a "then" method
6909 (mainly to just call the "getter" of "then" only once) */
6910
6911
6912 var then;
6913
6914 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6915 try {
6916 then = x.then;
6917 }
6918 /* [Promises/A+ 2.3.3.1, 3.5] */
6919 catch (e) {
6920 promise.reject(e);
6921 /* [Promises/A+ 2.3.3.2] */
6922
6923 return;
6924 }
6925 }
6926 /* handle own Thenables [Promises/A+ 2.3.2]
6927 and similar "thenables" [Promises/A+ 2.3.3] */
6928
6929
6930 if (typeof then === 'function') {
6931 var resolved = false;
6932
6933 try {
6934 /* call retrieved "then" method */
6935
6936 /* [Promises/A+ 2.3.3.3] */
6937 then.call(x,
6938 /* resolvePromise */
6939
6940 /* [Promises/A+ 2.3.3.3.1] */
6941 function (y) {
6942 if (resolved) return;
6943 resolved = true;
6944 /* [Promises/A+ 2.3.3.3.3] */
6945
6946 if (y === x)
6947 /* [Promises/A+ 3.6] */
6948 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6949 },
6950 /* rejectPromise */
6951
6952 /* [Promises/A+ 2.3.3.3.2] */
6953 function (r) {
6954 if (resolved) return;
6955 resolved = true;
6956 /* [Promises/A+ 2.3.3.3.3] */
6957
6958 promise.reject(r);
6959 });
6960 } catch (e) {
6961 if (!resolved)
6962 /* [Promises/A+ 2.3.3.3.3] */
6963 promise.reject(e);
6964 /* [Promises/A+ 2.3.3.3.4] */
6965 }
6966
6967 return;
6968 }
6969 /* handle other values */
6970
6971
6972 promise.fulfill(x);
6973 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6974 }; // so we always have Promise.all()
6975
6976
6977 api.all = function (ps) {
6978 return new api(function (resolveAll, rejectAll) {
6979 var vals = new Array(ps.length);
6980 var doneCount = 0;
6981
6982 var fulfill = function fulfill(i, val) {
6983 vals[i] = val;
6984 doneCount++;
6985
6986 if (doneCount === ps.length) {
6987 resolveAll(vals);
6988 }
6989 };
6990
6991 for (var i = 0; i < ps.length; i++) {
6992 (function (i) {
6993 var p = ps[i];
6994 var isPromise = p != null && p.then != null;
6995
6996 if (isPromise) {
6997 p.then(function (val) {
6998 fulfill(i, val);
6999 }, function (err) {
7000 rejectAll(err);
7001 });
7002 } else {
7003 var val = p;
7004 fulfill(i, val);
7005 }
7006 })(i);
7007 }
7008 });
7009 };
7010
7011 api.resolve = function (val) {
7012 return new api(function (resolve, reject) {
7013 resolve(val);
7014 });
7015 };
7016
7017 api.reject = function (val) {
7018 return new api(function (resolve, reject) {
7019 reject(val);
7020 });
7021 };
7022
7023 var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
7024
7025 var Animation = function Animation(target, opts, opts2) {
7026 var isCore = core(target);
7027 var isEle = !isCore;
7028
7029 var _p = this._private = extend({
7030 duration: 1000
7031 }, opts, opts2);
7032
7033 _p.target = target;
7034 _p.style = _p.style || _p.css;
7035 _p.started = false;
7036 _p.playing = false;
7037 _p.hooked = false;
7038 _p.applying = false;
7039 _p.progress = 0;
7040 _p.completes = [];
7041 _p.frames = [];
7042
7043 if (_p.complete && fn$6(_p.complete)) {
7044 _p.completes.push(_p.complete);
7045 }
7046
7047 if (isEle) {
7048 var pos = target.position();
7049 _p.startPosition = _p.startPosition || {
7050 x: pos.x,
7051 y: pos.y
7052 };
7053 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
7054 }
7055
7056 if (isCore) {
7057 var pan = target.pan();
7058 _p.startPan = {
7059 x: pan.x,
7060 y: pan.y
7061 };
7062 _p.startZoom = target.zoom();
7063 } // for future timeline/animations impl
7064
7065
7066 this.length = 1;
7067 this[0] = this;
7068 };
7069
7070 var anifn = Animation.prototype;
7071 extend(anifn, {
7072 instanceString: function instanceString() {
7073 return 'animation';
7074 },
7075 hook: function hook() {
7076 var _p = this._private;
7077
7078 if (!_p.hooked) {
7079 // add to target's animation queue
7080 var q;
7081 var tAni = _p.target._private.animation;
7082
7083 if (_p.queue) {
7084 q = tAni.queue;
7085 } else {
7086 q = tAni.current;
7087 }
7088
7089 q.push(this); // add to the animation loop pool
7090
7091 if (elementOrCollection(_p.target)) {
7092 _p.target.cy().addToAnimationPool(_p.target);
7093 }
7094
7095 _p.hooked = true;
7096 }
7097
7098 return this;
7099 },
7100 play: function play() {
7101 var _p = this._private; // autorewind
7102
7103 if (_p.progress === 1) {
7104 _p.progress = 0;
7105 }
7106
7107 _p.playing = true;
7108 _p.started = false; // needs to be started by animation loop
7109
7110 _p.stopped = false;
7111 this.hook(); // the animation loop will start the animation...
7112
7113 return this;
7114 },
7115 playing: function playing() {
7116 return this._private.playing;
7117 },
7118 apply: function apply() {
7119 var _p = this._private;
7120 _p.applying = true;
7121 _p.started = false; // needs to be started by animation loop
7122
7123 _p.stopped = false;
7124 this.hook(); // the animation loop will apply the animation at this progress
7125
7126 return this;
7127 },
7128 applying: function applying() {
7129 return this._private.applying;
7130 },
7131 pause: function pause() {
7132 var _p = this._private;
7133 _p.playing = false;
7134 _p.started = false;
7135 return this;
7136 },
7137 stop: function stop() {
7138 var _p = this._private;
7139 _p.playing = false;
7140 _p.started = false;
7141 _p.stopped = true; // to be removed from animation queues
7142
7143 return this;
7144 },
7145 rewind: function rewind() {
7146 return this.progress(0);
7147 },
7148 fastforward: function fastforward() {
7149 return this.progress(1);
7150 },
7151 time: function time(t) {
7152 var _p = this._private;
7153
7154 if (t === undefined) {
7155 return _p.progress * _p.duration;
7156 } else {
7157 return this.progress(t / _p.duration);
7158 }
7159 },
7160 progress: function progress(p) {
7161 var _p = this._private;
7162 var wasPlaying = _p.playing;
7163
7164 if (p === undefined) {
7165 return _p.progress;
7166 } else {
7167 if (wasPlaying) {
7168 this.pause();
7169 }
7170
7171 _p.progress = p;
7172 _p.started = false;
7173
7174 if (wasPlaying) {
7175 this.play();
7176 }
7177 }
7178
7179 return this;
7180 },
7181 completed: function completed() {
7182 return this._private.progress === 1;
7183 },
7184 reverse: function reverse() {
7185 var _p = this._private;
7186 var wasPlaying = _p.playing;
7187
7188 if (wasPlaying) {
7189 this.pause();
7190 }
7191
7192 _p.progress = 1 - _p.progress;
7193 _p.started = false;
7194
7195 var swap = function swap(a, b) {
7196 var _pa = _p[a];
7197
7198 if (_pa == null) {
7199 return;
7200 }
7201
7202 _p[a] = _p[b];
7203 _p[b] = _pa;
7204 };
7205
7206 swap('zoom', 'startZoom');
7207 swap('pan', 'startPan');
7208 swap('position', 'startPosition'); // swap styles
7209
7210 if (_p.style) {
7211 for (var i = 0; i < _p.style.length; i++) {
7212 var prop = _p.style[i];
7213 var name = prop.name;
7214 var startStyleProp = _p.startStyle[name];
7215 _p.startStyle[name] = prop;
7216 _p.style[i] = startStyleProp;
7217 }
7218 }
7219
7220 if (wasPlaying) {
7221 this.play();
7222 }
7223
7224 return this;
7225 },
7226 promise: function promise(type) {
7227 var _p = this._private;
7228 var arr;
7229
7230 switch (type) {
7231 case 'frame':
7232 arr = _p.frames;
7233 break;
7234
7235 default:
7236 case 'complete':
7237 case 'completed':
7238 arr = _p.completes;
7239 }
7240
7241 return new Promise$1(function (resolve, reject) {
7242 arr.push(function () {
7243 resolve();
7244 });
7245 });
7246 }
7247 });
7248 anifn.complete = anifn.completed;
7249 anifn.run = anifn.play;
7250 anifn.running = anifn.playing;
7251
7252 var define$3 = {
7253 animated: function animated() {
7254 return function animatedImpl() {
7255 var self = this;
7256 var selfIsArrayLike = self.length !== undefined;
7257 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7258
7259 var cy = this._private.cy || this;
7260
7261 if (!cy.styleEnabled()) {
7262 return false;
7263 }
7264
7265 var ele = all[0];
7266
7267 if (ele) {
7268 return ele._private.animation.current.length > 0;
7269 }
7270 };
7271 },
7272 // animated
7273 clearQueue: function clearQueue() {
7274 return function clearQueueImpl() {
7275 var self = this;
7276 var selfIsArrayLike = self.length !== undefined;
7277 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7278
7279 var cy = this._private.cy || this;
7280
7281 if (!cy.styleEnabled()) {
7282 return this;
7283 }
7284
7285 for (var i = 0; i < all.length; i++) {
7286 var ele = all[i];
7287 ele._private.animation.queue = [];
7288 }
7289
7290 return this;
7291 };
7292 },
7293 // clearQueue
7294 delay: function delay() {
7295 return function delayImpl(time, complete) {
7296 var cy = this._private.cy || this;
7297
7298 if (!cy.styleEnabled()) {
7299 return this;
7300 }
7301
7302 return this.animate({
7303 delay: time,
7304 duration: time,
7305 complete: complete
7306 });
7307 };
7308 },
7309 // delay
7310 delayAnimation: function delayAnimation() {
7311 return function delayAnimationImpl(time, complete) {
7312 var cy = this._private.cy || this;
7313
7314 if (!cy.styleEnabled()) {
7315 return this;
7316 }
7317
7318 return this.animation({
7319 delay: time,
7320 duration: time,
7321 complete: complete
7322 });
7323 };
7324 },
7325 // delay
7326 animation: function animation() {
7327 return function animationImpl(properties, params) {
7328 var self = this;
7329 var selfIsArrayLike = self.length !== undefined;
7330 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7331
7332 var cy = this._private.cy || this;
7333 var isCore = !selfIsArrayLike;
7334 var isEles = !isCore;
7335
7336 if (!cy.styleEnabled()) {
7337 return this;
7338 }
7339
7340 var style = cy.style();
7341 properties = extend({}, properties, params);
7342 var propertiesEmpty = Object.keys(properties).length === 0;
7343
7344 if (propertiesEmpty) {
7345 return new Animation(all[0], properties); // nothing to animate
7346 }
7347
7348 if (properties.duration === undefined) {
7349 properties.duration = 400;
7350 }
7351
7352 switch (properties.duration) {
7353 case 'slow':
7354 properties.duration = 600;
7355 break;
7356
7357 case 'fast':
7358 properties.duration = 200;
7359 break;
7360 }
7361
7362 if (isEles) {
7363 properties.style = style.getPropsList(properties.style || properties.css);
7364 properties.css = undefined;
7365 }
7366
7367 if (isEles && properties.renderedPosition != null) {
7368 var rpos = properties.renderedPosition;
7369 var pan = cy.pan();
7370 var zoom = cy.zoom();
7371 properties.position = renderedToModelPosition(rpos, zoom, pan);
7372 } // override pan w/ panBy if set
7373
7374
7375 if (isCore && properties.panBy != null) {
7376 var panBy = properties.panBy;
7377 var cyPan = cy.pan();
7378 properties.pan = {
7379 x: cyPan.x + panBy.x,
7380 y: cyPan.y + panBy.y
7381 };
7382 } // override pan w/ center if set
7383
7384
7385 var center = properties.center || properties.centre;
7386
7387 if (isCore && center != null) {
7388 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
7389
7390 if (centerPan != null) {
7391 properties.pan = centerPan;
7392 }
7393 } // override pan & zoom w/ fit if set
7394
7395
7396 if (isCore && properties.fit != null) {
7397 var fit = properties.fit;
7398 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
7399
7400 if (fitVp != null) {
7401 properties.pan = fitVp.pan;
7402 properties.zoom = fitVp.zoom;
7403 }
7404 } // override zoom (& potentially pan) w/ zoom obj if set
7405
7406
7407 if (isCore && plainObject(properties.zoom)) {
7408 var vp = cy.getZoomedViewport(properties.zoom);
7409
7410 if (vp != null) {
7411 if (vp.zoomed) {
7412 properties.zoom = vp.zoom;
7413 }
7414
7415 if (vp.panned) {
7416 properties.pan = vp.pan;
7417 }
7418 } else {
7419 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
7420 }
7421 }
7422
7423 return new Animation(all[0], properties);
7424 };
7425 },
7426 // animate
7427 animate: function animate() {
7428 return function animateImpl(properties, params) {
7429 var self = this;
7430 var selfIsArrayLike = self.length !== undefined;
7431 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7432
7433 var cy = this._private.cy || this;
7434
7435 if (!cy.styleEnabled()) {
7436 return this;
7437 }
7438
7439 if (params) {
7440 properties = extend({}, properties, params);
7441 } // manually hook and run the animation
7442
7443
7444 for (var i = 0; i < all.length; i++) {
7445 var ele = all[i];
7446 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
7447 var ani = ele.animation(properties, queue ? {
7448 queue: true
7449 } : undefined);
7450 ani.play();
7451 }
7452
7453 return this; // chaining
7454 };
7455 },
7456 // animate
7457 stop: function stop() {
7458 return function stopImpl(clearQueue, jumpToEnd) {
7459 var self = this;
7460 var selfIsArrayLike = self.length !== undefined;
7461 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7462
7463 var cy = this._private.cy || this;
7464
7465 if (!cy.styleEnabled()) {
7466 return this;
7467 }
7468
7469 for (var i = 0; i < all.length; i++) {
7470 var ele = all[i];
7471 var _p = ele._private;
7472 var anis = _p.animation.current;
7473
7474 for (var j = 0; j < anis.length; j++) {
7475 var ani = anis[j];
7476 var ani_p = ani._private;
7477
7478 if (jumpToEnd) {
7479 // next iteration of the animation loop, the animation
7480 // will go straight to the end and be removed
7481 ani_p.duration = 0;
7482 }
7483 } // clear the queue of future animations
7484
7485
7486 if (clearQueue) {
7487 _p.animation.queue = [];
7488 }
7489
7490 if (!jumpToEnd) {
7491 _p.animation.current = [];
7492 }
7493 } // we have to notify (the animation loop doesn't do it for us on `stop`)
7494
7495
7496 cy.notify('draw');
7497 return this;
7498 };
7499 } // stop
7500
7501 }; // define
7502
7503 /**
7504 * Checks if `value` is classified as an `Array` object.
7505 *
7506 * @static
7507 * @memberOf _
7508 * @since 0.1.0
7509 * @category Lang
7510 * @param {*} value The value to check.
7511 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
7512 * @example
7513 *
7514 * _.isArray([1, 2, 3]);
7515 * // => true
7516 *
7517 * _.isArray(document.body.children);
7518 * // => false
7519 *
7520 * _.isArray('abc');
7521 * // => false
7522 *
7523 * _.isArray(_.noop);
7524 * // => false
7525 */
7526 var isArray = Array.isArray;
7527
7528 var isArray_1 = isArray;
7529
7530 /** Used to match property names within property paths. */
7531 var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
7532 reIsPlainProp = /^\w*$/;
7533
7534 /**
7535 * Checks if `value` is a property name and not a property path.
7536 *
7537 * @private
7538 * @param {*} value The value to check.
7539 * @param {Object} [object] The object to query keys on.
7540 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
7541 */
7542 function isKey(value, object) {
7543 if (isArray_1(value)) {
7544 return false;
7545 }
7546 var type = typeof value;
7547 if (type == 'number' || type == 'symbol' || type == 'boolean' ||
7548 value == null || isSymbol_1(value)) {
7549 return true;
7550 }
7551 return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
7552 (object != null && value in Object(object));
7553 }
7554
7555 var _isKey = isKey;
7556
7557 /** `Object#toString` result references. */
7558 var asyncTag = '[object AsyncFunction]',
7559 funcTag = '[object Function]',
7560 genTag = '[object GeneratorFunction]',
7561 proxyTag = '[object Proxy]';
7562
7563 /**
7564 * Checks if `value` is classified as a `Function` object.
7565 *
7566 * @static
7567 * @memberOf _
7568 * @since 0.1.0
7569 * @category Lang
7570 * @param {*} value The value to check.
7571 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
7572 * @example
7573 *
7574 * _.isFunction(_);
7575 * // => true
7576 *
7577 * _.isFunction(/abc/);
7578 * // => false
7579 */
7580 function isFunction(value) {
7581 if (!isObject_1(value)) {
7582 return false;
7583 }
7584 // The use of `Object#toString` avoids issues with the `typeof` operator
7585 // in Safari 9 which returns 'object' for typed arrays and other constructors.
7586 var tag = _baseGetTag(value);
7587 return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
7588 }
7589
7590 var isFunction_1 = isFunction;
7591
7592 /** Used to detect overreaching core-js shims. */
7593 var coreJsData = _root['__core-js_shared__'];
7594
7595 var _coreJsData = coreJsData;
7596
7597 /** Used to detect methods masquerading as native. */
7598 var maskSrcKey = (function() {
7599 var uid = /[^.]+$/.exec(_coreJsData && _coreJsData.keys && _coreJsData.keys.IE_PROTO || '');
7600 return uid ? ('Symbol(src)_1.' + uid) : '';
7601 }());
7602
7603 /**
7604 * Checks if `func` has its source masked.
7605 *
7606 * @private
7607 * @param {Function} func The function to check.
7608 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
7609 */
7610 function isMasked(func) {
7611 return !!maskSrcKey && (maskSrcKey in func);
7612 }
7613
7614 var _isMasked = isMasked;
7615
7616 /** Used for built-in method references. */
7617 var funcProto$1 = Function.prototype;
7618
7619 /** Used to resolve the decompiled source of functions. */
7620 var funcToString$1 = funcProto$1.toString;
7621
7622 /**
7623 * Converts `func` to its source code.
7624 *
7625 * @private
7626 * @param {Function} func The function to convert.
7627 * @returns {string} Returns the source code.
7628 */
7629 function toSource(func) {
7630 if (func != null) {
7631 try {
7632 return funcToString$1.call(func);
7633 } catch (e) {}
7634 try {
7635 return (func + '');
7636 } catch (e) {}
7637 }
7638 return '';
7639 }
7640
7641 var _toSource = toSource;
7642
7643 /**
7644 * Used to match `RegExp`
7645 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
7646 */
7647 var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
7648
7649 /** Used to detect host constructors (Safari). */
7650 var reIsHostCtor = /^\[object .+?Constructor\]$/;
7651
7652 /** Used for built-in method references. */
7653 var funcProto = Function.prototype,
7654 objectProto$3 = Object.prototype;
7655
7656 /** Used to resolve the decompiled source of functions. */
7657 var funcToString = funcProto.toString;
7658
7659 /** Used to check objects for own properties. */
7660 var hasOwnProperty$3 = objectProto$3.hasOwnProperty;
7661
7662 /** Used to detect if a method is native. */
7663 var reIsNative = RegExp('^' +
7664 funcToString.call(hasOwnProperty$3).replace(reRegExpChar, '\\$&')
7665 .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
7666 );
7667
7668 /**
7669 * The base implementation of `_.isNative` without bad shim checks.
7670 *
7671 * @private
7672 * @param {*} value The value to check.
7673 * @returns {boolean} Returns `true` if `value` is a native function,
7674 * else `false`.
7675 */
7676 function baseIsNative(value) {
7677 if (!isObject_1(value) || _isMasked(value)) {
7678 return false;
7679 }
7680 var pattern = isFunction_1(value) ? reIsNative : reIsHostCtor;
7681 return pattern.test(_toSource(value));
7682 }
7683
7684 var _baseIsNative = baseIsNative;
7685
7686 /**
7687 * Gets the value at `key` of `object`.
7688 *
7689 * @private
7690 * @param {Object} [object] The object to query.
7691 * @param {string} key The key of the property to get.
7692 * @returns {*} Returns the property value.
7693 */
7694 function getValue$1(object, key) {
7695 return object == null ? undefined : object[key];
7696 }
7697
7698 var _getValue = getValue$1;
7699
7700 /**
7701 * Gets the native function at `key` of `object`.
7702 *
7703 * @private
7704 * @param {Object} object The object to query.
7705 * @param {string} key The key of the method to get.
7706 * @returns {*} Returns the function if it's native, else `undefined`.
7707 */
7708 function getNative(object, key) {
7709 var value = _getValue(object, key);
7710 return _baseIsNative(value) ? value : undefined;
7711 }
7712
7713 var _getNative = getNative;
7714
7715 /* Built-in method references that are verified to be native. */
7716 var nativeCreate = _getNative(Object, 'create');
7717
7718 var _nativeCreate = nativeCreate;
7719
7720 /**
7721 * Removes all key-value entries from the hash.
7722 *
7723 * @private
7724 * @name clear
7725 * @memberOf Hash
7726 */
7727 function hashClear() {
7728 this.__data__ = _nativeCreate ? _nativeCreate(null) : {};
7729 this.size = 0;
7730 }
7731
7732 var _hashClear = hashClear;
7733
7734 /**
7735 * Removes `key` and its value from the hash.
7736 *
7737 * @private
7738 * @name delete
7739 * @memberOf Hash
7740 * @param {Object} hash The hash to modify.
7741 * @param {string} key The key of the value to remove.
7742 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
7743 */
7744 function hashDelete(key) {
7745 var result = this.has(key) && delete this.__data__[key];
7746 this.size -= result ? 1 : 0;
7747 return result;
7748 }
7749
7750 var _hashDelete = hashDelete;
7751
7752 /** Used to stand-in for `undefined` hash values. */
7753 var HASH_UNDEFINED$1 = '__lodash_hash_undefined__';
7754
7755 /** Used for built-in method references. */
7756 var objectProto$2 = Object.prototype;
7757
7758 /** Used to check objects for own properties. */
7759 var hasOwnProperty$2 = objectProto$2.hasOwnProperty;
7760
7761 /**
7762 * Gets the hash value for `key`.
7763 *
7764 * @private
7765 * @name get
7766 * @memberOf Hash
7767 * @param {string} key The key of the value to get.
7768 * @returns {*} Returns the entry value.
7769 */
7770 function hashGet(key) {
7771 var data = this.__data__;
7772 if (_nativeCreate) {
7773 var result = data[key];
7774 return result === HASH_UNDEFINED$1 ? undefined : result;
7775 }
7776 return hasOwnProperty$2.call(data, key) ? data[key] : undefined;
7777 }
7778
7779 var _hashGet = hashGet;
7780
7781 /** Used for built-in method references. */
7782 var objectProto$1 = Object.prototype;
7783
7784 /** Used to check objects for own properties. */
7785 var hasOwnProperty$1 = objectProto$1.hasOwnProperty;
7786
7787 /**
7788 * Checks if a hash value for `key` exists.
7789 *
7790 * @private
7791 * @name has
7792 * @memberOf Hash
7793 * @param {string} key The key of the entry to check.
7794 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7795 */
7796 function hashHas(key) {
7797 var data = this.__data__;
7798 return _nativeCreate ? (data[key] !== undefined) : hasOwnProperty$1.call(data, key);
7799 }
7800
7801 var _hashHas = hashHas;
7802
7803 /** Used to stand-in for `undefined` hash values. */
7804 var HASH_UNDEFINED = '__lodash_hash_undefined__';
7805
7806 /**
7807 * Sets the hash `key` to `value`.
7808 *
7809 * @private
7810 * @name set
7811 * @memberOf Hash
7812 * @param {string} key The key of the value to set.
7813 * @param {*} value The value to set.
7814 * @returns {Object} Returns the hash instance.
7815 */
7816 function hashSet(key, value) {
7817 var data = this.__data__;
7818 this.size += this.has(key) ? 0 : 1;
7819 data[key] = (_nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
7820 return this;
7821 }
7822
7823 var _hashSet = hashSet;
7824
7825 /**
7826 * Creates a hash object.
7827 *
7828 * @private
7829 * @constructor
7830 * @param {Array} [entries] The key-value pairs to cache.
7831 */
7832 function Hash(entries) {
7833 var index = -1,
7834 length = entries == null ? 0 : entries.length;
7835
7836 this.clear();
7837 while (++index < length) {
7838 var entry = entries[index];
7839 this.set(entry[0], entry[1]);
7840 }
7841 }
7842
7843 // Add methods to `Hash`.
7844 Hash.prototype.clear = _hashClear;
7845 Hash.prototype['delete'] = _hashDelete;
7846 Hash.prototype.get = _hashGet;
7847 Hash.prototype.has = _hashHas;
7848 Hash.prototype.set = _hashSet;
7849
7850 var _Hash = Hash;
7851
7852 /**
7853 * Removes all key-value entries from the list cache.
7854 *
7855 * @private
7856 * @name clear
7857 * @memberOf ListCache
7858 */
7859 function listCacheClear() {
7860 this.__data__ = [];
7861 this.size = 0;
7862 }
7863
7864 var _listCacheClear = listCacheClear;
7865
7866 /**
7867 * Performs a
7868 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
7869 * comparison between two values to determine if they are equivalent.
7870 *
7871 * @static
7872 * @memberOf _
7873 * @since 4.0.0
7874 * @category Lang
7875 * @param {*} value The value to compare.
7876 * @param {*} other The other value to compare.
7877 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
7878 * @example
7879 *
7880 * var object = { 'a': 1 };
7881 * var other = { 'a': 1 };
7882 *
7883 * _.eq(object, object);
7884 * // => true
7885 *
7886 * _.eq(object, other);
7887 * // => false
7888 *
7889 * _.eq('a', 'a');
7890 * // => true
7891 *
7892 * _.eq('a', Object('a'));
7893 * // => false
7894 *
7895 * _.eq(NaN, NaN);
7896 * // => true
7897 */
7898 function eq(value, other) {
7899 return value === other || (value !== value && other !== other);
7900 }
7901
7902 var eq_1 = eq;
7903
7904 /**
7905 * Gets the index at which the `key` is found in `array` of key-value pairs.
7906 *
7907 * @private
7908 * @param {Array} array The array to inspect.
7909 * @param {*} key The key to search for.
7910 * @returns {number} Returns the index of the matched value, else `-1`.
7911 */
7912 function assocIndexOf(array, key) {
7913 var length = array.length;
7914 while (length--) {
7915 if (eq_1(array[length][0], key)) {
7916 return length;
7917 }
7918 }
7919 return -1;
7920 }
7921
7922 var _assocIndexOf = assocIndexOf;
7923
7924 /** Used for built-in method references. */
7925 var arrayProto = Array.prototype;
7926
7927 /** Built-in value references. */
7928 var splice = arrayProto.splice;
7929
7930 /**
7931 * Removes `key` and its value from the list cache.
7932 *
7933 * @private
7934 * @name delete
7935 * @memberOf ListCache
7936 * @param {string} key The key of the value to remove.
7937 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
7938 */
7939 function listCacheDelete(key) {
7940 var data = this.__data__,
7941 index = _assocIndexOf(data, key);
7942
7943 if (index < 0) {
7944 return false;
7945 }
7946 var lastIndex = data.length - 1;
7947 if (index == lastIndex) {
7948 data.pop();
7949 } else {
7950 splice.call(data, index, 1);
7951 }
7952 --this.size;
7953 return true;
7954 }
7955
7956 var _listCacheDelete = listCacheDelete;
7957
7958 /**
7959 * Gets the list cache value for `key`.
7960 *
7961 * @private
7962 * @name get
7963 * @memberOf ListCache
7964 * @param {string} key The key of the value to get.
7965 * @returns {*} Returns the entry value.
7966 */
7967 function listCacheGet(key) {
7968 var data = this.__data__,
7969 index = _assocIndexOf(data, key);
7970
7971 return index < 0 ? undefined : data[index][1];
7972 }
7973
7974 var _listCacheGet = listCacheGet;
7975
7976 /**
7977 * Checks if a list cache value for `key` exists.
7978 *
7979 * @private
7980 * @name has
7981 * @memberOf ListCache
7982 * @param {string} key The key of the entry to check.
7983 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7984 */
7985 function listCacheHas(key) {
7986 return _assocIndexOf(this.__data__, key) > -1;
7987 }
7988
7989 var _listCacheHas = listCacheHas;
7990
7991 /**
7992 * Sets the list cache `key` to `value`.
7993 *
7994 * @private
7995 * @name set
7996 * @memberOf ListCache
7997 * @param {string} key The key of the value to set.
7998 * @param {*} value The value to set.
7999 * @returns {Object} Returns the list cache instance.
8000 */
8001 function listCacheSet(key, value) {
8002 var data = this.__data__,
8003 index = _assocIndexOf(data, key);
8004
8005 if (index < 0) {
8006 ++this.size;
8007 data.push([key, value]);
8008 } else {
8009 data[index][1] = value;
8010 }
8011 return this;
8012 }
8013
8014 var _listCacheSet = listCacheSet;
8015
8016 /**
8017 * Creates an list cache object.
8018 *
8019 * @private
8020 * @constructor
8021 * @param {Array} [entries] The key-value pairs to cache.
8022 */
8023 function ListCache(entries) {
8024 var index = -1,
8025 length = entries == null ? 0 : entries.length;
8026
8027 this.clear();
8028 while (++index < length) {
8029 var entry = entries[index];
8030 this.set(entry[0], entry[1]);
8031 }
8032 }
8033
8034 // Add methods to `ListCache`.
8035 ListCache.prototype.clear = _listCacheClear;
8036 ListCache.prototype['delete'] = _listCacheDelete;
8037 ListCache.prototype.get = _listCacheGet;
8038 ListCache.prototype.has = _listCacheHas;
8039 ListCache.prototype.set = _listCacheSet;
8040
8041 var _ListCache = ListCache;
8042
8043 /* Built-in method references that are verified to be native. */
8044 var Map$1 = _getNative(_root, 'Map');
8045
8046 var _Map = Map$1;
8047
8048 /**
8049 * Removes all key-value entries from the map.
8050 *
8051 * @private
8052 * @name clear
8053 * @memberOf MapCache
8054 */
8055 function mapCacheClear() {
8056 this.size = 0;
8057 this.__data__ = {
8058 'hash': new _Hash,
8059 'map': new (_Map || _ListCache),
8060 'string': new _Hash
8061 };
8062 }
8063
8064 var _mapCacheClear = mapCacheClear;
8065
8066 /**
8067 * Checks if `value` is suitable for use as unique object key.
8068 *
8069 * @private
8070 * @param {*} value The value to check.
8071 * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
8072 */
8073 function isKeyable(value) {
8074 var type = typeof value;
8075 return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
8076 ? (value !== '__proto__')
8077 : (value === null);
8078 }
8079
8080 var _isKeyable = isKeyable;
8081
8082 /**
8083 * Gets the data for `map`.
8084 *
8085 * @private
8086 * @param {Object} map The map to query.
8087 * @param {string} key The reference key.
8088 * @returns {*} Returns the map data.
8089 */
8090 function getMapData(map, key) {
8091 var data = map.__data__;
8092 return _isKeyable(key)
8093 ? data[typeof key == 'string' ? 'string' : 'hash']
8094 : data.map;
8095 }
8096
8097 var _getMapData = getMapData;
8098
8099 /**
8100 * Removes `key` and its value from the map.
8101 *
8102 * @private
8103 * @name delete
8104 * @memberOf MapCache
8105 * @param {string} key The key of the value to remove.
8106 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
8107 */
8108 function mapCacheDelete(key) {
8109 var result = _getMapData(this, key)['delete'](key);
8110 this.size -= result ? 1 : 0;
8111 return result;
8112 }
8113
8114 var _mapCacheDelete = mapCacheDelete;
8115
8116 /**
8117 * Gets the map value for `key`.
8118 *
8119 * @private
8120 * @name get
8121 * @memberOf MapCache
8122 * @param {string} key The key of the value to get.
8123 * @returns {*} Returns the entry value.
8124 */
8125 function mapCacheGet(key) {
8126 return _getMapData(this, key).get(key);
8127 }
8128
8129 var _mapCacheGet = mapCacheGet;
8130
8131 /**
8132 * Checks if a map value for `key` exists.
8133 *
8134 * @private
8135 * @name has
8136 * @memberOf MapCache
8137 * @param {string} key The key of the entry to check.
8138 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
8139 */
8140 function mapCacheHas(key) {
8141 return _getMapData(this, key).has(key);
8142 }
8143
8144 var _mapCacheHas = mapCacheHas;
8145
8146 /**
8147 * Sets the map `key` to `value`.
8148 *
8149 * @private
8150 * @name set
8151 * @memberOf MapCache
8152 * @param {string} key The key of the value to set.
8153 * @param {*} value The value to set.
8154 * @returns {Object} Returns the map cache instance.
8155 */
8156 function mapCacheSet(key, value) {
8157 var data = _getMapData(this, key),
8158 size = data.size;
8159
8160 data.set(key, value);
8161 this.size += data.size == size ? 0 : 1;
8162 return this;
8163 }
8164
8165 var _mapCacheSet = mapCacheSet;
8166
8167 /**
8168 * Creates a map cache object to store key-value pairs.
8169 *
8170 * @private
8171 * @constructor
8172 * @param {Array} [entries] The key-value pairs to cache.
8173 */
8174 function MapCache(entries) {
8175 var index = -1,
8176 length = entries == null ? 0 : entries.length;
8177
8178 this.clear();
8179 while (++index < length) {
8180 var entry = entries[index];
8181 this.set(entry[0], entry[1]);
8182 }
8183 }
8184
8185 // Add methods to `MapCache`.
8186 MapCache.prototype.clear = _mapCacheClear;
8187 MapCache.prototype['delete'] = _mapCacheDelete;
8188 MapCache.prototype.get = _mapCacheGet;
8189 MapCache.prototype.has = _mapCacheHas;
8190 MapCache.prototype.set = _mapCacheSet;
8191
8192 var _MapCache = MapCache;
8193
8194 /** Error message constants. */
8195 var FUNC_ERROR_TEXT = 'Expected a function';
8196
8197 /**
8198 * Creates a function that memoizes the result of `func`. If `resolver` is
8199 * provided, it determines the cache key for storing the result based on the
8200 * arguments provided to the memoized function. By default, the first argument
8201 * provided to the memoized function is used as the map cache key. The `func`
8202 * is invoked with the `this` binding of the memoized function.
8203 *
8204 * **Note:** The cache is exposed as the `cache` property on the memoized
8205 * function. Its creation may be customized by replacing the `_.memoize.Cache`
8206 * constructor with one whose instances implement the
8207 * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
8208 * method interface of `clear`, `delete`, `get`, `has`, and `set`.
8209 *
8210 * @static
8211 * @memberOf _
8212 * @since 0.1.0
8213 * @category Function
8214 * @param {Function} func The function to have its output memoized.
8215 * @param {Function} [resolver] The function to resolve the cache key.
8216 * @returns {Function} Returns the new memoized function.
8217 * @example
8218 *
8219 * var object = { 'a': 1, 'b': 2 };
8220 * var other = { 'c': 3, 'd': 4 };
8221 *
8222 * var values = _.memoize(_.values);
8223 * values(object);
8224 * // => [1, 2]
8225 *
8226 * values(other);
8227 * // => [3, 4]
8228 *
8229 * object.a = 2;
8230 * values(object);
8231 * // => [1, 2]
8232 *
8233 * // Modify the result cache.
8234 * values.cache.set(object, ['a', 'b']);
8235 * values(object);
8236 * // => ['a', 'b']
8237 *
8238 * // Replace `_.memoize.Cache`.
8239 * _.memoize.Cache = WeakMap;
8240 */
8241 function memoize(func, resolver) {
8242 if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
8243 throw new TypeError(FUNC_ERROR_TEXT);
8244 }
8245 var memoized = function() {
8246 var args = arguments,
8247 key = resolver ? resolver.apply(this, args) : args[0],
8248 cache = memoized.cache;
8249
8250 if (cache.has(key)) {
8251 return cache.get(key);
8252 }
8253 var result = func.apply(this, args);
8254 memoized.cache = cache.set(key, result) || cache;
8255 return result;
8256 };
8257 memoized.cache = new (memoize.Cache || _MapCache);
8258 return memoized;
8259 }
8260
8261 // Expose `MapCache`.
8262 memoize.Cache = _MapCache;
8263
8264 var memoize_1 = memoize;
8265
8266 /** Used as the maximum memoize cache size. */
8267 var MAX_MEMOIZE_SIZE = 500;
8268
8269 /**
8270 * A specialized version of `_.memoize` which clears the memoized function's
8271 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
8272 *
8273 * @private
8274 * @param {Function} func The function to have its output memoized.
8275 * @returns {Function} Returns the new memoized function.
8276 */
8277 function memoizeCapped(func) {
8278 var result = memoize_1(func, function(key) {
8279 if (cache.size === MAX_MEMOIZE_SIZE) {
8280 cache.clear();
8281 }
8282 return key;
8283 });
8284
8285 var cache = result.cache;
8286 return result;
8287 }
8288
8289 var _memoizeCapped = memoizeCapped;
8290
8291 /** Used to match property names within property paths. */
8292 var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
8293
8294 /** Used to match backslashes in property paths. */
8295 var reEscapeChar = /\\(\\)?/g;
8296
8297 /**
8298 * Converts `string` to a property path array.
8299 *
8300 * @private
8301 * @param {string} string The string to convert.
8302 * @returns {Array} Returns the property path array.
8303 */
8304 var stringToPath = _memoizeCapped(function(string) {
8305 var result = [];
8306 if (string.charCodeAt(0) === 46 /* . */) {
8307 result.push('');
8308 }
8309 string.replace(rePropName, function(match, number, quote, subString) {
8310 result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
8311 });
8312 return result;
8313 });
8314
8315 var _stringToPath = stringToPath;
8316
8317 /**
8318 * A specialized version of `_.map` for arrays without support for iteratee
8319 * shorthands.
8320 *
8321 * @private
8322 * @param {Array} [array] The array to iterate over.
8323 * @param {Function} iteratee The function invoked per iteration.
8324 * @returns {Array} Returns the new mapped array.
8325 */
8326 function arrayMap(array, iteratee) {
8327 var index = -1,
8328 length = array == null ? 0 : array.length,
8329 result = Array(length);
8330
8331 while (++index < length) {
8332 result[index] = iteratee(array[index], index, array);
8333 }
8334 return result;
8335 }
8336
8337 var _arrayMap = arrayMap;
8338
8339 /** Used as references for various `Number` constants. */
8340 var INFINITY$1 = 1 / 0;
8341
8342 /** Used to convert symbols to primitives and strings. */
8343 var symbolProto = _Symbol ? _Symbol.prototype : undefined,
8344 symbolToString = symbolProto ? symbolProto.toString : undefined;
8345
8346 /**
8347 * The base implementation of `_.toString` which doesn't convert nullish
8348 * values to empty strings.
8349 *
8350 * @private
8351 * @param {*} value The value to process.
8352 * @returns {string} Returns the string.
8353 */
8354 function baseToString(value) {
8355 // Exit early for strings to avoid a performance hit in some environments.
8356 if (typeof value == 'string') {
8357 return value;
8358 }
8359 if (isArray_1(value)) {
8360 // Recursively convert values (susceptible to call stack limits).
8361 return _arrayMap(value, baseToString) + '';
8362 }
8363 if (isSymbol_1(value)) {
8364 return symbolToString ? symbolToString.call(value) : '';
8365 }
8366 var result = (value + '');
8367 return (result == '0' && (1 / value) == -INFINITY$1) ? '-0' : result;
8368 }
8369
8370 var _baseToString = baseToString;
8371
8372 /**
8373 * Converts `value` to a string. An empty string is returned for `null`
8374 * and `undefined` values. The sign of `-0` is preserved.
8375 *
8376 * @static
8377 * @memberOf _
8378 * @since 4.0.0
8379 * @category Lang
8380 * @param {*} value The value to convert.
8381 * @returns {string} Returns the converted string.
8382 * @example
8383 *
8384 * _.toString(null);
8385 * // => ''
8386 *
8387 * _.toString(-0);
8388 * // => '-0'
8389 *
8390 * _.toString([1, 2, 3]);
8391 * // => '1,2,3'
8392 */
8393 function toString$1(value) {
8394 return value == null ? '' : _baseToString(value);
8395 }
8396
8397 var toString_1 = toString$1;
8398
8399 /**
8400 * Casts `value` to a path array if it's not one.
8401 *
8402 * @private
8403 * @param {*} value The value to inspect.
8404 * @param {Object} [object] The object to query keys on.
8405 * @returns {Array} Returns the cast property path array.
8406 */
8407 function castPath(value, object) {
8408 if (isArray_1(value)) {
8409 return value;
8410 }
8411 return _isKey(value, object) ? [value] : _stringToPath(toString_1(value));
8412 }
8413
8414 var _castPath = castPath;
8415
8416 /** Used as references for various `Number` constants. */
8417 var INFINITY = 1 / 0;
8418
8419 /**
8420 * Converts `value` to a string key if it's not a string or symbol.
8421 *
8422 * @private
8423 * @param {*} value The value to inspect.
8424 * @returns {string|symbol} Returns the key.
8425 */
8426 function toKey(value) {
8427 if (typeof value == 'string' || isSymbol_1(value)) {
8428 return value;
8429 }
8430 var result = (value + '');
8431 return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
8432 }
8433
8434 var _toKey = toKey;
8435
8436 /**
8437 * The base implementation of `_.get` without support for default values.
8438 *
8439 * @private
8440 * @param {Object} object The object to query.
8441 * @param {Array|string} path The path of the property to get.
8442 * @returns {*} Returns the resolved value.
8443 */
8444 function baseGet(object, path) {
8445 path = _castPath(path, object);
8446
8447 var index = 0,
8448 length = path.length;
8449
8450 while (object != null && index < length) {
8451 object = object[_toKey(path[index++])];
8452 }
8453 return (index && index == length) ? object : undefined;
8454 }
8455
8456 var _baseGet = baseGet;
8457
8458 /**
8459 * Gets the value at `path` of `object`. If the resolved value is
8460 * `undefined`, the `defaultValue` is returned in its place.
8461 *
8462 * @static
8463 * @memberOf _
8464 * @since 3.7.0
8465 * @category Object
8466 * @param {Object} object The object to query.
8467 * @param {Array|string} path The path of the property to get.
8468 * @param {*} [defaultValue] The value returned for `undefined` resolved values.
8469 * @returns {*} Returns the resolved value.
8470 * @example
8471 *
8472 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
8473 *
8474 * _.get(object, 'a[0].b.c');
8475 * // => 3
8476 *
8477 * _.get(object, ['a', '0', 'b', 'c']);
8478 * // => 3
8479 *
8480 * _.get(object, 'a.b.c', 'default');
8481 * // => 'default'
8482 */
8483 function get(object, path, defaultValue) {
8484 var result = object == null ? undefined : _baseGet(object, path);
8485 return result === undefined ? defaultValue : result;
8486 }
8487
8488 var get_1 = get;
8489
8490 var defineProperty = (function() {
8491 try {
8492 var func = _getNative(Object, 'defineProperty');
8493 func({}, '', {});
8494 return func;
8495 } catch (e) {}
8496 }());
8497
8498 var _defineProperty = defineProperty;
8499
8500 /**
8501 * The base implementation of `assignValue` and `assignMergeValue` without
8502 * value checks.
8503 *
8504 * @private
8505 * @param {Object} object The object to modify.
8506 * @param {string} key The key of the property to assign.
8507 * @param {*} value The value to assign.
8508 */
8509 function baseAssignValue(object, key, value) {
8510 if (key == '__proto__' && _defineProperty) {
8511 _defineProperty(object, key, {
8512 'configurable': true,
8513 'enumerable': true,
8514 'value': value,
8515 'writable': true
8516 });
8517 } else {
8518 object[key] = value;
8519 }
8520 }
8521
8522 var _baseAssignValue = baseAssignValue;
8523
8524 /** Used for built-in method references. */
8525 var objectProto = Object.prototype;
8526
8527 /** Used to check objects for own properties. */
8528 var hasOwnProperty = objectProto.hasOwnProperty;
8529
8530 /**
8531 * Assigns `value` to `key` of `object` if the existing value is not equivalent
8532 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
8533 * for equality comparisons.
8534 *
8535 * @private
8536 * @param {Object} object The object to modify.
8537 * @param {string} key The key of the property to assign.
8538 * @param {*} value The value to assign.
8539 */
8540 function assignValue(object, key, value) {
8541 var objValue = object[key];
8542 if (!(hasOwnProperty.call(object, key) && eq_1(objValue, value)) ||
8543 (value === undefined && !(key in object))) {
8544 _baseAssignValue(object, key, value);
8545 }
8546 }
8547
8548 var _assignValue = assignValue;
8549
8550 /** Used as references for various `Number` constants. */
8551 var MAX_SAFE_INTEGER = 9007199254740991;
8552
8553 /** Used to detect unsigned integer values. */
8554 var reIsUint = /^(?:0|[1-9]\d*)$/;
8555
8556 /**
8557 * Checks if `value` is a valid array-like index.
8558 *
8559 * @private
8560 * @param {*} value The value to check.
8561 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
8562 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
8563 */
8564 function isIndex(value, length) {
8565 var type = typeof value;
8566 length = length == null ? MAX_SAFE_INTEGER : length;
8567
8568 return !!length &&
8569 (type == 'number' ||
8570 (type != 'symbol' && reIsUint.test(value))) &&
8571 (value > -1 && value % 1 == 0 && value < length);
8572 }
8573
8574 var _isIndex = isIndex;
8575
8576 /**
8577 * The base implementation of `_.set`.
8578 *
8579 * @private
8580 * @param {Object} object The object to modify.
8581 * @param {Array|string} path The path of the property to set.
8582 * @param {*} value The value to set.
8583 * @param {Function} [customizer] The function to customize path creation.
8584 * @returns {Object} Returns `object`.
8585 */
8586 function baseSet(object, path, value, customizer) {
8587 if (!isObject_1(object)) {
8588 return object;
8589 }
8590 path = _castPath(path, object);
8591
8592 var index = -1,
8593 length = path.length,
8594 lastIndex = length - 1,
8595 nested = object;
8596
8597 while (nested != null && ++index < length) {
8598 var key = _toKey(path[index]),
8599 newValue = value;
8600
8601 if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
8602 return object;
8603 }
8604
8605 if (index != lastIndex) {
8606 var objValue = nested[key];
8607 newValue = customizer ? customizer(objValue, key, nested) : undefined;
8608 if (newValue === undefined) {
8609 newValue = isObject_1(objValue)
8610 ? objValue
8611 : (_isIndex(path[index + 1]) ? [] : {});
8612 }
8613 }
8614 _assignValue(nested, key, newValue);
8615 nested = nested[key];
8616 }
8617 return object;
8618 }
8619
8620 var _baseSet = baseSet;
8621
8622 /**
8623 * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
8624 * it's created. Arrays are created for missing index properties while objects
8625 * are created for all other missing properties. Use `_.setWith` to customize
8626 * `path` creation.
8627 *
8628 * **Note:** This method mutates `object`.
8629 *
8630 * @static
8631 * @memberOf _
8632 * @since 3.7.0
8633 * @category Object
8634 * @param {Object} object The object to modify.
8635 * @param {Array|string} path The path of the property to set.
8636 * @param {*} value The value to set.
8637 * @returns {Object} Returns `object`.
8638 * @example
8639 *
8640 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
8641 *
8642 * _.set(object, 'a[0].b.c', 4);
8643 * console.log(object.a[0].b.c);
8644 * // => 4
8645 *
8646 * _.set(object, ['x', '0', 'y', 'z'], 5);
8647 * console.log(object.x[0].y.z);
8648 * // => 5
8649 */
8650 function set(object, path, value) {
8651 return object == null ? object : _baseSet(object, path, value);
8652 }
8653
8654 var set_1 = set;
8655
8656 /**
8657 * Copies the values of `source` to `array`.
8658 *
8659 * @private
8660 * @param {Array} source The array to copy values from.
8661 * @param {Array} [array=[]] The array to copy values to.
8662 * @returns {Array} Returns `array`.
8663 */
8664 function copyArray(source, array) {
8665 var index = -1,
8666 length = source.length;
8667
8668 array || (array = Array(length));
8669 while (++index < length) {
8670 array[index] = source[index];
8671 }
8672 return array;
8673 }
8674
8675 var _copyArray = copyArray;
8676
8677 /**
8678 * Converts `value` to a property path array.
8679 *
8680 * @static
8681 * @memberOf _
8682 * @since 4.0.0
8683 * @category Util
8684 * @param {*} value The value to convert.
8685 * @returns {Array} Returns the new property path array.
8686 * @example
8687 *
8688 * _.toPath('a.b.c');
8689 * // => ['a', 'b', 'c']
8690 *
8691 * _.toPath('a[0].b.c');
8692 * // => ['a', '0', 'b', 'c']
8693 */
8694 function toPath(value) {
8695 if (isArray_1(value)) {
8696 return _arrayMap(value, _toKey);
8697 }
8698 return isSymbol_1(value) ? [value] : _copyArray(_stringToPath(toString_1(value)));
8699 }
8700
8701 var toPath_1 = toPath;
8702
8703 var define$2 = {
8704 // access data field
8705 data: function data(params) {
8706 var defaults = {
8707 field: 'data',
8708 bindingEvent: 'data',
8709 allowBinding: false,
8710 allowSetting: false,
8711 allowGetting: false,
8712 settingEvent: 'data',
8713 settingTriggersEvent: false,
8714 triggerFnName: 'trigger',
8715 immutableKeys: {},
8716 // key => true if immutable
8717 updateStyle: false,
8718 beforeGet: function beforeGet(self) {},
8719 beforeSet: function beforeSet(self, obj) {},
8720 onSet: function onSet(self) {},
8721 canSet: function canSet(self) {
8722 return true;
8723 }
8724 };
8725 params = extend({}, defaults, params);
8726 return function dataImpl(name, value) {
8727 var p = params;
8728 var self = this;
8729 var selfIsArrayLike = self.length !== undefined;
8730 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
8731
8732 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
8733
8734 if (string(name)) {
8735 // set or get property
8736 var isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
8737
8738 var path = isPathLike && toPath_1(name); // .data('foo')
8739
8740 if (p.allowGetting && value === undefined) {
8741 // get
8742 var ret;
8743
8744 if (single) {
8745 p.beforeGet(single); // check if it's path and a field with the same name doesn't exist
8746
8747 if (path && single._private[p.field][name] === undefined) {
8748 ret = get_1(single._private[p.field], path);
8749 } else {
8750 ret = single._private[p.field][name];
8751 }
8752 }
8753
8754 return ret; // .data('foo', 'bar')
8755 } else if (p.allowSetting && value !== undefined) {
8756 // set
8757 var valid = !p.immutableKeys[name];
8758
8759 if (valid) {
8760 var change = _defineProperty$1({}, name, value);
8761
8762 p.beforeSet(self, change);
8763
8764 for (var i = 0, l = all.length; i < l; i++) {
8765 var ele = all[i];
8766
8767 if (p.canSet(ele)) {
8768 if (path && single._private[p.field][name] === undefined) {
8769 set_1(ele._private[p.field], path, value);
8770 } else {
8771 ele._private[p.field][name] = value;
8772 }
8773 }
8774 } // update mappers if asked
8775
8776
8777 if (p.updateStyle) {
8778 self.updateStyle();
8779 } // call onSet callback
8780
8781
8782 p.onSet(self);
8783
8784 if (p.settingTriggersEvent) {
8785 self[p.triggerFnName](p.settingEvent);
8786 }
8787 }
8788 } // .data({ 'foo': 'bar' })
8789
8790 } else if (p.allowSetting && plainObject(name)) {
8791 // extend
8792 var obj = name;
8793 var k, v;
8794 var keys = Object.keys(obj);
8795 p.beforeSet(self, obj);
8796
8797 for (var _i = 0; _i < keys.length; _i++) {
8798 k = keys[_i];
8799 v = obj[k];
8800
8801 var _valid = !p.immutableKeys[k];
8802
8803 if (_valid) {
8804 for (var j = 0; j < all.length; j++) {
8805 var _ele = all[j];
8806
8807 if (p.canSet(_ele)) {
8808 _ele._private[p.field][k] = v;
8809 }
8810 }
8811 }
8812 } // update mappers if asked
8813
8814
8815 if (p.updateStyle) {
8816 self.updateStyle();
8817 } // call onSet callback
8818
8819
8820 p.onSet(self);
8821
8822 if (p.settingTriggersEvent) {
8823 self[p.triggerFnName](p.settingEvent);
8824 } // .data(function(){ ... })
8825
8826 } else if (p.allowBinding && fn$6(name)) {
8827 // bind to event
8828 var fn = name;
8829 self.on(p.bindingEvent, fn); // .data()
8830 } else if (p.allowGetting && name === undefined) {
8831 // get whole object
8832 var _ret;
8833
8834 if (single) {
8835 p.beforeGet(single);
8836 _ret = single._private[p.field];
8837 }
8838
8839 return _ret;
8840 }
8841
8842 return self; // maintain chainability
8843 }; // function
8844 },
8845 // data
8846 // remove data field
8847 removeData: function removeData(params) {
8848 var defaults = {
8849 field: 'data',
8850 event: 'data',
8851 triggerFnName: 'trigger',
8852 triggerEvent: false,
8853 immutableKeys: {} // key => true if immutable
8854
8855 };
8856 params = extend({}, defaults, params);
8857 return function removeDataImpl(names) {
8858 var p = params;
8859 var self = this;
8860 var selfIsArrayLike = self.length !== undefined;
8861 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
8862 // .removeData('foo bar')
8863
8864 if (string(names)) {
8865 // then get the list of keys, and delete them
8866 var keys = names.split(/\s+/);
8867 var l = keys.length;
8868
8869 for (var i = 0; i < l; i++) {
8870 // delete each non-empty key
8871 var key = keys[i];
8872
8873 if (emptyString(key)) {
8874 continue;
8875 }
8876
8877 var valid = !p.immutableKeys[key]; // not valid if immutable
8878
8879 if (valid) {
8880 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
8881 all[i_a]._private[p.field][key] = undefined;
8882 }
8883 }
8884 }
8885
8886 if (p.triggerEvent) {
8887 self[p.triggerFnName](p.event);
8888 } // .removeData()
8889
8890 } else if (names === undefined) {
8891 // then delete all keys
8892 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
8893 var _privateFields = all[_i_a]._private[p.field];
8894
8895 var _keys = Object.keys(_privateFields);
8896
8897 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
8898 var _key = _keys[_i2];
8899 var validKeyToDelete = !p.immutableKeys[_key];
8900
8901 if (validKeyToDelete) {
8902 _privateFields[_key] = undefined;
8903 }
8904 }
8905 }
8906
8907 if (p.triggerEvent) {
8908 self[p.triggerFnName](p.event);
8909 }
8910 }
8911
8912 return self; // maintain chaining
8913 }; // function
8914 } // removeData
8915
8916 }; // define
8917
8918 var define$1 = {
8919 eventAliasesOn: function eventAliasesOn(proto) {
8920 var p = proto;
8921 p.addListener = p.listen = p.bind = p.on;
8922 p.unlisten = p.unbind = p.off = p.removeListener;
8923 p.trigger = p.emit; // this is just a wrapper alias of .on()
8924
8925 p.pon = p.promiseOn = function (events, selector) {
8926 var self = this;
8927 var args = Array.prototype.slice.call(arguments, 0);
8928 return new Promise$1(function (resolve, reject) {
8929 var callback = function callback(e) {
8930 self.off.apply(self, offArgs);
8931 resolve(e);
8932 };
8933
8934 var onArgs = args.concat([callback]);
8935 var offArgs = onArgs.concat([]);
8936 self.on.apply(self, onArgs);
8937 });
8938 };
8939 }
8940 }; // define
8941
8942 // use this module to cherry pick functions into your prototype
8943 var define = {};
8944 [define$3, define$2, define$1].forEach(function (m) {
8945 extend(define, m);
8946 });
8947
8948 var elesfn$i = {
8949 animate: define.animate(),
8950 animation: define.animation(),
8951 animated: define.animated(),
8952 clearQueue: define.clearQueue(),
8953 delay: define.delay(),
8954 delayAnimation: define.delayAnimation(),
8955 stop: define.stop()
8956 };
8957
8958 var elesfn$h = {
8959 classes: function classes(_classes) {
8960 var self = this;
8961
8962 if (_classes === undefined) {
8963 var ret = [];
8964
8965 self[0]._private.classes.forEach(function (cls) {
8966 return ret.push(cls);
8967 });
8968
8969 return ret;
8970 } else if (!array(_classes)) {
8971 // extract classes from string
8972 _classes = (_classes || '').match(/\S+/g) || [];
8973 }
8974
8975 var changed = [];
8976 var classesSet = new Set$1(_classes); // check and update each ele
8977
8978 for (var j = 0; j < self.length; j++) {
8979 var ele = self[j];
8980 var _p = ele._private;
8981 var eleClasses = _p.classes;
8982 var changedEle = false; // check if ele has all of the passed classes
8983
8984 for (var i = 0; i < _classes.length; i++) {
8985 var cls = _classes[i];
8986 var eleHasClass = eleClasses.has(cls);
8987
8988 if (!eleHasClass) {
8989 changedEle = true;
8990 break;
8991 }
8992 } // check if ele has classes outside of those passed
8993
8994
8995 if (!changedEle) {
8996 changedEle = eleClasses.size !== _classes.length;
8997 }
8998
8999 if (changedEle) {
9000 _p.classes = classesSet;
9001 changed.push(ele);
9002 }
9003 } // trigger update style on those eles that had class changes
9004
9005
9006 if (changed.length > 0) {
9007 this.spawn(changed).updateStyle().emit('class');
9008 }
9009
9010 return self;
9011 },
9012 addClass: function addClass(classes) {
9013 return this.toggleClass(classes, true);
9014 },
9015 hasClass: function hasClass(className) {
9016 var ele = this[0];
9017 return ele != null && ele._private.classes.has(className);
9018 },
9019 toggleClass: function toggleClass(classes, toggle) {
9020 if (!array(classes)) {
9021 // extract classes from string
9022 classes = classes.match(/\S+/g) || [];
9023 }
9024
9025 var self = this;
9026 var toggleUndefd = toggle === undefined;
9027 var changed = []; // eles who had classes changed
9028
9029 for (var i = 0, il = self.length; i < il; i++) {
9030 var ele = self[i];
9031 var eleClasses = ele._private.classes;
9032 var changedEle = false;
9033
9034 for (var j = 0; j < classes.length; j++) {
9035 var cls = classes[j];
9036 var hasClass = eleClasses.has(cls);
9037 var changedNow = false;
9038
9039 if (toggle || toggleUndefd && !hasClass) {
9040 eleClasses.add(cls);
9041 changedNow = true;
9042 } else if (!toggle || toggleUndefd && hasClass) {
9043 eleClasses["delete"](cls);
9044 changedNow = true;
9045 }
9046
9047 if (!changedEle && changedNow) {
9048 changed.push(ele);
9049 changedEle = true;
9050 }
9051 } // for j classes
9052
9053 } // for i eles
9054 // trigger update style on those eles that had class changes
9055
9056
9057 if (changed.length > 0) {
9058 this.spawn(changed).updateStyle().emit('class');
9059 }
9060
9061 return self;
9062 },
9063 removeClass: function removeClass(classes) {
9064 return this.toggleClass(classes, false);
9065 },
9066 flashClass: function flashClass(classes, duration) {
9067 var self = this;
9068
9069 if (duration == null) {
9070 duration = 250;
9071 } else if (duration === 0) {
9072 return self; // nothing to do really
9073 }
9074
9075 self.addClass(classes);
9076 setTimeout(function () {
9077 self.removeClass(classes);
9078 }, duration);
9079 return self;
9080 }
9081 };
9082 elesfn$h.className = elesfn$h.classNames = elesfn$h.classes;
9083
9084 var tokens = {
9085 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
9086 // chars we need to escape in let names, etc
9087 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
9088 // binary comparison op (used in data selectors)
9089 boolOp: '\\?|\\!|\\^',
9090 // boolean (unary) operators (used in data selectors)
9091 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
9092 // string literals (used in data selectors) -- doublequotes | singlequotes
9093 number: number,
9094 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
9095 meta: 'degree|indegree|outdegree',
9096 // allowed metadata fields (i.e. allowed functions to use from Collection)
9097 separator: '\\s*,\\s*',
9098 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
9099 descendant: '\\s+',
9100 child: '\\s+>\\s+',
9101 subject: '\\$',
9102 group: 'node|edge|\\*',
9103 directedEdge: '\\s+->\\s+',
9104 undirectedEdge: '\\s+<->\\s+'
9105 };
9106 tokens.variable = '(?:[\\w-.]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name can have letters, numbers, dashes, and periods
9107
9108 tokens.className = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a class name has the same rules as a variable except it can't have a '.' in the name
9109
9110 tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
9111
9112 tokens.id = tokens.variable; // an element id (follows variable conventions)
9113
9114 (function () {
9115 var ops, op, i; // add @ variants to comparatorOp
9116
9117 ops = tokens.comparatorOp.split('|');
9118
9119 for (i = 0; i < ops.length; i++) {
9120 op = ops[i];
9121 tokens.comparatorOp += '|@' + op;
9122 } // add ! variants to comparatorOp
9123
9124
9125 ops = tokens.comparatorOp.split('|');
9126
9127 for (i = 0; i < ops.length; i++) {
9128 op = ops[i];
9129
9130 if (op.indexOf('!') >= 0) {
9131 continue;
9132 } // skip ops that explicitly contain !
9133
9134
9135 if (op === '=') {
9136 continue;
9137 } // skip = b/c != is explicitly defined
9138
9139
9140 tokens.comparatorOp += '|\\!' + op;
9141 }
9142 })();
9143
9144 /**
9145 * Make a new query object
9146 *
9147 * @prop type {Type} The type enum (int) of the query
9148 * @prop checks List of checks to make against an ele to test for a match
9149 */
9150 var newQuery = function newQuery() {
9151 return {
9152 checks: []
9153 };
9154 };
9155
9156 /**
9157 * A check type enum-like object. Uses integer values for fast match() lookup.
9158 * The ordering does not matter as long as the ints are unique.
9159 */
9160 var Type = {
9161 /** E.g. node */
9162 GROUP: 0,
9163
9164 /** A collection of elements */
9165 COLLECTION: 1,
9166
9167 /** A filter(ele) function */
9168 FILTER: 2,
9169
9170 /** E.g. [foo > 1] */
9171 DATA_COMPARE: 3,
9172
9173 /** E.g. [foo] */
9174 DATA_EXIST: 4,
9175
9176 /** E.g. [?foo] */
9177 DATA_BOOL: 5,
9178
9179 /** E.g. [[degree > 2]] */
9180 META_COMPARE: 6,
9181
9182 /** E.g. :selected */
9183 STATE: 7,
9184
9185 /** E.g. #foo */
9186 ID: 8,
9187
9188 /** E.g. .foo */
9189 CLASS: 9,
9190
9191 /** E.g. #foo <-> #bar */
9192 UNDIRECTED_EDGE: 10,
9193
9194 /** E.g. #foo -> #bar */
9195 DIRECTED_EDGE: 11,
9196
9197 /** E.g. $#foo -> #bar */
9198 NODE_SOURCE: 12,
9199
9200 /** E.g. #foo -> $#bar */
9201 NODE_TARGET: 13,
9202
9203 /** E.g. $#foo <-> #bar */
9204 NODE_NEIGHBOR: 14,
9205
9206 /** E.g. #foo > #bar */
9207 CHILD: 15,
9208
9209 /** E.g. #foo #bar */
9210 DESCENDANT: 16,
9211
9212 /** E.g. $#foo > #bar */
9213 PARENT: 17,
9214
9215 /** E.g. $#foo #bar */
9216 ANCESTOR: 18,
9217
9218 /** E.g. #foo > $bar > #baz */
9219 COMPOUND_SPLIT: 19,
9220
9221 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
9222 TRUE: 20
9223 };
9224
9225 var stateSelectors = [{
9226 selector: ':selected',
9227 matches: function matches(ele) {
9228 return ele.selected();
9229 }
9230 }, {
9231 selector: ':unselected',
9232 matches: function matches(ele) {
9233 return !ele.selected();
9234 }
9235 }, {
9236 selector: ':selectable',
9237 matches: function matches(ele) {
9238 return ele.selectable();
9239 }
9240 }, {
9241 selector: ':unselectable',
9242 matches: function matches(ele) {
9243 return !ele.selectable();
9244 }
9245 }, {
9246 selector: ':locked',
9247 matches: function matches(ele) {
9248 return ele.locked();
9249 }
9250 }, {
9251 selector: ':unlocked',
9252 matches: function matches(ele) {
9253 return !ele.locked();
9254 }
9255 }, {
9256 selector: ':visible',
9257 matches: function matches(ele) {
9258 return ele.visible();
9259 }
9260 }, {
9261 selector: ':hidden',
9262 matches: function matches(ele) {
9263 return !ele.visible();
9264 }
9265 }, {
9266 selector: ':transparent',
9267 matches: function matches(ele) {
9268 return ele.transparent();
9269 }
9270 }, {
9271 selector: ':grabbed',
9272 matches: function matches(ele) {
9273 return ele.grabbed();
9274 }
9275 }, {
9276 selector: ':free',
9277 matches: function matches(ele) {
9278 return !ele.grabbed();
9279 }
9280 }, {
9281 selector: ':removed',
9282 matches: function matches(ele) {
9283 return ele.removed();
9284 }
9285 }, {
9286 selector: ':inside',
9287 matches: function matches(ele) {
9288 return !ele.removed();
9289 }
9290 }, {
9291 selector: ':grabbable',
9292 matches: function matches(ele) {
9293 return ele.grabbable();
9294 }
9295 }, {
9296 selector: ':ungrabbable',
9297 matches: function matches(ele) {
9298 return !ele.grabbable();
9299 }
9300 }, {
9301 selector: ':animated',
9302 matches: function matches(ele) {
9303 return ele.animated();
9304 }
9305 }, {
9306 selector: ':unanimated',
9307 matches: function matches(ele) {
9308 return !ele.animated();
9309 }
9310 }, {
9311 selector: ':parent',
9312 matches: function matches(ele) {
9313 return ele.isParent();
9314 }
9315 }, {
9316 selector: ':childless',
9317 matches: function matches(ele) {
9318 return ele.isChildless();
9319 }
9320 }, {
9321 selector: ':child',
9322 matches: function matches(ele) {
9323 return ele.isChild();
9324 }
9325 }, {
9326 selector: ':orphan',
9327 matches: function matches(ele) {
9328 return ele.isOrphan();
9329 }
9330 }, {
9331 selector: ':nonorphan',
9332 matches: function matches(ele) {
9333 return ele.isChild();
9334 }
9335 }, {
9336 selector: ':compound',
9337 matches: function matches(ele) {
9338 if (ele.isNode()) {
9339 return ele.isParent();
9340 } else {
9341 return ele.source().isParent() || ele.target().isParent();
9342 }
9343 }
9344 }, {
9345 selector: ':loop',
9346 matches: function matches(ele) {
9347 return ele.isLoop();
9348 }
9349 }, {
9350 selector: ':simple',
9351 matches: function matches(ele) {
9352 return ele.isSimple();
9353 }
9354 }, {
9355 selector: ':active',
9356 matches: function matches(ele) {
9357 return ele.active();
9358 }
9359 }, {
9360 selector: ':inactive',
9361 matches: function matches(ele) {
9362 return !ele.active();
9363 }
9364 }, {
9365 selector: ':backgrounding',
9366 matches: function matches(ele) {
9367 return ele.backgrounding();
9368 }
9369 }, {
9370 selector: ':nonbackgrounding',
9371 matches: function matches(ele) {
9372 return !ele.backgrounding();
9373 }
9374 }].sort(function (a, b) {
9375 // n.b. selectors that are starting substrings of others must have the longer ones first
9376 return descending(a.selector, b.selector);
9377 });
9378
9379 var lookup = function () {
9380 var selToFn = {};
9381 var s;
9382
9383 for (var i = 0; i < stateSelectors.length; i++) {
9384 s = stateSelectors[i];
9385 selToFn[s.selector] = s.matches;
9386 }
9387
9388 return selToFn;
9389 }();
9390
9391 var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
9392 return lookup[sel](ele);
9393 };
9394 var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
9395 return s.selector;
9396 }).join('|') + ')';
9397
9398 // so that values get compared properly in Selector.filter()
9399
9400 var cleanMetaChars = function cleanMetaChars(str) {
9401 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
9402 return $1;
9403 });
9404 };
9405
9406 var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
9407 selector[selector.length - 1] = replacementQuery;
9408 }; // NOTE: add new expression syntax here to have it recognised by the parser;
9409 // - a query contains all adjacent (i.e. no separator in between) expressions;
9410 // - the current query is stored in selector[i]
9411 // - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
9412
9413
9414 var exprs = [{
9415 name: 'group',
9416 // just used for identifying when debugging
9417 query: true,
9418 regex: '(' + tokens.group + ')',
9419 populate: function populate(selector, query, _ref) {
9420 var _ref2 = _slicedToArray(_ref, 1),
9421 group = _ref2[0];
9422
9423 query.checks.push({
9424 type: Type.GROUP,
9425 value: group === '*' ? group : group + 's'
9426 });
9427 }
9428 }, {
9429 name: 'state',
9430 query: true,
9431 regex: stateSelectorRegex,
9432 populate: function populate(selector, query, _ref3) {
9433 var _ref4 = _slicedToArray(_ref3, 1),
9434 state = _ref4[0];
9435
9436 query.checks.push({
9437 type: Type.STATE,
9438 value: state
9439 });
9440 }
9441 }, {
9442 name: 'id',
9443 query: true,
9444 regex: '\\#(' + tokens.id + ')',
9445 populate: function populate(selector, query, _ref5) {
9446 var _ref6 = _slicedToArray(_ref5, 1),
9447 id = _ref6[0];
9448
9449 query.checks.push({
9450 type: Type.ID,
9451 value: cleanMetaChars(id)
9452 });
9453 }
9454 }, {
9455 name: 'className',
9456 query: true,
9457 regex: '\\.(' + tokens.className + ')',
9458 populate: function populate(selector, query, _ref7) {
9459 var _ref8 = _slicedToArray(_ref7, 1),
9460 className = _ref8[0];
9461
9462 query.checks.push({
9463 type: Type.CLASS,
9464 value: cleanMetaChars(className)
9465 });
9466 }
9467 }, {
9468 name: 'dataExists',
9469 query: true,
9470 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
9471 populate: function populate(selector, query, _ref9) {
9472 var _ref10 = _slicedToArray(_ref9, 1),
9473 variable = _ref10[0];
9474
9475 query.checks.push({
9476 type: Type.DATA_EXIST,
9477 field: cleanMetaChars(variable)
9478 });
9479 }
9480 }, {
9481 name: 'dataCompare',
9482 query: true,
9483 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
9484 populate: function populate(selector, query, _ref11) {
9485 var _ref12 = _slicedToArray(_ref11, 3),
9486 variable = _ref12[0],
9487 comparatorOp = _ref12[1],
9488 value = _ref12[2];
9489
9490 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
9491
9492 if (valueIsString) {
9493 value = value.substring(1, value.length - 1);
9494 } else {
9495 value = parseFloat(value);
9496 }
9497
9498 query.checks.push({
9499 type: Type.DATA_COMPARE,
9500 field: cleanMetaChars(variable),
9501 operator: comparatorOp,
9502 value: value
9503 });
9504 }
9505 }, {
9506 name: 'dataBool',
9507 query: true,
9508 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
9509 populate: function populate(selector, query, _ref13) {
9510 var _ref14 = _slicedToArray(_ref13, 2),
9511 boolOp = _ref14[0],
9512 variable = _ref14[1];
9513
9514 query.checks.push({
9515 type: Type.DATA_BOOL,
9516 field: cleanMetaChars(variable),
9517 operator: boolOp
9518 });
9519 }
9520 }, {
9521 name: 'metaCompare',
9522 query: true,
9523 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
9524 populate: function populate(selector, query, _ref15) {
9525 var _ref16 = _slicedToArray(_ref15, 3),
9526 meta = _ref16[0],
9527 comparatorOp = _ref16[1],
9528 number = _ref16[2];
9529
9530 query.checks.push({
9531 type: Type.META_COMPARE,
9532 field: cleanMetaChars(meta),
9533 operator: comparatorOp,
9534 value: parseFloat(number)
9535 });
9536 }
9537 }, {
9538 name: 'nextQuery',
9539 separator: true,
9540 regex: tokens.separator,
9541 populate: function populate(selector, query) {
9542 var currentSubject = selector.currentSubject;
9543 var edgeCount = selector.edgeCount;
9544 var compoundCount = selector.compoundCount;
9545 var lastQ = selector[selector.length - 1];
9546
9547 if (currentSubject != null) {
9548 lastQ.subject = currentSubject;
9549 selector.currentSubject = null;
9550 }
9551
9552 lastQ.edgeCount = edgeCount;
9553 lastQ.compoundCount = compoundCount;
9554 selector.edgeCount = 0;
9555 selector.compoundCount = 0; // go on to next query
9556
9557 var nextQuery = selector[selector.length++] = newQuery();
9558 return nextQuery; // this is the new query to be filled by the following exprs
9559 }
9560 }, {
9561 name: 'directedEdge',
9562 separator: true,
9563 regex: tokens.directedEdge,
9564 populate: function populate(selector, query) {
9565 if (selector.currentSubject == null) {
9566 // undirected edge
9567 var edgeQuery = newQuery();
9568 var source = query;
9569 var target = newQuery();
9570 edgeQuery.checks.push({
9571 type: Type.DIRECTED_EDGE,
9572 source: source,
9573 target: target
9574 }); // the query in the selector should be the edge rather than the source
9575
9576 replaceLastQuery(selector, query, edgeQuery);
9577 selector.edgeCount++; // we're now populating the target query with expressions that follow
9578
9579 return target;
9580 } else {
9581 // source/target
9582 var srcTgtQ = newQuery();
9583 var _source = query;
9584
9585 var _target = newQuery();
9586
9587 srcTgtQ.checks.push({
9588 type: Type.NODE_SOURCE,
9589 source: _source,
9590 target: _target
9591 }); // the query in the selector should be the neighbourhood rather than the node
9592
9593 replaceLastQuery(selector, query, srcTgtQ);
9594 selector.edgeCount++;
9595 return _target; // now populating the target with the following expressions
9596 }
9597 }
9598 }, {
9599 name: 'undirectedEdge',
9600 separator: true,
9601 regex: tokens.undirectedEdge,
9602 populate: function populate(selector, query) {
9603 if (selector.currentSubject == null) {
9604 // undirected edge
9605 var edgeQuery = newQuery();
9606 var source = query;
9607 var target = newQuery();
9608 edgeQuery.checks.push({
9609 type: Type.UNDIRECTED_EDGE,
9610 nodes: [source, target]
9611 }); // the query in the selector should be the edge rather than the source
9612
9613 replaceLastQuery(selector, query, edgeQuery);
9614 selector.edgeCount++; // we're now populating the target query with expressions that follow
9615
9616 return target;
9617 } else {
9618 // neighbourhood
9619 var nhoodQ = newQuery();
9620 var node = query;
9621 var neighbor = newQuery();
9622 nhoodQ.checks.push({
9623 type: Type.NODE_NEIGHBOR,
9624 node: node,
9625 neighbor: neighbor
9626 }); // the query in the selector should be the neighbourhood rather than the node
9627
9628 replaceLastQuery(selector, query, nhoodQ);
9629 return neighbor; // now populating the neighbor with following expressions
9630 }
9631 }
9632 }, {
9633 name: 'child',
9634 separator: true,
9635 regex: tokens.child,
9636 populate: function populate(selector, query) {
9637 if (selector.currentSubject == null) {
9638 // default: child query
9639 var parentChildQuery = newQuery();
9640 var child = newQuery();
9641 var parent = selector[selector.length - 1];
9642 parentChildQuery.checks.push({
9643 type: Type.CHILD,
9644 parent: parent,
9645 child: child
9646 }); // the query in the selector should be the '>' itself
9647
9648 replaceLastQuery(selector, query, parentChildQuery);
9649 selector.compoundCount++; // we're now populating the child query with expressions that follow
9650
9651 return child;
9652 } else if (selector.currentSubject === query) {
9653 // compound split query
9654 var compound = newQuery();
9655 var left = selector[selector.length - 1];
9656 var right = newQuery();
9657 var subject = newQuery();
9658
9659 var _child = newQuery();
9660
9661 var _parent = newQuery(); // set up the root compound q
9662
9663
9664 compound.checks.push({
9665 type: Type.COMPOUND_SPLIT,
9666 left: left,
9667 right: right,
9668 subject: subject
9669 }); // populate the subject and replace the q at the old spot (within left) with TRUE
9670
9671 subject.checks = query.checks; // take the checks from the left
9672
9673 query.checks = [{
9674 type: Type.TRUE
9675 }]; // checks under left refs the subject implicitly
9676 // set up the right q
9677
9678 _parent.checks.push({
9679 type: Type.TRUE
9680 }); // parent implicitly refs the subject
9681
9682
9683 right.checks.push({
9684 type: Type.PARENT,
9685 // type is swapped on right side queries
9686 parent: _parent,
9687 child: _child // empty for now
9688
9689 });
9690 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
9691
9692 selector.currentSubject = subject;
9693 selector.compoundCount++;
9694 return _child; // now populating the right side's child
9695 } else {
9696 // parent query
9697 // info for parent query
9698 var _parent2 = newQuery();
9699
9700 var _child2 = newQuery();
9701
9702 var pcQChecks = [{
9703 type: Type.PARENT,
9704 parent: _parent2,
9705 child: _child2
9706 }]; // the parent-child query takes the place of the query previously being populated
9707
9708 _parent2.checks = query.checks; // the previous query contains the checks for the parent
9709
9710 query.checks = pcQChecks; // pc query takes over
9711
9712 selector.compoundCount++;
9713 return _child2; // we're now populating the child
9714 }
9715 }
9716 }, {
9717 name: 'descendant',
9718 separator: true,
9719 regex: tokens.descendant,
9720 populate: function populate(selector, query) {
9721 if (selector.currentSubject == null) {
9722 // default: descendant query
9723 var ancChQuery = newQuery();
9724 var descendant = newQuery();
9725 var ancestor = selector[selector.length - 1];
9726 ancChQuery.checks.push({
9727 type: Type.DESCENDANT,
9728 ancestor: ancestor,
9729 descendant: descendant
9730 }); // the query in the selector should be the '>' itself
9731
9732 replaceLastQuery(selector, query, ancChQuery);
9733 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
9734
9735 return descendant;
9736 } else if (selector.currentSubject === query) {
9737 // compound split query
9738 var compound = newQuery();
9739 var left = selector[selector.length - 1];
9740 var right = newQuery();
9741 var subject = newQuery();
9742
9743 var _descendant = newQuery();
9744
9745 var _ancestor = newQuery(); // set up the root compound q
9746
9747
9748 compound.checks.push({
9749 type: Type.COMPOUND_SPLIT,
9750 left: left,
9751 right: right,
9752 subject: subject
9753 }); // populate the subject and replace the q at the old spot (within left) with TRUE
9754
9755 subject.checks = query.checks; // take the checks from the left
9756
9757 query.checks = [{
9758 type: Type.TRUE
9759 }]; // checks under left refs the subject implicitly
9760 // set up the right q
9761
9762 _ancestor.checks.push({
9763 type: Type.TRUE
9764 }); // ancestor implicitly refs the subject
9765
9766
9767 right.checks.push({
9768 type: Type.ANCESTOR,
9769 // type is swapped on right side queries
9770 ancestor: _ancestor,
9771 descendant: _descendant // empty for now
9772
9773 });
9774 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
9775
9776 selector.currentSubject = subject;
9777 selector.compoundCount++;
9778 return _descendant; // now populating the right side's descendant
9779 } else {
9780 // ancestor query
9781 // info for parent query
9782 var _ancestor2 = newQuery();
9783
9784 var _descendant2 = newQuery();
9785
9786 var adQChecks = [{
9787 type: Type.ANCESTOR,
9788 ancestor: _ancestor2,
9789 descendant: _descendant2
9790 }]; // the parent-child query takes the place of the query previously being populated
9791
9792 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
9793
9794 query.checks = adQChecks; // pc query takes over
9795
9796 selector.compoundCount++;
9797 return _descendant2; // we're now populating the child
9798 }
9799 }
9800 }, {
9801 name: 'subject',
9802 modifier: true,
9803 regex: tokens.subject,
9804 populate: function populate(selector, query) {
9805 if (selector.currentSubject != null && selector.currentSubject !== query) {
9806 warn('Redefinition of subject in selector `' + selector.toString() + '`');
9807 return false;
9808 }
9809
9810 selector.currentSubject = query;
9811 var topQ = selector[selector.length - 1];
9812 var topChk = topQ.checks[0];
9813 var topType = topChk == null ? null : topChk.type;
9814
9815 if (topType === Type.DIRECTED_EDGE) {
9816 // directed edge with subject on the target
9817 // change to target node check
9818 topChk.type = Type.NODE_TARGET;
9819 } else if (topType === Type.UNDIRECTED_EDGE) {
9820 // undirected edge with subject on the second node
9821 // change to neighbor check
9822 topChk.type = Type.NODE_NEIGHBOR;
9823 topChk.node = topChk.nodes[1]; // second node is subject
9824
9825 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
9826
9827 topChk.nodes = null;
9828 }
9829 }
9830 }];
9831 exprs.forEach(function (e) {
9832 return e.regexObj = new RegExp('^' + e.regex);
9833 });
9834
9835 /**
9836 * Of all the expressions, find the first match in the remaining text.
9837 * @param {string} remaining The remaining text to parse
9838 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
9839 */
9840
9841 var consumeExpr = function consumeExpr(remaining) {
9842 var expr;
9843 var match;
9844 var name;
9845
9846 for (var j = 0; j < exprs.length; j++) {
9847 var e = exprs[j];
9848 var n = e.name;
9849 var m = remaining.match(e.regexObj);
9850
9851 if (m != null) {
9852 match = m;
9853 expr = e;
9854 name = n;
9855 var consumed = m[0];
9856 remaining = remaining.substring(consumed.length);
9857 break; // we've consumed one expr, so we can return now
9858 }
9859 }
9860
9861 return {
9862 expr: expr,
9863 match: match,
9864 name: name,
9865 remaining: remaining
9866 };
9867 };
9868 /**
9869 * Consume all the leading whitespace
9870 * @param {string} remaining The text to consume
9871 * @returns The text with the leading whitespace removed
9872 */
9873
9874
9875 var consumeWhitespace = function consumeWhitespace(remaining) {
9876 var match = remaining.match(/^\s+/);
9877
9878 if (match) {
9879 var consumed = match[0];
9880 remaining = remaining.substring(consumed.length);
9881 }
9882
9883 return remaining;
9884 };
9885 /**
9886 * Parse the string and store the parsed representation in the Selector.
9887 * @param {string} selector The selector string
9888 * @returns `true` if the selector was successfully parsed, `false` otherwise
9889 */
9890
9891
9892 var parse = function parse(selector) {
9893 var self = this;
9894 var remaining = self.inputText = selector;
9895 var currentQuery = self[0] = newQuery();
9896 self.length = 1;
9897 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
9898
9899 for (;;) {
9900 var exprInfo = consumeExpr(remaining);
9901
9902 if (exprInfo.expr == null) {
9903 warn('The selector `' + selector + '`is invalid');
9904 return false;
9905 } else {
9906 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
9907
9908 var ret = exprInfo.expr.populate(self, currentQuery, args);
9909
9910 if (ret === false) {
9911 return false; // exit if population failed
9912 } else if (ret != null) {
9913 currentQuery = ret; // change the current query to be filled if the expr specifies
9914 }
9915 }
9916
9917 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
9918
9919 if (remaining.match(/^\s*$/)) {
9920 break;
9921 }
9922 }
9923
9924 var lastQ = self[self.length - 1];
9925
9926 if (self.currentSubject != null) {
9927 lastQ.subject = self.currentSubject;
9928 }
9929
9930 lastQ.edgeCount = self.edgeCount;
9931 lastQ.compoundCount = self.compoundCount;
9932
9933 for (var i = 0; i < self.length; i++) {
9934 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
9935
9936 if (q.compoundCount > 0 && q.edgeCount > 0) {
9937 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
9938 return false;
9939 }
9940
9941 if (q.edgeCount > 1) {
9942 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
9943 return false;
9944 } else if (q.edgeCount === 1) {
9945 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.');
9946 }
9947 }
9948
9949 return true; // success
9950 };
9951 /**
9952 * Get the selector represented as a string. This value uses default formatting,
9953 * so things like spacing may differ from the input text passed to the constructor.
9954 * @returns {string} The selector string
9955 */
9956
9957
9958 var toString = function toString() {
9959 if (this.toStringCache != null) {
9960 return this.toStringCache;
9961 }
9962
9963 var clean = function clean(obj) {
9964 if (obj == null) {
9965 return '';
9966 } else {
9967 return obj;
9968 }
9969 };
9970
9971 var cleanVal = function cleanVal(val) {
9972 if (string(val)) {
9973 return '"' + val + '"';
9974 } else {
9975 return clean(val);
9976 }
9977 };
9978
9979 var space = function space(val) {
9980 return ' ' + val + ' ';
9981 };
9982
9983 var checkToString = function checkToString(check, subject) {
9984 var type = check.type,
9985 value = check.value;
9986
9987 switch (type) {
9988 case Type.GROUP:
9989 {
9990 var group = clean(value);
9991 return group.substring(0, group.length - 1);
9992 }
9993
9994 case Type.DATA_COMPARE:
9995 {
9996 var field = check.field,
9997 operator = check.operator;
9998 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
9999 }
10000
10001 case Type.DATA_BOOL:
10002 {
10003 var _operator = check.operator,
10004 _field = check.field;
10005 return '[' + clean(_operator) + _field + ']';
10006 }
10007
10008 case Type.DATA_EXIST:
10009 {
10010 var _field2 = check.field;
10011 return '[' + _field2 + ']';
10012 }
10013
10014 case Type.META_COMPARE:
10015 {
10016 var _operator2 = check.operator,
10017 _field3 = check.field;
10018 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
10019 }
10020
10021 case Type.STATE:
10022 {
10023 return value;
10024 }
10025
10026 case Type.ID:
10027 {
10028 return '#' + value;
10029 }
10030
10031 case Type.CLASS:
10032 {
10033 return '.' + value;
10034 }
10035
10036 case Type.PARENT:
10037 case Type.CHILD:
10038 {
10039 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
10040 }
10041
10042 case Type.ANCESTOR:
10043 case Type.DESCENDANT:
10044 {
10045 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
10046 }
10047
10048 case Type.COMPOUND_SPLIT:
10049 {
10050 var lhs = queryToString(check.left, subject);
10051 var sub = queryToString(check.subject, subject);
10052 var rhs = queryToString(check.right, subject);
10053 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
10054 }
10055
10056 case Type.TRUE:
10057 {
10058 return '';
10059 }
10060 }
10061 };
10062
10063 var queryToString = function queryToString(query, subject) {
10064 return query.checks.reduce(function (str, chk, i) {
10065 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
10066 }, '');
10067 };
10068
10069 var str = '';
10070
10071 for (var i = 0; i < this.length; i++) {
10072 var query = this[i];
10073 str += queryToString(query, query.subject);
10074
10075 if (this.length > 1 && i < this.length - 1) {
10076 str += ', ';
10077 }
10078 }
10079
10080 this.toStringCache = str;
10081 return str;
10082 };
10083 var parse$1 = {
10084 parse: parse,
10085 toString: toString
10086 };
10087
10088 var valCmp = function valCmp(fieldVal, operator, value) {
10089 var matches;
10090 var isFieldStr = string(fieldVal);
10091 var isFieldNum = number$1(fieldVal);
10092 var isValStr = string(value);
10093 var fieldStr, valStr;
10094 var caseInsensitive = false;
10095 var notExpr = false;
10096 var isIneqCmp = false;
10097
10098 if (operator.indexOf('!') >= 0) {
10099 operator = operator.replace('!', '');
10100 notExpr = true;
10101 }
10102
10103 if (operator.indexOf('@') >= 0) {
10104 operator = operator.replace('@', '');
10105 caseInsensitive = true;
10106 }
10107
10108 if (isFieldStr || isValStr || caseInsensitive) {
10109 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
10110 valStr = '' + value;
10111 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
10112 // even if we're comparing numbers
10113
10114
10115 if (caseInsensitive) {
10116 fieldVal = fieldStr = fieldStr.toLowerCase();
10117 value = valStr = valStr.toLowerCase();
10118 }
10119
10120 switch (operator) {
10121 case '*=':
10122 matches = fieldStr.indexOf(valStr) >= 0;
10123 break;
10124
10125 case '$=':
10126 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
10127 break;
10128
10129 case '^=':
10130 matches = fieldStr.indexOf(valStr) === 0;
10131 break;
10132
10133 case '=':
10134 matches = fieldVal === value;
10135 break;
10136
10137 case '>':
10138 isIneqCmp = true;
10139 matches = fieldVal > value;
10140 break;
10141
10142 case '>=':
10143 isIneqCmp = true;
10144 matches = fieldVal >= value;
10145 break;
10146
10147 case '<':
10148 isIneqCmp = true;
10149 matches = fieldVal < value;
10150 break;
10151
10152 case '<=':
10153 isIneqCmp = true;
10154 matches = fieldVal <= value;
10155 break;
10156
10157 default:
10158 matches = false;
10159 break;
10160 } // apply the not op, but null vals for inequalities should always stay non-matching
10161
10162
10163 if (notExpr && (fieldVal != null || !isIneqCmp)) {
10164 matches = !matches;
10165 }
10166
10167 return matches;
10168 };
10169 var boolCmp = function boolCmp(fieldVal, operator) {
10170 switch (operator) {
10171 case '?':
10172 return fieldVal ? true : false;
10173
10174 case '!':
10175 return fieldVal ? false : true;
10176
10177 case '^':
10178 return fieldVal === undefined;
10179 }
10180 };
10181 var existCmp = function existCmp(fieldVal) {
10182 return fieldVal !== undefined;
10183 };
10184 var data$1 = function data(ele, field) {
10185 return ele.data(field);
10186 };
10187 var meta = function meta(ele, field) {
10188 return ele[field]();
10189 };
10190
10191 /** A lookup of `match(check, ele)` functions by `Type` int */
10192
10193 var match = [];
10194 /**
10195 * Returns whether the query matches for the element
10196 * @param query The `{ type, value, ... }` query object
10197 * @param ele The element to compare against
10198 */
10199
10200 var matches$1 = function matches(query, ele) {
10201 return query.checks.every(function (chk) {
10202 return match[chk.type](chk, ele);
10203 });
10204 };
10205
10206 match[Type.GROUP] = function (check, ele) {
10207 var group = check.value;
10208 return group === '*' || group === ele.group();
10209 };
10210
10211 match[Type.STATE] = function (check, ele) {
10212 var stateSelector = check.value;
10213 return stateSelectorMatches(stateSelector, ele);
10214 };
10215
10216 match[Type.ID] = function (check, ele) {
10217 var id = check.value;
10218 return ele.id() === id;
10219 };
10220
10221 match[Type.CLASS] = function (check, ele) {
10222 var cls = check.value;
10223 return ele.hasClass(cls);
10224 };
10225
10226 match[Type.META_COMPARE] = function (check, ele) {
10227 var field = check.field,
10228 operator = check.operator,
10229 value = check.value;
10230 return valCmp(meta(ele, field), operator, value);
10231 };
10232
10233 match[Type.DATA_COMPARE] = function (check, ele) {
10234 var field = check.field,
10235 operator = check.operator,
10236 value = check.value;
10237 return valCmp(data$1(ele, field), operator, value);
10238 };
10239
10240 match[Type.DATA_BOOL] = function (check, ele) {
10241 var field = check.field,
10242 operator = check.operator;
10243 return boolCmp(data$1(ele, field), operator);
10244 };
10245
10246 match[Type.DATA_EXIST] = function (check, ele) {
10247 var field = check.field;
10248 check.operator;
10249 return existCmp(data$1(ele, field));
10250 };
10251
10252 match[Type.UNDIRECTED_EDGE] = function (check, ele) {
10253 var qA = check.nodes[0];
10254 var qB = check.nodes[1];
10255 var src = ele.source();
10256 var tgt = ele.target();
10257 return matches$1(qA, src) && matches$1(qB, tgt) || matches$1(qB, src) && matches$1(qA, tgt);
10258 };
10259
10260 match[Type.NODE_NEIGHBOR] = function (check, ele) {
10261 return matches$1(check.node, ele) && ele.neighborhood().some(function (n) {
10262 return n.isNode() && matches$1(check.neighbor, n);
10263 });
10264 };
10265
10266 match[Type.DIRECTED_EDGE] = function (check, ele) {
10267 return matches$1(check.source, ele.source()) && matches$1(check.target, ele.target());
10268 };
10269
10270 match[Type.NODE_SOURCE] = function (check, ele) {
10271 return matches$1(check.source, ele) && ele.outgoers().some(function (n) {
10272 return n.isNode() && matches$1(check.target, n);
10273 });
10274 };
10275
10276 match[Type.NODE_TARGET] = function (check, ele) {
10277 return matches$1(check.target, ele) && ele.incomers().some(function (n) {
10278 return n.isNode() && matches$1(check.source, n);
10279 });
10280 };
10281
10282 match[Type.CHILD] = function (check, ele) {
10283 return matches$1(check.child, ele) && matches$1(check.parent, ele.parent());
10284 };
10285
10286 match[Type.PARENT] = function (check, ele) {
10287 return matches$1(check.parent, ele) && ele.children().some(function (c) {
10288 return matches$1(check.child, c);
10289 });
10290 };
10291
10292 match[Type.DESCENDANT] = function (check, ele) {
10293 return matches$1(check.descendant, ele) && ele.ancestors().some(function (a) {
10294 return matches$1(check.ancestor, a);
10295 });
10296 };
10297
10298 match[Type.ANCESTOR] = function (check, ele) {
10299 return matches$1(check.ancestor, ele) && ele.descendants().some(function (d) {
10300 return matches$1(check.descendant, d);
10301 });
10302 };
10303
10304 match[Type.COMPOUND_SPLIT] = function (check, ele) {
10305 return matches$1(check.subject, ele) && matches$1(check.left, ele) && matches$1(check.right, ele);
10306 };
10307
10308 match[Type.TRUE] = function () {
10309 return true;
10310 };
10311
10312 match[Type.COLLECTION] = function (check, ele) {
10313 var collection = check.value;
10314 return collection.has(ele);
10315 };
10316
10317 match[Type.FILTER] = function (check, ele) {
10318 var filter = check.value;
10319 return filter(ele);
10320 };
10321
10322 var filter = function filter(collection) {
10323 var self = this; // for 1 id #foo queries, just get the element
10324
10325 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
10326 return collection.getElementById(self[0].checks[0].value).collection();
10327 }
10328
10329 var selectorFunction = function selectorFunction(element) {
10330 for (var j = 0; j < self.length; j++) {
10331 var query = self[j];
10332
10333 if (matches$1(query, element)) {
10334 return true;
10335 }
10336 }
10337
10338 return false;
10339 };
10340
10341 if (self.text() == null) {
10342 selectorFunction = function selectorFunction() {
10343 return true;
10344 };
10345 }
10346
10347 return collection.filter(selectorFunction);
10348 }; // filter
10349 // does selector match a single element?
10350
10351
10352 var matches = function matches(ele) {
10353 var self = this;
10354
10355 for (var j = 0; j < self.length; j++) {
10356 var query = self[j];
10357
10358 if (matches$1(query, ele)) {
10359 return true;
10360 }
10361 }
10362
10363 return false;
10364 }; // matches
10365
10366
10367 var matching = {
10368 matches: matches,
10369 filter: filter
10370 };
10371
10372 var Selector = function Selector(selector) {
10373 this.inputText = selector;
10374 this.currentSubject = null;
10375 this.compoundCount = 0;
10376 this.edgeCount = 0;
10377 this.length = 0;
10378
10379 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
10380 this.addQuery({
10381 checks: [{
10382 type: Type.COLLECTION,
10383 value: selector.collection()
10384 }]
10385 });
10386 } else if (fn$6(selector)) {
10387 this.addQuery({
10388 checks: [{
10389 type: Type.FILTER,
10390 value: selector
10391 }]
10392 });
10393 } else if (string(selector)) {
10394 if (!this.parse(selector)) {
10395 this.invalid = true;
10396 }
10397 } else {
10398 error('A selector must be created from a string; found ');
10399 }
10400 };
10401
10402 var selfn = Selector.prototype;
10403 [parse$1, matching].forEach(function (p) {
10404 return extend(selfn, p);
10405 });
10406
10407 selfn.text = function () {
10408 return this.inputText;
10409 };
10410
10411 selfn.size = function () {
10412 return this.length;
10413 };
10414
10415 selfn.eq = function (i) {
10416 return this[i];
10417 };
10418
10419 selfn.sameText = function (otherSel) {
10420 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
10421 };
10422
10423 selfn.addQuery = function (q) {
10424 this[this.length++] = q;
10425 };
10426
10427 selfn.selector = selfn.toString;
10428
10429 var elesfn$g = {
10430 allAre: function allAre(selector) {
10431 var selObj = new Selector(selector);
10432 return this.every(function (ele) {
10433 return selObj.matches(ele);
10434 });
10435 },
10436 is: function is(selector) {
10437 var selObj = new Selector(selector);
10438 return this.some(function (ele) {
10439 return selObj.matches(ele);
10440 });
10441 },
10442 some: function some(fn, thisArg) {
10443 for (var i = 0; i < this.length; i++) {
10444 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
10445
10446 if (ret) {
10447 return true;
10448 }
10449 }
10450
10451 return false;
10452 },
10453 every: function every(fn, thisArg) {
10454 for (var i = 0; i < this.length; i++) {
10455 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
10456
10457 if (!ret) {
10458 return false;
10459 }
10460 }
10461
10462 return true;
10463 },
10464 same: function same(collection) {
10465 // cheap collection ref check
10466 if (this === collection) {
10467 return true;
10468 }
10469
10470 collection = this.cy().collection(collection);
10471 var thisLength = this.length;
10472 var collectionLength = collection.length; // cheap length check
10473
10474 if (thisLength !== collectionLength) {
10475 return false;
10476 } // cheap element ref check
10477
10478
10479 if (thisLength === 1) {
10480 return this[0] === collection[0];
10481 }
10482
10483 return this.every(function (ele) {
10484 return collection.hasElementWithId(ele.id());
10485 });
10486 },
10487 anySame: function anySame(collection) {
10488 collection = this.cy().collection(collection);
10489 return this.some(function (ele) {
10490 return collection.hasElementWithId(ele.id());
10491 });
10492 },
10493 allAreNeighbors: function allAreNeighbors(collection) {
10494 collection = this.cy().collection(collection);
10495 var nhood = this.neighborhood();
10496 return collection.every(function (ele) {
10497 return nhood.hasElementWithId(ele.id());
10498 });
10499 },
10500 contains: function contains(collection) {
10501 collection = this.cy().collection(collection);
10502 var self = this;
10503 return collection.every(function (ele) {
10504 return self.hasElementWithId(ele.id());
10505 });
10506 }
10507 };
10508 elesfn$g.allAreNeighbours = elesfn$g.allAreNeighbors;
10509 elesfn$g.has = elesfn$g.contains;
10510 elesfn$g.equal = elesfn$g.equals = elesfn$g.same;
10511
10512 var cache = function cache(fn, name) {
10513 return function traversalCache(arg1, arg2, arg3, arg4) {
10514 var selectorOrEles = arg1;
10515 var eles = this;
10516 var key;
10517
10518 if (selectorOrEles == null) {
10519 key = '';
10520 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
10521 key = selectorOrEles.id();
10522 }
10523
10524 if (eles.length === 1 && key) {
10525 var _p = eles[0]._private;
10526 var tch = _p.traversalCache = _p.traversalCache || {};
10527 var ch = tch[name] = tch[name] || [];
10528 var hash = hashString(key);
10529 var cacheHit = ch[hash];
10530
10531 if (cacheHit) {
10532 return cacheHit;
10533 } else {
10534 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
10535 }
10536 } else {
10537 return fn.call(eles, arg1, arg2, arg3, arg4);
10538 }
10539 };
10540 };
10541
10542 var elesfn$f = {
10543 parent: function parent(selector) {
10544 var parents = []; // optimisation for single ele call
10545
10546 if (this.length === 1) {
10547 var parent = this[0]._private.parent;
10548
10549 if (parent) {
10550 return parent;
10551 }
10552 }
10553
10554 for (var i = 0; i < this.length; i++) {
10555 var ele = this[i];
10556 var _parent = ele._private.parent;
10557
10558 if (_parent) {
10559 parents.push(_parent);
10560 }
10561 }
10562
10563 return this.spawn(parents, true).filter(selector);
10564 },
10565 parents: function parents(selector) {
10566 var parents = [];
10567 var eles = this.parent();
10568
10569 while (eles.nonempty()) {
10570 for (var i = 0; i < eles.length; i++) {
10571 var ele = eles[i];
10572 parents.push(ele);
10573 }
10574
10575 eles = eles.parent();
10576 }
10577
10578 return this.spawn(parents, true).filter(selector);
10579 },
10580 commonAncestors: function commonAncestors(selector) {
10581 var ancestors;
10582
10583 for (var i = 0; i < this.length; i++) {
10584 var ele = this[i];
10585 var parents = ele.parents();
10586 ancestors = ancestors || parents;
10587 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
10588 }
10589
10590 return ancestors.filter(selector);
10591 },
10592 orphans: function orphans(selector) {
10593 return this.stdFilter(function (ele) {
10594 return ele.isOrphan();
10595 }).filter(selector);
10596 },
10597 nonorphans: function nonorphans(selector) {
10598 return this.stdFilter(function (ele) {
10599 return ele.isChild();
10600 }).filter(selector);
10601 },
10602 children: cache(function (selector) {
10603 var children = [];
10604
10605 for (var i = 0; i < this.length; i++) {
10606 var ele = this[i];
10607 var eleChildren = ele._private.children;
10608
10609 for (var j = 0; j < eleChildren.length; j++) {
10610 children.push(eleChildren[j]);
10611 }
10612 }
10613
10614 return this.spawn(children, true).filter(selector);
10615 }, 'children'),
10616 siblings: function siblings(selector) {
10617 return this.parent().children().not(this).filter(selector);
10618 },
10619 isParent: function isParent() {
10620 var ele = this[0];
10621
10622 if (ele) {
10623 return ele.isNode() && ele._private.children.length !== 0;
10624 }
10625 },
10626 isChildless: function isChildless() {
10627 var ele = this[0];
10628
10629 if (ele) {
10630 return ele.isNode() && ele._private.children.length === 0;
10631 }
10632 },
10633 isChild: function isChild() {
10634 var ele = this[0];
10635
10636 if (ele) {
10637 return ele.isNode() && ele._private.parent != null;
10638 }
10639 },
10640 isOrphan: function isOrphan() {
10641 var ele = this[0];
10642
10643 if (ele) {
10644 return ele.isNode() && ele._private.parent == null;
10645 }
10646 },
10647 descendants: function descendants(selector) {
10648 var elements = [];
10649
10650 function add(eles) {
10651 for (var i = 0; i < eles.length; i++) {
10652 var ele = eles[i];
10653 elements.push(ele);
10654
10655 if (ele.children().nonempty()) {
10656 add(ele.children());
10657 }
10658 }
10659 }
10660
10661 add(this.children());
10662 return this.spawn(elements, true).filter(selector);
10663 }
10664 };
10665
10666 function forEachCompound(eles, fn, includeSelf, recursiveStep) {
10667 var q = [];
10668 var did = new Set$1();
10669 var cy = eles.cy();
10670 var hasCompounds = cy.hasCompoundNodes();
10671
10672 for (var i = 0; i < eles.length; i++) {
10673 var ele = eles[i];
10674
10675 if (includeSelf) {
10676 q.push(ele);
10677 } else if (hasCompounds) {
10678 recursiveStep(q, did, ele);
10679 }
10680 }
10681
10682 while (q.length > 0) {
10683 var _ele = q.shift();
10684
10685 fn(_ele);
10686 did.add(_ele.id());
10687
10688 if (hasCompounds) {
10689 recursiveStep(q, did, _ele);
10690 }
10691 }
10692
10693 return eles;
10694 }
10695
10696 function addChildren(q, did, ele) {
10697 if (ele.isParent()) {
10698 var children = ele._private.children;
10699
10700 for (var i = 0; i < children.length; i++) {
10701 var child = children[i];
10702
10703 if (!did.has(child.id())) {
10704 q.push(child);
10705 }
10706 }
10707 }
10708 } // very efficient version of eles.add( eles.descendants() ).forEach()
10709 // for internal use
10710
10711
10712 elesfn$f.forEachDown = function (fn) {
10713 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
10714 return forEachCompound(this, fn, includeSelf, addChildren);
10715 };
10716
10717 function addParent(q, did, ele) {
10718 if (ele.isChild()) {
10719 var parent = ele._private.parent;
10720
10721 if (!did.has(parent.id())) {
10722 q.push(parent);
10723 }
10724 }
10725 }
10726
10727 elesfn$f.forEachUp = function (fn) {
10728 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
10729 return forEachCompound(this, fn, includeSelf, addParent);
10730 };
10731
10732 function addParentAndChildren(q, did, ele) {
10733 addParent(q, did, ele);
10734 addChildren(q, did, ele);
10735 }
10736
10737 elesfn$f.forEachUpAndDown = function (fn) {
10738 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
10739 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
10740 }; // aliases
10741
10742
10743 elesfn$f.ancestors = elesfn$f.parents;
10744
10745 var fn$5, elesfn$e;
10746 fn$5 = elesfn$e = {
10747 data: define.data({
10748 field: 'data',
10749 bindingEvent: 'data',
10750 allowBinding: true,
10751 allowSetting: true,
10752 settingEvent: 'data',
10753 settingTriggersEvent: true,
10754 triggerFnName: 'trigger',
10755 allowGetting: true,
10756 immutableKeys: {
10757 'id': true,
10758 'source': true,
10759 'target': true,
10760 'parent': true
10761 },
10762 updateStyle: true
10763 }),
10764 removeData: define.removeData({
10765 field: 'data',
10766 event: 'data',
10767 triggerFnName: 'trigger',
10768 triggerEvent: true,
10769 immutableKeys: {
10770 'id': true,
10771 'source': true,
10772 'target': true,
10773 'parent': true
10774 },
10775 updateStyle: true
10776 }),
10777 scratch: define.data({
10778 field: 'scratch',
10779 bindingEvent: 'scratch',
10780 allowBinding: true,
10781 allowSetting: true,
10782 settingEvent: 'scratch',
10783 settingTriggersEvent: true,
10784 triggerFnName: 'trigger',
10785 allowGetting: true,
10786 updateStyle: true
10787 }),
10788 removeScratch: define.removeData({
10789 field: 'scratch',
10790 event: 'scratch',
10791 triggerFnName: 'trigger',
10792 triggerEvent: true,
10793 updateStyle: true
10794 }),
10795 rscratch: define.data({
10796 field: 'rscratch',
10797 allowBinding: false,
10798 allowSetting: true,
10799 settingTriggersEvent: false,
10800 allowGetting: true
10801 }),
10802 removeRscratch: define.removeData({
10803 field: 'rscratch',
10804 triggerEvent: false
10805 }),
10806 id: function id() {
10807 var ele = this[0];
10808
10809 if (ele) {
10810 return ele._private.data.id;
10811 }
10812 }
10813 }; // aliases
10814
10815 fn$5.attr = fn$5.data;
10816 fn$5.removeAttr = fn$5.removeData;
10817 var data = elesfn$e;
10818
10819 var elesfn$d = {};
10820
10821 function defineDegreeFunction(callback) {
10822 return function (includeLoops) {
10823 var self = this;
10824
10825 if (includeLoops === undefined) {
10826 includeLoops = true;
10827 }
10828
10829 if (self.length === 0) {
10830 return;
10831 }
10832
10833 if (self.isNode() && !self.removed()) {
10834 var degree = 0;
10835 var node = self[0];
10836 var connectedEdges = node._private.edges;
10837
10838 for (var i = 0; i < connectedEdges.length; i++) {
10839 var edge = connectedEdges[i];
10840
10841 if (!includeLoops && edge.isLoop()) {
10842 continue;
10843 }
10844
10845 degree += callback(node, edge);
10846 }
10847
10848 return degree;
10849 } else {
10850 return;
10851 }
10852 };
10853 }
10854
10855 extend(elesfn$d, {
10856 degree: defineDegreeFunction(function (node, edge) {
10857 if (edge.source().same(edge.target())) {
10858 return 2;
10859 } else {
10860 return 1;
10861 }
10862 }),
10863 indegree: defineDegreeFunction(function (node, edge) {
10864 if (edge.target().same(node)) {
10865 return 1;
10866 } else {
10867 return 0;
10868 }
10869 }),
10870 outdegree: defineDegreeFunction(function (node, edge) {
10871 if (edge.source().same(node)) {
10872 return 1;
10873 } else {
10874 return 0;
10875 }
10876 })
10877 });
10878
10879 function defineDegreeBoundsFunction(degreeFn, callback) {
10880 return function (includeLoops) {
10881 var ret;
10882 var nodes = this.nodes();
10883
10884 for (var i = 0; i < nodes.length; i++) {
10885 var ele = nodes[i];
10886 var degree = ele[degreeFn](includeLoops);
10887
10888 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
10889 ret = degree;
10890 }
10891 }
10892
10893 return ret;
10894 };
10895 }
10896
10897 extend(elesfn$d, {
10898 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
10899 return degree < min;
10900 }),
10901 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
10902 return degree > max;
10903 }),
10904 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
10905 return degree < min;
10906 }),
10907 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
10908 return degree > max;
10909 }),
10910 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
10911 return degree < min;
10912 }),
10913 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
10914 return degree > max;
10915 })
10916 });
10917 extend(elesfn$d, {
10918 totalDegree: function totalDegree(includeLoops) {
10919 var total = 0;
10920 var nodes = this.nodes();
10921
10922 for (var i = 0; i < nodes.length; i++) {
10923 total += nodes[i].degree(includeLoops);
10924 }
10925
10926 return total;
10927 }
10928 });
10929
10930 var fn$4, elesfn$c;
10931
10932 var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
10933 for (var i = 0; i < eles.length; i++) {
10934 var ele = eles[i];
10935
10936 if (!ele.locked()) {
10937 var oldPos = ele._private.position;
10938 var delta = {
10939 x: newPos.x != null ? newPos.x - oldPos.x : 0,
10940 y: newPos.y != null ? newPos.y - oldPos.y : 0
10941 };
10942
10943 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
10944 ele.children().shift(delta, silent);
10945 }
10946
10947 ele.dirtyBoundingBoxCache();
10948 }
10949 }
10950 };
10951
10952 var positionDef = {
10953 field: 'position',
10954 bindingEvent: 'position',
10955 allowBinding: true,
10956 allowSetting: true,
10957 settingEvent: 'position',
10958 settingTriggersEvent: true,
10959 triggerFnName: 'emitAndNotify',
10960 allowGetting: true,
10961 validKeys: ['x', 'y'],
10962 beforeGet: function beforeGet(ele) {
10963 ele.updateCompoundBounds();
10964 },
10965 beforeSet: function beforeSet(eles, newPos) {
10966 beforePositionSet(eles, newPos, false);
10967 },
10968 onSet: function onSet(eles) {
10969 eles.dirtyCompoundBoundsCache();
10970 },
10971 canSet: function canSet(ele) {
10972 return !ele.locked();
10973 }
10974 };
10975 fn$4 = elesfn$c = {
10976 position: define.data(positionDef),
10977 // position but no notification to renderer
10978 silentPosition: define.data(extend({}, positionDef, {
10979 allowBinding: false,
10980 allowSetting: true,
10981 settingTriggersEvent: false,
10982 allowGetting: false,
10983 beforeSet: function beforeSet(eles, newPos) {
10984 beforePositionSet(eles, newPos, true);
10985 },
10986 onSet: function onSet(eles) {
10987 eles.dirtyCompoundBoundsCache();
10988 }
10989 })),
10990 positions: function positions(pos, silent) {
10991 if (plainObject(pos)) {
10992 if (silent) {
10993 this.silentPosition(pos);
10994 } else {
10995 this.position(pos);
10996 }
10997 } else if (fn$6(pos)) {
10998 var _fn = pos;
10999 var cy = this.cy();
11000 cy.startBatch();
11001
11002 for (var i = 0; i < this.length; i++) {
11003 var ele = this[i];
11004
11005 var _pos = void 0;
11006
11007 if (_pos = _fn(ele, i)) {
11008 if (silent) {
11009 ele.silentPosition(_pos);
11010 } else {
11011 ele.position(_pos);
11012 }
11013 }
11014 }
11015
11016 cy.endBatch();
11017 }
11018
11019 return this; // chaining
11020 },
11021 silentPositions: function silentPositions(pos) {
11022 return this.positions(pos, true);
11023 },
11024 shift: function shift(dim, val, silent) {
11025 var delta;
11026
11027 if (plainObject(dim)) {
11028 delta = {
11029 x: number$1(dim.x) ? dim.x : 0,
11030 y: number$1(dim.y) ? dim.y : 0
11031 };
11032 silent = val;
11033 } else if (string(dim) && number$1(val)) {
11034 delta = {
11035 x: 0,
11036 y: 0
11037 };
11038 delta[dim] = val;
11039 }
11040
11041 if (delta != null) {
11042 var cy = this.cy();
11043 cy.startBatch();
11044
11045 for (var i = 0; i < this.length; i++) {
11046 var ele = this[i]; // exclude any node that is a descendant of the calling collection
11047
11048 if (cy.hasCompoundNodes() && ele.isChild() && ele.ancestors().anySame(this)) {
11049 continue;
11050 }
11051
11052 var pos = ele.position();
11053 var newPos = {
11054 x: pos.x + delta.x,
11055 y: pos.y + delta.y
11056 };
11057
11058 if (silent) {
11059 ele.silentPosition(newPos);
11060 } else {
11061 ele.position(newPos);
11062 }
11063 }
11064
11065 cy.endBatch();
11066 }
11067
11068 return this;
11069 },
11070 silentShift: function silentShift(dim, val) {
11071 if (plainObject(dim)) {
11072 this.shift(dim, true);
11073 } else if (string(dim) && number$1(val)) {
11074 this.shift(dim, val, true);
11075 }
11076
11077 return this;
11078 },
11079 // get/set the rendered (i.e. on screen) positon of the element
11080 renderedPosition: function renderedPosition(dim, val) {
11081 var ele = this[0];
11082 var cy = this.cy();
11083 var zoom = cy.zoom();
11084 var pan = cy.pan();
11085 var rpos = plainObject(dim) ? dim : undefined;
11086 var setting = rpos !== undefined || val !== undefined && string(dim);
11087
11088 if (ele && ele.isNode()) {
11089 // must have an element and must be a node to return position
11090 if (setting) {
11091 for (var i = 0; i < this.length; i++) {
11092 var _ele = this[i];
11093
11094 if (val !== undefined) {
11095 // set one dimension
11096 _ele.position(dim, (val - pan[dim]) / zoom);
11097 } else if (rpos !== undefined) {
11098 // set whole position
11099 _ele.position(renderedToModelPosition(rpos, zoom, pan));
11100 }
11101 }
11102 } else {
11103 // getting
11104 var pos = ele.position();
11105 rpos = modelToRenderedPosition(pos, zoom, pan);
11106
11107 if (dim === undefined) {
11108 // then return the whole rendered position
11109 return rpos;
11110 } else {
11111 // then return the specified dimension
11112 return rpos[dim];
11113 }
11114 }
11115 } else if (!setting) {
11116 return undefined; // for empty collection case
11117 }
11118
11119 return this; // chaining
11120 },
11121 // get/set the position relative to the parent
11122 relativePosition: function relativePosition(dim, val) {
11123 var ele = this[0];
11124 var cy = this.cy();
11125 var ppos = plainObject(dim) ? dim : undefined;
11126 var setting = ppos !== undefined || val !== undefined && string(dim);
11127 var hasCompoundNodes = cy.hasCompoundNodes();
11128
11129 if (ele && ele.isNode()) {
11130 // must have an element and must be a node to return position
11131 if (setting) {
11132 for (var i = 0; i < this.length; i++) {
11133 var _ele2 = this[i];
11134 var parent = hasCompoundNodes ? _ele2.parent() : null;
11135 var hasParent = parent && parent.length > 0;
11136 var relativeToParent = hasParent;
11137
11138 if (hasParent) {
11139 parent = parent[0];
11140 }
11141
11142 var origin = relativeToParent ? parent.position() : {
11143 x: 0,
11144 y: 0
11145 };
11146
11147 if (val !== undefined) {
11148 // set one dimension
11149 _ele2.position(dim, val + origin[dim]);
11150 } else if (ppos !== undefined) {
11151 // set whole position
11152 _ele2.position({
11153 x: ppos.x + origin.x,
11154 y: ppos.y + origin.y
11155 });
11156 }
11157 }
11158 } else {
11159 // getting
11160 var pos = ele.position();
11161
11162 var _parent = hasCompoundNodes ? ele.parent() : null;
11163
11164 var _hasParent = _parent && _parent.length > 0;
11165
11166 var _relativeToParent = _hasParent;
11167
11168 if (_hasParent) {
11169 _parent = _parent[0];
11170 }
11171
11172 var _origin = _relativeToParent ? _parent.position() : {
11173 x: 0,
11174 y: 0
11175 };
11176
11177 ppos = {
11178 x: pos.x - _origin.x,
11179 y: pos.y - _origin.y
11180 };
11181
11182 if (dim === undefined) {
11183 // then return the whole rendered position
11184 return ppos;
11185 } else {
11186 // then return the specified dimension
11187 return ppos[dim];
11188 }
11189 }
11190 } else if (!setting) {
11191 return undefined; // for empty collection case
11192 }
11193
11194 return this; // chaining
11195 }
11196 }; // aliases
11197
11198 fn$4.modelPosition = fn$4.point = fn$4.position;
11199 fn$4.modelPositions = fn$4.points = fn$4.positions;
11200 fn$4.renderedPoint = fn$4.renderedPosition;
11201 fn$4.relativePoint = fn$4.relativePosition;
11202 var position = elesfn$c;
11203
11204 var fn$3, elesfn$b;
11205 fn$3 = elesfn$b = {};
11206
11207 elesfn$b.renderedBoundingBox = function (options) {
11208 var bb = this.boundingBox(options);
11209 var cy = this.cy();
11210 var zoom = cy.zoom();
11211 var pan = cy.pan();
11212 var x1 = bb.x1 * zoom + pan.x;
11213 var x2 = bb.x2 * zoom + pan.x;
11214 var y1 = bb.y1 * zoom + pan.y;
11215 var y2 = bb.y2 * zoom + pan.y;
11216 return {
11217 x1: x1,
11218 x2: x2,
11219 y1: y1,
11220 y2: y2,
11221 w: x2 - x1,
11222 h: y2 - y1
11223 };
11224 };
11225
11226 elesfn$b.dirtyCompoundBoundsCache = function () {
11227 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
11228 var cy = this.cy();
11229
11230 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
11231 return this;
11232 }
11233
11234 this.forEachUp(function (ele) {
11235 if (ele.isParent()) {
11236 var _p = ele._private;
11237 _p.compoundBoundsClean = false;
11238 _p.bbCache = null;
11239
11240 if (!silent) {
11241 ele.emitAndNotify('bounds');
11242 }
11243 }
11244 });
11245 return this;
11246 };
11247
11248 elesfn$b.updateCompoundBounds = function () {
11249 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
11250 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
11251
11252 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
11253 return this;
11254 } // save cycles when batching -- but bounds will be stale (or not exist yet)
11255
11256
11257 if (!force && cy.batching()) {
11258 return this;
11259 }
11260
11261 function update(parent) {
11262 if (!parent.isParent()) {
11263 return;
11264 }
11265
11266 var _p = parent._private;
11267 var children = parent.children();
11268 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
11269 var min = {
11270 width: {
11271 val: parent.pstyle('min-width').pfValue,
11272 left: parent.pstyle('min-width-bias-left'),
11273 right: parent.pstyle('min-width-bias-right')
11274 },
11275 height: {
11276 val: parent.pstyle('min-height').pfValue,
11277 top: parent.pstyle('min-height-bias-top'),
11278 bottom: parent.pstyle('min-height-bias-bottom')
11279 }
11280 };
11281 var bb = children.boundingBox({
11282 includeLabels: includeLabels,
11283 includeOverlays: false,
11284 // updating the compound bounds happens outside of the regular
11285 // cache cycle (i.e. before fired events)
11286 useCache: false
11287 });
11288 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
11289
11290 if (bb.w === 0 || bb.h === 0) {
11291 bb = {
11292 w: parent.pstyle('width').pfValue,
11293 h: parent.pstyle('height').pfValue
11294 };
11295 bb.x1 = pos.x - bb.w / 2;
11296 bb.x2 = pos.x + bb.w / 2;
11297 bb.y1 = pos.y - bb.h / 2;
11298 bb.y2 = pos.y + bb.h / 2;
11299 }
11300
11301 function computeBiasValues(propDiff, propBias, propBiasComplement) {
11302 var biasDiff = 0;
11303 var biasComplementDiff = 0;
11304 var biasTotal = propBias + propBiasComplement;
11305
11306 if (propDiff > 0 && biasTotal > 0) {
11307 biasDiff = propBias / biasTotal * propDiff;
11308 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
11309 }
11310
11311 return {
11312 biasDiff: biasDiff,
11313 biasComplementDiff: biasComplementDiff
11314 };
11315 }
11316
11317 function computePaddingValues(width, height, paddingObject, relativeTo) {
11318 // Assuming percentage is number from 0 to 1
11319 if (paddingObject.units === '%') {
11320 switch (relativeTo) {
11321 case 'width':
11322 return width > 0 ? paddingObject.pfValue * width : 0;
11323
11324 case 'height':
11325 return height > 0 ? paddingObject.pfValue * height : 0;
11326
11327 case 'average':
11328 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
11329
11330 case 'min':
11331 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
11332
11333 case 'max':
11334 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
11335
11336 default:
11337 return 0;
11338 }
11339 } else if (paddingObject.units === 'px') {
11340 return paddingObject.pfValue;
11341 } else {
11342 return 0;
11343 }
11344 }
11345
11346 var leftVal = min.width.left.value;
11347
11348 if (min.width.left.units === 'px' && min.width.val > 0) {
11349 leftVal = leftVal * 100 / min.width.val;
11350 }
11351
11352 var rightVal = min.width.right.value;
11353
11354 if (min.width.right.units === 'px' && min.width.val > 0) {
11355 rightVal = rightVal * 100 / min.width.val;
11356 }
11357
11358 var topVal = min.height.top.value;
11359
11360 if (min.height.top.units === 'px' && min.height.val > 0) {
11361 topVal = topVal * 100 / min.height.val;
11362 }
11363
11364 var bottomVal = min.height.bottom.value;
11365
11366 if (min.height.bottom.units === 'px' && min.height.val > 0) {
11367 bottomVal = bottomVal * 100 / min.height.val;
11368 }
11369
11370 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
11371 var diffLeft = widthBiasDiffs.biasDiff;
11372 var diffRight = widthBiasDiffs.biasComplementDiff;
11373 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
11374 var diffTop = heightBiasDiffs.biasDiff;
11375 var diffBottom = heightBiasDiffs.biasComplementDiff;
11376 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
11377 _p.autoWidth = Math.max(bb.w, min.width.val);
11378 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
11379 _p.autoHeight = Math.max(bb.h, min.height.val);
11380 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
11381 }
11382
11383 for (var i = 0; i < this.length; i++) {
11384 var ele = this[i];
11385 var _p = ele._private;
11386
11387 if (!_p.compoundBoundsClean || force) {
11388 update(ele);
11389
11390 if (!cy.batching()) {
11391 _p.compoundBoundsClean = true;
11392 }
11393 }
11394 }
11395
11396 return this;
11397 };
11398
11399 var noninf = function noninf(x) {
11400 if (x === Infinity || x === -Infinity) {
11401 return 0;
11402 }
11403
11404 return x;
11405 };
11406
11407 var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
11408 // don't update with zero area boxes
11409 if (x2 - x1 === 0 || y2 - y1 === 0) {
11410 return;
11411 } // don't update with null dim
11412
11413
11414 if (x1 == null || y1 == null || x2 == null || y2 == null) {
11415 return;
11416 }
11417
11418 b.x1 = x1 < b.x1 ? x1 : b.x1;
11419 b.x2 = x2 > b.x2 ? x2 : b.x2;
11420 b.y1 = y1 < b.y1 ? y1 : b.y1;
11421 b.y2 = y2 > b.y2 ? y2 : b.y2;
11422 b.w = b.x2 - b.x1;
11423 b.h = b.y2 - b.y1;
11424 };
11425
11426 var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
11427 if (b2 == null) {
11428 return b;
11429 }
11430
11431 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
11432 };
11433
11434 var prefixedProperty = function prefixedProperty(obj, field, prefix) {
11435 return getPrefixedProperty(obj, field, prefix);
11436 };
11437
11438 var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
11439 if (ele.cy().headless()) {
11440 return;
11441 }
11442
11443 var _p = ele._private;
11444 var rstyle = _p.rstyle;
11445 var halfArW = rstyle.arrowWidth / 2;
11446 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
11447 var x;
11448 var y;
11449
11450 if (arrowType !== 'none') {
11451 if (prefix === 'source') {
11452 x = rstyle.srcX;
11453 y = rstyle.srcY;
11454 } else if (prefix === 'target') {
11455 x = rstyle.tgtX;
11456 y = rstyle.tgtY;
11457 } else {
11458 x = rstyle.midX;
11459 y = rstyle.midY;
11460 } // always store the individual arrow bounds
11461
11462
11463 var bbs = _p.arrowBounds = _p.arrowBounds || {};
11464 var bb = bbs[prefix] = bbs[prefix] || {};
11465 bb.x1 = x - halfArW;
11466 bb.y1 = y - halfArW;
11467 bb.x2 = x + halfArW;
11468 bb.y2 = y + halfArW;
11469 bb.w = bb.x2 - bb.x1;
11470 bb.h = bb.y2 - bb.y1;
11471 expandBoundingBox(bb, 1);
11472 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
11473 }
11474 };
11475
11476 var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
11477 if (ele.cy().headless()) {
11478 return;
11479 }
11480
11481 var prefixDash;
11482
11483 if (prefix) {
11484 prefixDash = prefix + '-';
11485 } else {
11486 prefixDash = '';
11487 }
11488
11489 var _p = ele._private;
11490 var rstyle = _p.rstyle;
11491 var label = ele.pstyle(prefixDash + 'label').strValue;
11492
11493 if (label) {
11494 var halign = ele.pstyle('text-halign');
11495 var valign = ele.pstyle('text-valign');
11496 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
11497 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
11498 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
11499 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
11500 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
11501 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
11502 var isEdge = ele.isEdge();
11503 var rotation = ele.pstyle(prefixDash + 'text-rotation');
11504 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
11505 var borderWidth = ele.pstyle('text-border-width').pfValue;
11506 var halfBorderWidth = borderWidth / 2;
11507 var padding = ele.pstyle('text-background-padding').pfValue;
11508 var marginOfError = 2; // expand to work around browser dimension inaccuracies
11509
11510 var lh = labelHeight;
11511 var lw = labelWidth;
11512 var lw_2 = lw / 2;
11513 var lh_2 = lh / 2;
11514 var lx1, lx2, ly1, ly2;
11515
11516 if (isEdge) {
11517 lx1 = labelX - lw_2;
11518 lx2 = labelX + lw_2;
11519 ly1 = labelY - lh_2;
11520 ly2 = labelY + lh_2;
11521 } else {
11522 switch (halign.value) {
11523 case 'left':
11524 lx1 = labelX - lw;
11525 lx2 = labelX;
11526 break;
11527
11528 case 'center':
11529 lx1 = labelX - lw_2;
11530 lx2 = labelX + lw_2;
11531 break;
11532
11533 case 'right':
11534 lx1 = labelX;
11535 lx2 = labelX + lw;
11536 break;
11537 }
11538
11539 switch (valign.value) {
11540 case 'top':
11541 ly1 = labelY - lh;
11542 ly2 = labelY;
11543 break;
11544
11545 case 'center':
11546 ly1 = labelY - lh_2;
11547 ly2 = labelY + lh_2;
11548 break;
11549
11550 case 'bottom':
11551 ly1 = labelY;
11552 ly2 = labelY + lh;
11553 break;
11554 }
11555 } // shift by margin and expand by outline and border
11556
11557
11558 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
11559 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
11560 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
11561 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
11562
11563 var bbPrefix = prefix || 'main';
11564 var bbs = _p.labelBounds;
11565 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
11566 bb.x1 = lx1;
11567 bb.y1 = ly1;
11568 bb.x2 = lx2;
11569 bb.y2 = ly2;
11570 bb.w = lx2 - lx1;
11571 bb.h = ly2 - ly1;
11572 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
11573 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
11574
11575 if (isAutorotate || isPfValue) {
11576 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
11577 var cos = Math.cos(theta);
11578 var sin = Math.sin(theta); // rotation point (default value for center-center)
11579
11580 var xo = (lx1 + lx2) / 2;
11581 var yo = (ly1 + ly2) / 2;
11582
11583 if (!isEdge) {
11584 switch (halign.value) {
11585 case 'left':
11586 xo = lx2;
11587 break;
11588
11589 case 'right':
11590 xo = lx1;
11591 break;
11592 }
11593
11594 switch (valign.value) {
11595 case 'top':
11596 yo = ly2;
11597 break;
11598
11599 case 'bottom':
11600 yo = ly1;
11601 break;
11602 }
11603 }
11604
11605 var rotate = function rotate(x, y) {
11606 x = x - xo;
11607 y = y - yo;
11608 return {
11609 x: x * cos - y * sin + xo,
11610 y: x * sin + y * cos + yo
11611 };
11612 };
11613
11614 var px1y1 = rotate(lx1, ly1);
11615 var px1y2 = rotate(lx1, ly2);
11616 var px2y1 = rotate(lx2, ly1);
11617 var px2y2 = rotate(lx2, ly2);
11618 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
11619 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
11620 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
11621 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
11622 }
11623
11624 var bbPrefixRot = bbPrefix + 'Rot';
11625 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
11626 bbRot.x1 = lx1;
11627 bbRot.y1 = ly1;
11628 bbRot.x2 = lx2;
11629 bbRot.y2 = ly2;
11630 bbRot.w = lx2 - lx1;
11631 bbRot.h = ly2 - ly1;
11632 updateBounds(bounds, lx1, ly1, lx2, ly2);
11633 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
11634 }
11635
11636 return bounds;
11637 }; // get the bounding box of the elements (in raw model position)
11638
11639
11640 var boundingBoxImpl = function boundingBoxImpl(ele, options) {
11641 var cy = ele._private.cy;
11642 var styleEnabled = cy.styleEnabled();
11643 var headless = cy.headless();
11644 var bounds = makeBoundingBox();
11645 var _p = ele._private;
11646 var isNode = ele.isNode();
11647 var isEdge = ele.isEdge();
11648 var ex1, ex2, ey1, ey2; // extrema of body / lines
11649
11650 var x, y; // node pos
11651
11652 var rstyle = _p.rstyle;
11653 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
11654 // (other factors like width values will be considered later in this function anyway)
11655
11656 var isDisplayed = function isDisplayed(ele) {
11657 return ele.pstyle('display').value !== 'none';
11658 };
11659
11660 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
11661 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
11662
11663 if (displayed) {
11664 // displayed suffices, since we will find zero area eles anyway
11665 var overlayOpacity = 0;
11666 var overlayPadding = 0;
11667
11668 if (styleEnabled && options.includeOverlays) {
11669 overlayOpacity = ele.pstyle('overlay-opacity').value;
11670
11671 if (overlayOpacity !== 0) {
11672 overlayPadding = ele.pstyle('overlay-padding').value;
11673 }
11674 }
11675
11676 var underlayOpacity = 0;
11677 var underlayPadding = 0;
11678
11679 if (styleEnabled && options.includeUnderlays) {
11680 underlayOpacity = ele.pstyle('underlay-opacity').value;
11681
11682 if (underlayOpacity !== 0) {
11683 underlayPadding = ele.pstyle('underlay-padding').value;
11684 }
11685 }
11686
11687 var padding = Math.max(overlayPadding, underlayPadding);
11688 var w = 0;
11689 var wHalf = 0;
11690
11691 if (styleEnabled) {
11692 w = ele.pstyle('width').pfValue;
11693 wHalf = w / 2;
11694 }
11695
11696 if (isNode && options.includeNodes) {
11697 var pos = ele.position();
11698 x = pos.x;
11699 y = pos.y;
11700
11701 var _w = ele.outerWidth();
11702
11703 var halfW = _w / 2;
11704 var h = ele.outerHeight();
11705 var halfH = h / 2; // handle node dimensions
11706 /////////////////////////
11707
11708 ex1 = x - halfW;
11709 ex2 = x + halfW;
11710 ey1 = y - halfH;
11711 ey2 = y + halfH;
11712 updateBounds(bounds, ex1, ey1, ex2, ey2);
11713 } else if (isEdge && options.includeEdges) {
11714 if (styleEnabled && !headless) {
11715 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
11716 //////////////////////////////////////////////
11717
11718 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
11719 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
11720 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
11721 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
11722
11723 ex1 -= wHalf;
11724 ex2 += wHalf;
11725 ey1 -= wHalf;
11726 ey2 += wHalf;
11727 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
11728 ////////////////
11729
11730 if (curveStyle === 'haystack') {
11731 var hpts = rstyle.haystackPts;
11732
11733 if (hpts && hpts.length === 2) {
11734 ex1 = hpts[0].x;
11735 ey1 = hpts[0].y;
11736 ex2 = hpts[1].x;
11737 ey2 = hpts[1].y;
11738
11739 if (ex1 > ex2) {
11740 var temp = ex1;
11741 ex1 = ex2;
11742 ex2 = temp;
11743 }
11744
11745 if (ey1 > ey2) {
11746 var _temp = ey1;
11747 ey1 = ey2;
11748 ey2 = _temp;
11749 }
11750
11751 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
11752 }
11753 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
11754 var pts;
11755
11756 switch (curveStyle) {
11757 case 'bezier':
11758 case 'unbundled-bezier':
11759 pts = rstyle.bezierPts;
11760 break;
11761
11762 case 'segments':
11763 case 'taxi':
11764 pts = rstyle.linePts;
11765 break;
11766 }
11767
11768 if (pts != null) {
11769 for (var j = 0; j < pts.length; j++) {
11770 var pt = pts[j];
11771 ex1 = pt.x - wHalf;
11772 ex2 = pt.x + wHalf;
11773 ey1 = pt.y - wHalf;
11774 ey2 = pt.y + wHalf;
11775 updateBounds(bounds, ex1, ey1, ex2, ey2);
11776 }
11777 }
11778 } // bezier-like or segment-like edge
11779
11780 } else {
11781 // headless or style disabled
11782 // fallback on source and target positions
11783 //////////////////////////////////////////
11784 var n1 = ele.source();
11785 var n1pos = n1.position();
11786 var n2 = ele.target();
11787 var n2pos = n2.position();
11788 ex1 = n1pos.x;
11789 ex2 = n2pos.x;
11790 ey1 = n1pos.y;
11791 ey2 = n2pos.y;
11792
11793 if (ex1 > ex2) {
11794 var _temp2 = ex1;
11795 ex1 = ex2;
11796 ex2 = _temp2;
11797 }
11798
11799 if (ey1 > ey2) {
11800 var _temp3 = ey1;
11801 ey1 = ey2;
11802 ey2 = _temp3;
11803 } // take into account edge width
11804
11805
11806 ex1 -= wHalf;
11807 ex2 += wHalf;
11808 ey1 -= wHalf;
11809 ey2 += wHalf;
11810 updateBounds(bounds, ex1, ey1, ex2, ey2);
11811 } // headless or style disabled
11812
11813 } // edges
11814 // handle edge arrow size
11815 /////////////////////////
11816
11817
11818 if (styleEnabled && options.includeEdges && isEdge) {
11819 updateBoundsFromArrow(bounds, ele, 'mid-source');
11820 updateBoundsFromArrow(bounds, ele, 'mid-target');
11821 updateBoundsFromArrow(bounds, ele, 'source');
11822 updateBoundsFromArrow(bounds, ele, 'target');
11823 } // ghost
11824 ////////
11825
11826
11827 if (styleEnabled) {
11828 var ghost = ele.pstyle('ghost').value === 'yes';
11829
11830 if (ghost) {
11831 var gx = ele.pstyle('ghost-offset-x').pfValue;
11832 var gy = ele.pstyle('ghost-offset-y').pfValue;
11833 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
11834 }
11835 } // always store the body bounds separately from the labels
11836
11837
11838 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
11839 assignBoundingBox(bbBody, bounds);
11840 expandBoundingBoxSides(bbBody, manualExpansion);
11841 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
11842 // overlay
11843 //////////
11844
11845 if (styleEnabled) {
11846 ex1 = bounds.x1;
11847 ex2 = bounds.x2;
11848 ey1 = bounds.y1;
11849 ey2 = bounds.y2;
11850 updateBounds(bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding);
11851 } // always store the body bounds separately from the labels
11852
11853
11854 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
11855 assignBoundingBox(bbOverlay, bounds);
11856 expandBoundingBoxSides(bbOverlay, manualExpansion);
11857 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
11858 // handle label dimensions
11859 //////////////////////////
11860
11861 var bbLabels = _p.labelBounds = _p.labelBounds || {};
11862
11863 if (bbLabels.all != null) {
11864 clearBoundingBox(bbLabels.all);
11865 } else {
11866 bbLabels.all = makeBoundingBox();
11867 }
11868
11869 if (styleEnabled && options.includeLabels) {
11870 if (options.includeMainLabels) {
11871 updateBoundsFromLabel(bounds, ele, null);
11872 }
11873
11874 if (isEdge) {
11875 if (options.includeSourceLabels) {
11876 updateBoundsFromLabel(bounds, ele, 'source');
11877 }
11878
11879 if (options.includeTargetLabels) {
11880 updateBoundsFromLabel(bounds, ele, 'target');
11881 }
11882 }
11883 } // style enabled for labels
11884
11885 } // if displayed
11886
11887
11888 bounds.x1 = noninf(bounds.x1);
11889 bounds.y1 = noninf(bounds.y1);
11890 bounds.x2 = noninf(bounds.x2);
11891 bounds.y2 = noninf(bounds.y2);
11892 bounds.w = noninf(bounds.x2 - bounds.x1);
11893 bounds.h = noninf(bounds.y2 - bounds.y1);
11894
11895 if (bounds.w > 0 && bounds.h > 0 && displayed) {
11896 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
11897
11898 expandBoundingBox(bounds, 1);
11899 }
11900
11901 return bounds;
11902 };
11903
11904 var getKey = function getKey(opts) {
11905 var i = 0;
11906
11907 var tf = function tf(val) {
11908 return (val ? 1 : 0) << i++;
11909 };
11910
11911 var key = 0;
11912 key += tf(opts.incudeNodes);
11913 key += tf(opts.includeEdges);
11914 key += tf(opts.includeLabels);
11915 key += tf(opts.includeMainLabels);
11916 key += tf(opts.includeSourceLabels);
11917 key += tf(opts.includeTargetLabels);
11918 key += tf(opts.includeOverlays);
11919 return key;
11920 };
11921
11922 var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
11923 if (ele.isEdge()) {
11924 var p1 = ele.source().position();
11925 var p2 = ele.target().position();
11926
11927 var r = function r(x) {
11928 return Math.round(x);
11929 };
11930
11931 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
11932 } else {
11933 return 0;
11934 }
11935 };
11936
11937 var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
11938 var _p = ele._private;
11939 var bb;
11940 var isEdge = ele.isEdge();
11941 var key = opts == null ? defBbOptsKey : getKey(opts);
11942 var usingDefOpts = key === defBbOptsKey;
11943 var currPosKey = getBoundingBoxPosKey(ele);
11944 var isPosKeySame = _p.bbCachePosKey === currPosKey;
11945 var useCache = opts.useCache && isPosKeySame;
11946
11947 var isDirty = function isDirty(ele) {
11948 return ele._private.bbCache == null || ele._private.styleDirty;
11949 };
11950
11951 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
11952
11953 if (needRecalc) {
11954 if (!isPosKeySame) {
11955 ele.recalculateRenderedStyle(useCache);
11956 }
11957
11958 bb = boundingBoxImpl(ele, defBbOpts);
11959 _p.bbCache = bb;
11960 _p.bbCachePosKey = currPosKey;
11961 } else {
11962 bb = _p.bbCache;
11963 } // not using def opts => need to build up bb from combination of sub bbs
11964
11965
11966 if (!usingDefOpts) {
11967 var isNode = ele.isNode();
11968 bb = makeBoundingBox();
11969
11970 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
11971 if (opts.includeOverlays) {
11972 updateBoundsFromBox(bb, _p.overlayBounds);
11973 } else {
11974 updateBoundsFromBox(bb, _p.bodyBounds);
11975 }
11976 }
11977
11978 if (opts.includeLabels) {
11979 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
11980 updateBoundsFromBox(bb, _p.labelBounds.all);
11981 } else {
11982 if (opts.includeMainLabels) {
11983 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
11984 }
11985
11986 if (opts.includeSourceLabels) {
11987 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
11988 }
11989
11990 if (opts.includeTargetLabels) {
11991 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
11992 }
11993 }
11994 }
11995
11996 bb.w = bb.x2 - bb.x1;
11997 bb.h = bb.y2 - bb.y1;
11998 }
11999
12000 return bb;
12001 };
12002
12003 var defBbOpts = {
12004 includeNodes: true,
12005 includeEdges: true,
12006 includeLabels: true,
12007 includeMainLabels: true,
12008 includeSourceLabels: true,
12009 includeTargetLabels: true,
12010 includeOverlays: true,
12011 includeUnderlays: true,
12012 useCache: true
12013 };
12014 var defBbOptsKey = getKey(defBbOpts);
12015 var filledBbOpts = defaults$g(defBbOpts);
12016
12017 elesfn$b.boundingBox = function (options) {
12018 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
12019 // specified s.t. the cache is used, so check for this case to make it faster by
12020 // avoiding the overhead of the rest of the function
12021
12022 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
12023 if (options === undefined) {
12024 options = defBbOpts;
12025 } else {
12026 options = filledBbOpts(options);
12027 }
12028
12029 bounds = cachedBoundingBoxImpl(this[0], options);
12030 } else {
12031 bounds = makeBoundingBox();
12032 options = options || defBbOpts;
12033 var opts = filledBbOpts(options);
12034 var eles = this;
12035 var cy = eles.cy();
12036 var styleEnabled = cy.styleEnabled();
12037
12038 if (styleEnabled) {
12039 for (var i = 0; i < eles.length; i++) {
12040 var ele = eles[i];
12041 var _p = ele._private;
12042 var currPosKey = getBoundingBoxPosKey(ele);
12043 var isPosKeySame = _p.bbCachePosKey === currPosKey;
12044 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
12045 ele.recalculateRenderedStyle(useCache);
12046 }
12047 }
12048
12049 this.updateCompoundBounds(!options.useCache);
12050
12051 for (var _i = 0; _i < eles.length; _i++) {
12052 var _ele = eles[_i];
12053 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
12054 }
12055 }
12056
12057 bounds.x1 = noninf(bounds.x1);
12058 bounds.y1 = noninf(bounds.y1);
12059 bounds.x2 = noninf(bounds.x2);
12060 bounds.y2 = noninf(bounds.y2);
12061 bounds.w = noninf(bounds.x2 - bounds.x1);
12062 bounds.h = noninf(bounds.y2 - bounds.y1);
12063 return bounds;
12064 };
12065
12066 elesfn$b.dirtyBoundingBoxCache = function () {
12067 for (var i = 0; i < this.length; i++) {
12068 var _p = this[i]._private;
12069 _p.bbCache = null;
12070 _p.bbCachePosKey = null;
12071 _p.bodyBounds = null;
12072 _p.overlayBounds = null;
12073 _p.labelBounds.all = null;
12074 _p.labelBounds.source = null;
12075 _p.labelBounds.target = null;
12076 _p.labelBounds.main = null;
12077 _p.labelBounds.sourceRot = null;
12078 _p.labelBounds.targetRot = null;
12079 _p.labelBounds.mainRot = null;
12080 _p.arrowBounds.source = null;
12081 _p.arrowBounds.target = null;
12082 _p.arrowBounds['mid-source'] = null;
12083 _p.arrowBounds['mid-target'] = null;
12084 }
12085
12086 this.emitAndNotify('bounds');
12087 return this;
12088 }; // private helper to get bounding box for custom node positions
12089 // - good for perf in certain cases but currently requires dirtying the rendered style
12090 // - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
12091 // - try to use for only things like discrete layouts where the node position would change anyway
12092
12093
12094 elesfn$b.boundingBoxAt = function (fn) {
12095 var nodes = this.nodes();
12096 var cy = this.cy();
12097 var hasCompoundNodes = cy.hasCompoundNodes();
12098 var parents = cy.collection();
12099
12100 if (hasCompoundNodes) {
12101 parents = nodes.filter(function (node) {
12102 return node.isParent();
12103 });
12104 nodes = nodes.not(parents);
12105 }
12106
12107 if (plainObject(fn)) {
12108 var obj = fn;
12109
12110 fn = function fn() {
12111 return obj;
12112 };
12113 }
12114
12115 var storeOldPos = function storeOldPos(node, i) {
12116 return node._private.bbAtOldPos = fn(node, i);
12117 };
12118
12119 var getOldPos = function getOldPos(node) {
12120 return node._private.bbAtOldPos;
12121 };
12122
12123 cy.startBatch();
12124 nodes.forEach(storeOldPos).silentPositions(fn);
12125
12126 if (hasCompoundNodes) {
12127 parents.dirtyCompoundBoundsCache();
12128 parents.dirtyBoundingBoxCache();
12129 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
12130 }
12131
12132 var bb = copyBoundingBox(this.boundingBox({
12133 useCache: false
12134 }));
12135 nodes.silentPositions(getOldPos);
12136
12137 if (hasCompoundNodes) {
12138 parents.dirtyCompoundBoundsCache();
12139 parents.dirtyBoundingBoxCache();
12140 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
12141 }
12142
12143 cy.endBatch();
12144 return bb;
12145 };
12146
12147 fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
12148 fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
12149 var bounds = elesfn$b;
12150
12151 var fn$2, elesfn$a;
12152 fn$2 = elesfn$a = {};
12153
12154 var defineDimFns = function defineDimFns(opts) {
12155 opts.uppercaseName = capitalize(opts.name);
12156 opts.autoName = 'auto' + opts.uppercaseName;
12157 opts.labelName = 'label' + opts.uppercaseName;
12158 opts.outerName = 'outer' + opts.uppercaseName;
12159 opts.uppercaseOuterName = capitalize(opts.outerName);
12160
12161 fn$2[opts.name] = function dimImpl() {
12162 var ele = this[0];
12163 var _p = ele._private;
12164 var cy = _p.cy;
12165 var styleEnabled = cy._private.styleEnabled;
12166
12167 if (ele) {
12168 if (styleEnabled) {
12169 if (ele.isParent()) {
12170 ele.updateCompoundBounds();
12171 return _p[opts.autoName] || 0;
12172 }
12173
12174 var d = ele.pstyle(opts.name);
12175
12176 switch (d.strValue) {
12177 case 'label':
12178 ele.recalculateRenderedStyle();
12179 return _p.rstyle[opts.labelName] || 0;
12180
12181 default:
12182 return d.pfValue;
12183 }
12184 } else {
12185 return 1;
12186 }
12187 }
12188 };
12189
12190 fn$2['outer' + opts.uppercaseName] = function outerDimImpl() {
12191 var ele = this[0];
12192 var _p = ele._private;
12193 var cy = _p.cy;
12194 var styleEnabled = cy._private.styleEnabled;
12195
12196 if (ele) {
12197 if (styleEnabled) {
12198 var dim = ele[opts.name]();
12199 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
12200
12201 var padding = 2 * ele.padding();
12202 return dim + border + padding;
12203 } else {
12204 return 1;
12205 }
12206 }
12207 };
12208
12209 fn$2['rendered' + opts.uppercaseName] = function renderedDimImpl() {
12210 var ele = this[0];
12211
12212 if (ele) {
12213 var d = ele[opts.name]();
12214 return d * this.cy().zoom();
12215 }
12216 };
12217
12218 fn$2['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
12219 var ele = this[0];
12220
12221 if (ele) {
12222 var od = ele[opts.outerName]();
12223 return od * this.cy().zoom();
12224 }
12225 };
12226 };
12227
12228 defineDimFns({
12229 name: 'width'
12230 });
12231 defineDimFns({
12232 name: 'height'
12233 });
12234
12235 elesfn$a.padding = function () {
12236 var ele = this[0];
12237 var _p = ele._private;
12238
12239 if (ele.isParent()) {
12240 ele.updateCompoundBounds();
12241
12242 if (_p.autoPadding !== undefined) {
12243 return _p.autoPadding;
12244 } else {
12245 return ele.pstyle('padding').pfValue;
12246 }
12247 } else {
12248 return ele.pstyle('padding').pfValue;
12249 }
12250 };
12251
12252 elesfn$a.paddedHeight = function () {
12253 var ele = this[0];
12254 return ele.height() + 2 * ele.padding();
12255 };
12256
12257 elesfn$a.paddedWidth = function () {
12258 var ele = this[0];
12259 return ele.width() + 2 * ele.padding();
12260 };
12261
12262 var widthHeight = elesfn$a;
12263
12264 var ifEdge = function ifEdge(ele, getValue) {
12265 if (ele.isEdge()) {
12266 return getValue(ele);
12267 }
12268 };
12269
12270 var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
12271 if (ele.isEdge()) {
12272 var cy = ele.cy();
12273 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
12274 }
12275 };
12276
12277 var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
12278 if (ele.isEdge()) {
12279 var cy = ele.cy();
12280 var pan = cy.pan();
12281 var zoom = cy.zoom();
12282 return getPoints(ele).map(function (p) {
12283 return modelToRenderedPosition(p, zoom, pan);
12284 });
12285 }
12286 };
12287
12288 var controlPoints = function controlPoints(ele) {
12289 return ele.renderer().getControlPoints(ele);
12290 };
12291
12292 var segmentPoints = function segmentPoints(ele) {
12293 return ele.renderer().getSegmentPoints(ele);
12294 };
12295
12296 var sourceEndpoint = function sourceEndpoint(ele) {
12297 return ele.renderer().getSourceEndpoint(ele);
12298 };
12299
12300 var targetEndpoint = function targetEndpoint(ele) {
12301 return ele.renderer().getTargetEndpoint(ele);
12302 };
12303
12304 var midpoint = function midpoint(ele) {
12305 return ele.renderer().getEdgeMidpoint(ele);
12306 };
12307
12308 var pts = {
12309 controlPoints: {
12310 get: controlPoints,
12311 mult: true
12312 },
12313 segmentPoints: {
12314 get: segmentPoints,
12315 mult: true
12316 },
12317 sourceEndpoint: {
12318 get: sourceEndpoint
12319 },
12320 targetEndpoint: {
12321 get: targetEndpoint
12322 },
12323 midpoint: {
12324 get: midpoint
12325 }
12326 };
12327
12328 var renderedName = function renderedName(name) {
12329 return 'rendered' + name[0].toUpperCase() + name.substr(1);
12330 };
12331
12332 var edgePoints = Object.keys(pts).reduce(function (obj, name) {
12333 var spec = pts[name];
12334 var rName = renderedName(name);
12335
12336 obj[name] = function () {
12337 return ifEdge(this, spec.get);
12338 };
12339
12340 if (spec.mult) {
12341 obj[rName] = function () {
12342 return ifEdgeRenderedPositions(this, spec.get);
12343 };
12344 } else {
12345 obj[rName] = function () {
12346 return ifEdgeRenderedPosition(this, spec.get);
12347 };
12348 }
12349
12350 return obj;
12351 }, {});
12352
12353 var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
12354
12355 /*!
12356 Event object based on jQuery events, MIT license
12357
12358 https://jquery.org/license/
12359 https://tldrlegal.com/license/mit-license
12360 https://github.com/jquery/jquery/blob/master/src/event.js
12361 */
12362 var Event = function Event(src, props) {
12363 this.recycle(src, props);
12364 };
12365
12366 function returnFalse() {
12367 return false;
12368 }
12369
12370 function returnTrue() {
12371 return true;
12372 } // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
12373
12374
12375 Event.prototype = {
12376 instanceString: function instanceString() {
12377 return 'event';
12378 },
12379 recycle: function recycle(src, props) {
12380 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
12381
12382 if (src != null && src.preventDefault) {
12383 // Browser Event object
12384 this.type = src.type; // Events bubbling up the document may have been marked as prevented
12385 // by a handler lower down the tree; reflect the correct value.
12386
12387 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
12388 } else if (src != null && src.type) {
12389 // Plain object containing all event details
12390 props = src;
12391 } else {
12392 // Event string
12393 this.type = src;
12394 } // Put explicitly provided properties onto the event object
12395
12396
12397 if (props != null) {
12398 // more efficient to manually copy fields we use
12399 this.originalEvent = props.originalEvent;
12400 this.type = props.type != null ? props.type : this.type;
12401 this.cy = props.cy;
12402 this.target = props.target;
12403 this.position = props.position;
12404 this.renderedPosition = props.renderedPosition;
12405 this.namespace = props.namespace;
12406 this.layout = props.layout;
12407 }
12408
12409 if (this.cy != null && this.position != null && this.renderedPosition == null) {
12410 // create a rendered position based on the passed position
12411 var pos = this.position;
12412 var zoom = this.cy.zoom();
12413 var pan = this.cy.pan();
12414 this.renderedPosition = {
12415 x: pos.x * zoom + pan.x,
12416 y: pos.y * zoom + pan.y
12417 };
12418 } // Create a timestamp if incoming event doesn't have one
12419
12420
12421 this.timeStamp = src && src.timeStamp || Date.now();
12422 },
12423 preventDefault: function preventDefault() {
12424 this.isDefaultPrevented = returnTrue;
12425 var e = this.originalEvent;
12426
12427 if (!e) {
12428 return;
12429 } // if preventDefault exists run it on the original event
12430
12431
12432 if (e.preventDefault) {
12433 e.preventDefault();
12434 }
12435 },
12436 stopPropagation: function stopPropagation() {
12437 this.isPropagationStopped = returnTrue;
12438 var e = this.originalEvent;
12439
12440 if (!e) {
12441 return;
12442 } // if stopPropagation exists run it on the original event
12443
12444
12445 if (e.stopPropagation) {
12446 e.stopPropagation();
12447 }
12448 },
12449 stopImmediatePropagation: function stopImmediatePropagation() {
12450 this.isImmediatePropagationStopped = returnTrue;
12451 this.stopPropagation();
12452 },
12453 isDefaultPrevented: returnFalse,
12454 isPropagationStopped: returnFalse,
12455 isImmediatePropagationStopped: returnFalse
12456 };
12457
12458 var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
12459
12460 var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
12461
12462 var defaults$8 = {
12463 qualifierCompare: function qualifierCompare(q1, q2) {
12464 return q1 === q2;
12465 },
12466 eventMatches: function
12467 /*context, listener, eventObj*/
12468 eventMatches() {
12469 return true;
12470 },
12471 addEventFields: function
12472 /*context, evt*/
12473 addEventFields() {},
12474 callbackContext: function callbackContext(context
12475 /*, listener, eventObj*/
12476 ) {
12477 return context;
12478 },
12479 beforeEmit: function
12480 /* context, listener, eventObj */
12481 beforeEmit() {},
12482 afterEmit: function
12483 /* context, listener, eventObj */
12484 afterEmit() {},
12485 bubble: function
12486 /*context*/
12487 bubble() {
12488 return false;
12489 },
12490 parent: function
12491 /*context*/
12492 parent() {
12493 return null;
12494 },
12495 context: null
12496 };
12497 var defaultsKeys = Object.keys(defaults$8);
12498 var emptyOpts = {};
12499
12500 function Emitter() {
12501 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
12502 var context = arguments.length > 1 ? arguments[1] : undefined;
12503
12504 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
12505 for (var i = 0; i < defaultsKeys.length; i++) {
12506 var key = defaultsKeys[i];
12507 this[key] = opts[key] || defaults$8[key];
12508 }
12509
12510 this.context = context || this.context;
12511 this.listeners = [];
12512 this.emitting = 0;
12513 }
12514
12515 var p = Emitter.prototype;
12516
12517 var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
12518 if (fn$6(qualifier)) {
12519 callback = qualifier;
12520 qualifier = null;
12521 }
12522
12523 if (confOverrides) {
12524 if (conf == null) {
12525 conf = confOverrides;
12526 } else {
12527 conf = extend({}, conf, confOverrides);
12528 }
12529 }
12530
12531 var eventList = array(events) ? events : events.split(/\s+/);
12532
12533 for (var i = 0; i < eventList.length; i++) {
12534 var evt = eventList[i];
12535
12536 if (emptyString(evt)) {
12537 continue;
12538 }
12539
12540 var match = evt.match(eventRegex); // type[.namespace]
12541
12542 if (match) {
12543 var type = match[1];
12544 var namespace = match[2] ? match[2] : null;
12545 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
12546
12547 if (ret === false) {
12548 break;
12549 } // allow exiting early
12550
12551 }
12552 }
12553 };
12554
12555 var makeEventObj = function makeEventObj(self, obj) {
12556 self.addEventFields(self.context, obj);
12557 return new Event(obj.type, obj);
12558 };
12559
12560 var forEachEventObj = function forEachEventObj(self, handler, events) {
12561 if (event(events)) {
12562 handler(self, events);
12563 return;
12564 } else if (plainObject(events)) {
12565 handler(self, makeEventObj(self, events));
12566 return;
12567 }
12568
12569 var eventList = array(events) ? events : events.split(/\s+/);
12570
12571 for (var i = 0; i < eventList.length; i++) {
12572 var evt = eventList[i];
12573
12574 if (emptyString(evt)) {
12575 continue;
12576 }
12577
12578 var match = evt.match(eventRegex); // type[.namespace]
12579
12580 if (match) {
12581 var type = match[1];
12582 var namespace = match[2] ? match[2] : null;
12583 var eventObj = makeEventObj(self, {
12584 type: type,
12585 namespace: namespace,
12586 target: self.context
12587 });
12588 handler(self, eventObj);
12589 }
12590 }
12591 };
12592
12593 p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
12594 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
12595 if (fn$6(callback)) {
12596 self.listeners.push({
12597 event: event,
12598 // full event string
12599 callback: callback,
12600 // callback to run
12601 type: type,
12602 // the event type (e.g. 'click')
12603 namespace: namespace,
12604 // the event namespace (e.g. ".foo")
12605 qualifier: qualifier,
12606 // a restriction on whether to match this emitter
12607 conf: conf // additional configuration
12608
12609 });
12610 }
12611 }, events, qualifier, callback, conf, confOverrides);
12612 return this;
12613 };
12614
12615 p.one = function (events, qualifier, callback, conf) {
12616 return this.on(events, qualifier, callback, conf, {
12617 one: true
12618 });
12619 };
12620
12621 p.removeListener = p.off = function (events, qualifier, callback, conf) {
12622 var _this = this;
12623
12624 if (this.emitting !== 0) {
12625 this.listeners = copyArray$1(this.listeners);
12626 }
12627
12628 var listeners = this.listeners;
12629
12630 var _loop = function _loop(i) {
12631 var listener = listeners[i];
12632 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
12633 /*, conf*/
12634 ) {
12635 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
12636 listeners.splice(i, 1);
12637 return false;
12638 }
12639 }, events, qualifier, callback, conf);
12640 };
12641
12642 for (var i = listeners.length - 1; i >= 0; i--) {
12643 _loop(i);
12644 }
12645
12646 return this;
12647 };
12648
12649 p.removeAllListeners = function () {
12650 return this.removeListener('*');
12651 };
12652
12653 p.emit = p.trigger = function (events, extraParams, manualCallback) {
12654 var listeners = this.listeners;
12655 var numListenersBeforeEmit = listeners.length;
12656 this.emitting++;
12657
12658 if (!array(extraParams)) {
12659 extraParams = [extraParams];
12660 }
12661
12662 forEachEventObj(this, function (self, eventObj) {
12663 if (manualCallback != null) {
12664 listeners = [{
12665 event: eventObj.event,
12666 type: eventObj.type,
12667 namespace: eventObj.namespace,
12668 callback: manualCallback
12669 }];
12670 numListenersBeforeEmit = listeners.length;
12671 }
12672
12673 var _loop2 = function _loop2(i) {
12674 var listener = listeners[i];
12675
12676 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
12677 var args = [eventObj];
12678
12679 if (extraParams != null) {
12680 push(args, extraParams);
12681 }
12682
12683 self.beforeEmit(self.context, listener, eventObj);
12684
12685 if (listener.conf && listener.conf.one) {
12686 self.listeners = self.listeners.filter(function (l) {
12687 return l !== listener;
12688 });
12689 }
12690
12691 var context = self.callbackContext(self.context, listener, eventObj);
12692 var ret = listener.callback.apply(context, args);
12693 self.afterEmit(self.context, listener, eventObj);
12694
12695 if (ret === false) {
12696 eventObj.stopPropagation();
12697 eventObj.preventDefault();
12698 }
12699 } // if listener matches
12700
12701 };
12702
12703 for (var i = 0; i < numListenersBeforeEmit; i++) {
12704 _loop2(i);
12705 } // for listener
12706
12707
12708 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
12709 self.parent(self.context).emit(eventObj, extraParams);
12710 }
12711 }, events);
12712 this.emitting--;
12713 return this;
12714 };
12715
12716 var emitterOptions$1 = {
12717 qualifierCompare: function qualifierCompare(selector1, selector2) {
12718 if (selector1 == null || selector2 == null) {
12719 return selector1 == null && selector2 == null;
12720 } else {
12721 return selector1.sameText(selector2);
12722 }
12723 },
12724 eventMatches: function eventMatches(ele, listener, eventObj) {
12725 var selector = listener.qualifier;
12726
12727 if (selector != null) {
12728 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
12729 }
12730
12731 return true;
12732 },
12733 addEventFields: function addEventFields(ele, evt) {
12734 evt.cy = ele.cy();
12735 evt.target = ele;
12736 },
12737 callbackContext: function callbackContext(ele, listener, eventObj) {
12738 return listener.qualifier != null ? eventObj.target : ele;
12739 },
12740 beforeEmit: function beforeEmit(context, listener
12741 /*, eventObj*/
12742 ) {
12743 if (listener.conf && listener.conf.once) {
12744 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
12745 }
12746 },
12747 bubble: function bubble() {
12748 return true;
12749 },
12750 parent: function parent(ele) {
12751 return ele.isChild() ? ele.parent() : ele.cy();
12752 }
12753 };
12754
12755 var argSelector$1 = function argSelector(arg) {
12756 if (string(arg)) {
12757 return new Selector(arg);
12758 } else {
12759 return arg;
12760 }
12761 };
12762
12763 var elesfn$9 = {
12764 createEmitter: function createEmitter() {
12765 for (var i = 0; i < this.length; i++) {
12766 var ele = this[i];
12767 var _p = ele._private;
12768
12769 if (!_p.emitter) {
12770 _p.emitter = new Emitter(emitterOptions$1, ele);
12771 }
12772 }
12773
12774 return this;
12775 },
12776 emitter: function emitter() {
12777 return this._private.emitter;
12778 },
12779 on: function on(events, selector, callback) {
12780 var argSel = argSelector$1(selector);
12781
12782 for (var i = 0; i < this.length; i++) {
12783 var ele = this[i];
12784 ele.emitter().on(events, argSel, callback);
12785 }
12786
12787 return this;
12788 },
12789 removeListener: function removeListener(events, selector, callback) {
12790 var argSel = argSelector$1(selector);
12791
12792 for (var i = 0; i < this.length; i++) {
12793 var ele = this[i];
12794 ele.emitter().removeListener(events, argSel, callback);
12795 }
12796
12797 return this;
12798 },
12799 removeAllListeners: function removeAllListeners() {
12800 for (var i = 0; i < this.length; i++) {
12801 var ele = this[i];
12802 ele.emitter().removeAllListeners();
12803 }
12804
12805 return this;
12806 },
12807 one: function one(events, selector, callback) {
12808 var argSel = argSelector$1(selector);
12809
12810 for (var i = 0; i < this.length; i++) {
12811 var ele = this[i];
12812 ele.emitter().one(events, argSel, callback);
12813 }
12814
12815 return this;
12816 },
12817 once: function once(events, selector, callback) {
12818 var argSel = argSelector$1(selector);
12819
12820 for (var i = 0; i < this.length; i++) {
12821 var ele = this[i];
12822 ele.emitter().on(events, argSel, callback, {
12823 once: true,
12824 onceCollection: this
12825 });
12826 }
12827 },
12828 emit: function emit(events, extraParams) {
12829 for (var i = 0; i < this.length; i++) {
12830 var ele = this[i];
12831 ele.emitter().emit(events, extraParams);
12832 }
12833
12834 return this;
12835 },
12836 emitAndNotify: function emitAndNotify(event, extraParams) {
12837 // for internal use only
12838 if (this.length === 0) {
12839 return;
12840 } // empty collections don't need to notify anything
12841 // notify renderer
12842
12843
12844 this.cy().notify(event, this);
12845 this.emit(event, extraParams);
12846 return this;
12847 }
12848 };
12849 define.eventAliasesOn(elesfn$9);
12850
12851 var elesfn$8 = {
12852 nodes: function nodes(selector) {
12853 return this.filter(function (ele) {
12854 return ele.isNode();
12855 }).filter(selector);
12856 },
12857 edges: function edges(selector) {
12858 return this.filter(function (ele) {
12859 return ele.isEdge();
12860 }).filter(selector);
12861 },
12862 // internal helper to get nodes and edges as separate collections with single iteration over elements
12863 byGroup: function byGroup() {
12864 var nodes = this.spawn();
12865 var edges = this.spawn();
12866
12867 for (var i = 0; i < this.length; i++) {
12868 var ele = this[i];
12869
12870 if (ele.isNode()) {
12871 nodes.push(ele);
12872 } else {
12873 edges.push(ele);
12874 }
12875 }
12876
12877 return {
12878 nodes: nodes,
12879 edges: edges
12880 };
12881 },
12882 filter: function filter(_filter, thisArg) {
12883 if (_filter === undefined) {
12884 // check this first b/c it's the most common/performant case
12885 return this;
12886 } else if (string(_filter) || elementOrCollection(_filter)) {
12887 return new Selector(_filter).filter(this);
12888 } else if (fn$6(_filter)) {
12889 var filterEles = this.spawn();
12890 var eles = this;
12891
12892 for (var i = 0; i < eles.length; i++) {
12893 var ele = eles[i];
12894 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
12895
12896 if (include) {
12897 filterEles.push(ele);
12898 }
12899 }
12900
12901 return filterEles;
12902 }
12903
12904 return this.spawn(); // if not handled by above, give 'em an empty collection
12905 },
12906 not: function not(toRemove) {
12907 if (!toRemove) {
12908 return this;
12909 } else {
12910 if (string(toRemove)) {
12911 toRemove = this.filter(toRemove);
12912 }
12913
12914 var elements = this.spawn();
12915
12916 for (var i = 0; i < this.length; i++) {
12917 var element = this[i];
12918 var remove = toRemove.has(element);
12919
12920 if (!remove) {
12921 elements.push(element);
12922 }
12923 }
12924
12925 return elements;
12926 }
12927 },
12928 absoluteComplement: function absoluteComplement() {
12929 var cy = this.cy();
12930 return cy.mutableElements().not(this);
12931 },
12932 intersect: function intersect(other) {
12933 // if a selector is specified, then filter by it instead
12934 if (string(other)) {
12935 var selector = other;
12936 return this.filter(selector);
12937 }
12938
12939 var elements = this.spawn();
12940 var col1 = this;
12941 var col2 = other;
12942 var col1Smaller = this.length < other.length;
12943 var colS = col1Smaller ? col1 : col2;
12944 var colL = col1Smaller ? col2 : col1;
12945
12946 for (var i = 0; i < colS.length; i++) {
12947 var ele = colS[i];
12948
12949 if (colL.has(ele)) {
12950 elements.push(ele);
12951 }
12952 }
12953
12954 return elements;
12955 },
12956 xor: function xor(other) {
12957 var cy = this._private.cy;
12958
12959 if (string(other)) {
12960 other = cy.$(other);
12961 }
12962
12963 var elements = this.spawn();
12964 var col1 = this;
12965 var col2 = other;
12966
12967 var add = function add(col, other) {
12968 for (var i = 0; i < col.length; i++) {
12969 var ele = col[i];
12970 var id = ele._private.data.id;
12971 var inOther = other.hasElementWithId(id);
12972
12973 if (!inOther) {
12974 elements.push(ele);
12975 }
12976 }
12977 };
12978
12979 add(col1, col2);
12980 add(col2, col1);
12981 return elements;
12982 },
12983 diff: function diff(other) {
12984 var cy = this._private.cy;
12985
12986 if (string(other)) {
12987 other = cy.$(other);
12988 }
12989
12990 var left = this.spawn();
12991 var right = this.spawn();
12992 var both = this.spawn();
12993 var col1 = this;
12994 var col2 = other;
12995
12996 var add = function add(col, other, retEles) {
12997 for (var i = 0; i < col.length; i++) {
12998 var ele = col[i];
12999 var id = ele._private.data.id;
13000 var inOther = other.hasElementWithId(id);
13001
13002 if (inOther) {
13003 both.merge(ele);
13004 } else {
13005 retEles.push(ele);
13006 }
13007 }
13008 };
13009
13010 add(col1, col2, left);
13011 add(col2, col1, right);
13012 return {
13013 left: left,
13014 right: right,
13015 both: both
13016 };
13017 },
13018 add: function add(toAdd) {
13019 var cy = this._private.cy;
13020
13021 if (!toAdd) {
13022 return this;
13023 }
13024
13025 if (string(toAdd)) {
13026 var selector = toAdd;
13027 toAdd = cy.mutableElements().filter(selector);
13028 }
13029
13030 var elements = this.spawnSelf();
13031
13032 for (var i = 0; i < toAdd.length; i++) {
13033 var ele = toAdd[i];
13034 var add = !this.has(ele);
13035
13036 if (add) {
13037 elements.push(ele);
13038 }
13039 }
13040
13041 return elements;
13042 },
13043 // in place merge on calling collection
13044 merge: function merge(toAdd) {
13045 var _p = this._private;
13046 var cy = _p.cy;
13047
13048 if (!toAdd) {
13049 return this;
13050 }
13051
13052 if (toAdd && string(toAdd)) {
13053 var selector = toAdd;
13054 toAdd = cy.mutableElements().filter(selector);
13055 }
13056
13057 var map = _p.map;
13058
13059 for (var i = 0; i < toAdd.length; i++) {
13060 var toAddEle = toAdd[i];
13061 var id = toAddEle._private.data.id;
13062 var add = !map.has(id);
13063
13064 if (add) {
13065 var index = this.length++;
13066 this[index] = toAddEle;
13067 map.set(id, {
13068 ele: toAddEle,
13069 index: index
13070 });
13071 }
13072 }
13073
13074 return this; // chaining
13075 },
13076 unmergeAt: function unmergeAt(i) {
13077 var ele = this[i];
13078 var id = ele.id();
13079 var _p = this._private;
13080 var map = _p.map; // remove ele
13081
13082 this[i] = undefined;
13083 map["delete"](id);
13084 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
13085
13086 if (this.length > 1 && !unmergedLastEle) {
13087 var lastEleI = this.length - 1;
13088 var lastEle = this[lastEleI];
13089 var lastEleId = lastEle._private.data.id;
13090 this[lastEleI] = undefined;
13091 this[i] = lastEle;
13092 map.set(lastEleId, {
13093 ele: lastEle,
13094 index: i
13095 });
13096 } // the collection is now 1 ele smaller
13097
13098
13099 this.length--;
13100 return this;
13101 },
13102 // remove single ele in place in calling collection
13103 unmergeOne: function unmergeOne(ele) {
13104 ele = ele[0];
13105 var _p = this._private;
13106 var id = ele._private.data.id;
13107 var map = _p.map;
13108 var entry = map.get(id);
13109
13110 if (!entry) {
13111 return this; // no need to remove
13112 }
13113
13114 var i = entry.index;
13115 this.unmergeAt(i);
13116 return this;
13117 },
13118 // remove eles in place on calling collection
13119 unmerge: function unmerge(toRemove) {
13120 var cy = this._private.cy;
13121
13122 if (!toRemove) {
13123 return this;
13124 }
13125
13126 if (toRemove && string(toRemove)) {
13127 var selector = toRemove;
13128 toRemove = cy.mutableElements().filter(selector);
13129 }
13130
13131 for (var i = 0; i < toRemove.length; i++) {
13132 this.unmergeOne(toRemove[i]);
13133 }
13134
13135 return this; // chaining
13136 },
13137 unmergeBy: function unmergeBy(toRmFn) {
13138 for (var i = this.length - 1; i >= 0; i--) {
13139 var ele = this[i];
13140
13141 if (toRmFn(ele)) {
13142 this.unmergeAt(i);
13143 }
13144 }
13145
13146 return this;
13147 },
13148 map: function map(mapFn, thisArg) {
13149 var arr = [];
13150 var eles = this;
13151
13152 for (var i = 0; i < eles.length; i++) {
13153 var ele = eles[i];
13154 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
13155 arr.push(ret);
13156 }
13157
13158 return arr;
13159 },
13160 reduce: function reduce(fn, initialValue) {
13161 var val = initialValue;
13162 var eles = this;
13163
13164 for (var i = 0; i < eles.length; i++) {
13165 val = fn(val, eles[i], i, eles);
13166 }
13167
13168 return val;
13169 },
13170 max: function max(valFn, thisArg) {
13171 var max = -Infinity;
13172 var maxEle;
13173 var eles = this;
13174
13175 for (var i = 0; i < eles.length; i++) {
13176 var ele = eles[i];
13177 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
13178
13179 if (val > max) {
13180 max = val;
13181 maxEle = ele;
13182 }
13183 }
13184
13185 return {
13186 value: max,
13187 ele: maxEle
13188 };
13189 },
13190 min: function min(valFn, thisArg) {
13191 var min = Infinity;
13192 var minEle;
13193 var eles = this;
13194
13195 for (var i = 0; i < eles.length; i++) {
13196 var ele = eles[i];
13197 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
13198
13199 if (val < min) {
13200 min = val;
13201 minEle = ele;
13202 }
13203 }
13204
13205 return {
13206 value: min,
13207 ele: minEle
13208 };
13209 }
13210 }; // aliases
13211
13212 var fn$1 = elesfn$8;
13213 fn$1['u'] = fn$1['|'] = fn$1['+'] = fn$1.union = fn$1.or = fn$1.add;
13214 fn$1['\\'] = fn$1['!'] = fn$1['-'] = fn$1.difference = fn$1.relativeComplement = fn$1.subtract = fn$1.not;
13215 fn$1['n'] = fn$1['&'] = fn$1['.'] = fn$1.and = fn$1.intersection = fn$1.intersect;
13216 fn$1['^'] = fn$1['(+)'] = fn$1['(-)'] = fn$1.symmetricDifference = fn$1.symdiff = fn$1.xor;
13217 fn$1.fnFilter = fn$1.filterFn = fn$1.stdFilter = fn$1.filter;
13218 fn$1.complement = fn$1.abscomp = fn$1.absoluteComplement;
13219
13220 var elesfn$7 = {
13221 isNode: function isNode() {
13222 return this.group() === 'nodes';
13223 },
13224 isEdge: function isEdge() {
13225 return this.group() === 'edges';
13226 },
13227 isLoop: function isLoop() {
13228 return this.isEdge() && this.source()[0] === this.target()[0];
13229 },
13230 isSimple: function isSimple() {
13231 return this.isEdge() && this.source()[0] !== this.target()[0];
13232 },
13233 group: function group() {
13234 var ele = this[0];
13235
13236 if (ele) {
13237 return ele._private.group;
13238 }
13239 }
13240 };
13241
13242 /**
13243 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
13244 * and z-index (low to high). These styles affect how this applies:
13245 *
13246 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
13247 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
13248 * root to leaves of the compound graph. The last drawn is `top`.
13249 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
13250 * `manual` ignores this convention and draws based on the `z-index` value setting.
13251 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
13252 * `z-index` will be drawn on top of an element with a lower `z-index`.
13253 */
13254
13255 var zIndexSort = function zIndexSort(a, b) {
13256 var cy = a.cy();
13257 var hasCompoundNodes = cy.hasCompoundNodes();
13258
13259 function getDepth(ele) {
13260 var style = ele.pstyle('z-compound-depth');
13261
13262 if (style.value === 'auto') {
13263 return hasCompoundNodes ? ele.zDepth() : 0;
13264 } else if (style.value === 'bottom') {
13265 return -1;
13266 } else if (style.value === 'top') {
13267 return MAX_INT$1;
13268 } // 'orphan'
13269
13270
13271 return 0;
13272 }
13273
13274 var depthDiff = getDepth(a) - getDepth(b);
13275
13276 if (depthDiff !== 0) {
13277 return depthDiff;
13278 }
13279
13280 function getEleDepth(ele) {
13281 var style = ele.pstyle('z-index-compare');
13282
13283 if (style.value === 'auto') {
13284 return ele.isNode() ? 1 : 0;
13285 } // 'manual'
13286
13287
13288 return 0;
13289 }
13290
13291 var eleDiff = getEleDepth(a) - getEleDepth(b);
13292
13293 if (eleDiff !== 0) {
13294 return eleDiff;
13295 }
13296
13297 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
13298
13299 if (zDiff !== 0) {
13300 return zDiff;
13301 } // compare indices in the core (order added to graph w/ last on top)
13302
13303
13304 return a.poolIndex() - b.poolIndex();
13305 };
13306
13307 var elesfn$6 = {
13308 forEach: function forEach(fn, thisArg) {
13309 if (fn$6(fn)) {
13310 var N = this.length;
13311
13312 for (var i = 0; i < N; i++) {
13313 var ele = this[i];
13314 var ret = thisArg ? fn.apply(thisArg, [ele, i, this]) : fn(ele, i, this);
13315
13316 if (ret === false) {
13317 break;
13318 } // exit each early on return false
13319
13320 }
13321 }
13322
13323 return this;
13324 },
13325 toArray: function toArray() {
13326 var array = [];
13327
13328 for (var i = 0; i < this.length; i++) {
13329 array.push(this[i]);
13330 }
13331
13332 return array;
13333 },
13334 slice: function slice(start, end) {
13335 var array = [];
13336 var thisSize = this.length;
13337
13338 if (end == null) {
13339 end = thisSize;
13340 }
13341
13342 if (start == null) {
13343 start = 0;
13344 }
13345
13346 if (start < 0) {
13347 start = thisSize + start;
13348 }
13349
13350 if (end < 0) {
13351 end = thisSize + end;
13352 }
13353
13354 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
13355 array.push(this[i]);
13356 }
13357
13358 return this.spawn(array);
13359 },
13360 size: function size() {
13361 return this.length;
13362 },
13363 eq: function eq(i) {
13364 return this[i] || this.spawn();
13365 },
13366 first: function first() {
13367 return this[0] || this.spawn();
13368 },
13369 last: function last() {
13370 return this[this.length - 1] || this.spawn();
13371 },
13372 empty: function empty() {
13373 return this.length === 0;
13374 },
13375 nonempty: function nonempty() {
13376 return !this.empty();
13377 },
13378 sort: function sort(sortFn) {
13379 if (!fn$6(sortFn)) {
13380 return this;
13381 }
13382
13383 var sorted = this.toArray().sort(sortFn);
13384 return this.spawn(sorted);
13385 },
13386 sortByZIndex: function sortByZIndex() {
13387 return this.sort(zIndexSort);
13388 },
13389 zDepth: function zDepth() {
13390 var ele = this[0];
13391
13392 if (!ele) {
13393 return undefined;
13394 } // let cy = ele.cy();
13395
13396
13397 var _p = ele._private;
13398 var group = _p.group;
13399
13400 if (group === 'nodes') {
13401 var depth = _p.data.parent ? ele.parents().size() : 0;
13402
13403 if (!ele.isParent()) {
13404 return MAX_INT$1 - 1; // childless nodes always on top
13405 }
13406
13407 return depth;
13408 } else {
13409 var src = _p.source;
13410 var tgt = _p.target;
13411 var srcDepth = src.zDepth();
13412 var tgtDepth = tgt.zDepth();
13413 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
13414 }
13415 }
13416 };
13417 elesfn$6.each = elesfn$6.forEach;
13418
13419 var defineSymbolIterator = function defineSymbolIterator() {
13420 var typeofUndef = "undefined" ;
13421 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
13422
13423 if (isIteratorSupported) {
13424 elesfn$6[Symbol.iterator] = function () {
13425 var _this = this;
13426
13427 // eslint-disable-line no-undef
13428 var entry = {
13429 value: undefined,
13430 done: false
13431 };
13432 var i = 0;
13433 var length = this.length;
13434 return _defineProperty$1({
13435 next: function next() {
13436 if (i < length) {
13437 entry.value = _this[i++];
13438 } else {
13439 entry.value = undefined;
13440 entry.done = true;
13441 }
13442
13443 return entry;
13444 }
13445 }, Symbol.iterator, function () {
13446 // eslint-disable-line no-undef
13447 return this;
13448 });
13449 };
13450 }
13451 };
13452
13453 defineSymbolIterator();
13454
13455 var getLayoutDimensionOptions = defaults$g({
13456 nodeDimensionsIncludeLabels: false
13457 });
13458 var elesfn$5 = {
13459 // Calculates and returns node dimensions { x, y } based on options given
13460 layoutDimensions: function layoutDimensions(options) {
13461 options = getLayoutDimensionOptions(options);
13462 var dims;
13463
13464 if (!this.takesUpSpace()) {
13465 dims = {
13466 w: 0,
13467 h: 0
13468 };
13469 } else if (options.nodeDimensionsIncludeLabels) {
13470 var bbDim = this.boundingBox();
13471 dims = {
13472 w: bbDim.w,
13473 h: bbDim.h
13474 };
13475 } else {
13476 dims = {
13477 w: this.outerWidth(),
13478 h: this.outerHeight()
13479 };
13480 } // sanitise the dimensions for external layouts (avoid division by zero)
13481
13482
13483 if (dims.w === 0 || dims.h === 0) {
13484 dims.w = dims.h = 1;
13485 }
13486
13487 return dims;
13488 },
13489 // using standard layout options, apply position function (w/ or w/o animation)
13490 layoutPositions: function layoutPositions(layout, options, fn) {
13491 var nodes = this.nodes().filter(function (n) {
13492 return !n.isParent();
13493 });
13494 var cy = this.cy();
13495 var layoutEles = options.eles; // nodes & edges
13496
13497 var getMemoizeKey = function getMemoizeKey(node) {
13498 return node.id();
13499 };
13500
13501 var fnMem = memoize$1(fn, getMemoizeKey); // memoized version of position function
13502
13503 layout.emit({
13504 type: 'layoutstart',
13505 layout: layout
13506 });
13507 layout.animations = [];
13508
13509 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
13510 var center = {
13511 x: nodesBb.x1 + nodesBb.w / 2,
13512 y: nodesBb.y1 + nodesBb.h / 2
13513 };
13514 var spacingVector = {
13515 // scale from center of bounding box (not necessarily 0,0)
13516 x: (pos.x - center.x) * spacing,
13517 y: (pos.y - center.y) * spacing
13518 };
13519 return {
13520 x: center.x + spacingVector.x,
13521 y: center.y + spacingVector.y
13522 };
13523 };
13524
13525 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
13526
13527 var spacingBb = function spacingBb() {
13528 if (!useSpacingFactor) {
13529 return null;
13530 }
13531
13532 var bb = makeBoundingBox();
13533
13534 for (var i = 0; i < nodes.length; i++) {
13535 var node = nodes[i];
13536 var pos = fnMem(node, i);
13537 expandBoundingBoxByPoint(bb, pos.x, pos.y);
13538 }
13539
13540 return bb;
13541 };
13542
13543 var bb = spacingBb();
13544 var getFinalPos = memoize$1(function (node, i) {
13545 var newPos = fnMem(node, i);
13546
13547 if (useSpacingFactor) {
13548 var spacing = Math.abs(options.spacingFactor);
13549 newPos = calculateSpacing(spacing, bb, newPos);
13550 }
13551
13552 if (options.transform != null) {
13553 newPos = options.transform(node, newPos);
13554 }
13555
13556 return newPos;
13557 }, getMemoizeKey);
13558
13559 if (options.animate) {
13560 for (var i = 0; i < nodes.length; i++) {
13561 var node = nodes[i];
13562 var newPos = getFinalPos(node, i);
13563 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
13564
13565 if (animateNode) {
13566 var ani = node.animation({
13567 position: newPos,
13568 duration: options.animationDuration,
13569 easing: options.animationEasing
13570 });
13571 layout.animations.push(ani);
13572 } else {
13573 node.position(newPos);
13574 }
13575 }
13576
13577 if (options.fit) {
13578 var fitAni = cy.animation({
13579 fit: {
13580 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
13581 padding: options.padding
13582 },
13583 duration: options.animationDuration,
13584 easing: options.animationEasing
13585 });
13586 layout.animations.push(fitAni);
13587 } else if (options.zoom !== undefined && options.pan !== undefined) {
13588 var zoomPanAni = cy.animation({
13589 zoom: options.zoom,
13590 pan: options.pan,
13591 duration: options.animationDuration,
13592 easing: options.animationEasing
13593 });
13594 layout.animations.push(zoomPanAni);
13595 }
13596
13597 layout.animations.forEach(function (ani) {
13598 return ani.play();
13599 });
13600 layout.one('layoutready', options.ready);
13601 layout.emit({
13602 type: 'layoutready',
13603 layout: layout
13604 });
13605 Promise$1.all(layout.animations.map(function (ani) {
13606 return ani.promise();
13607 })).then(function () {
13608 layout.one('layoutstop', options.stop);
13609 layout.emit({
13610 type: 'layoutstop',
13611 layout: layout
13612 });
13613 });
13614 } else {
13615 nodes.positions(getFinalPos);
13616
13617 if (options.fit) {
13618 cy.fit(options.eles, options.padding);
13619 }
13620
13621 if (options.zoom != null) {
13622 cy.zoom(options.zoom);
13623 }
13624
13625 if (options.pan) {
13626 cy.pan(options.pan);
13627 }
13628
13629 layout.one('layoutready', options.ready);
13630 layout.emit({
13631 type: 'layoutready',
13632 layout: layout
13633 });
13634 layout.one('layoutstop', options.stop);
13635 layout.emit({
13636 type: 'layoutstop',
13637 layout: layout
13638 });
13639 }
13640
13641 return this; // chaining
13642 },
13643 layout: function layout(options) {
13644 var cy = this.cy();
13645 return cy.makeLayout(extend({}, options, {
13646 eles: this
13647 }));
13648 }
13649 }; // aliases:
13650
13651 elesfn$5.createLayout = elesfn$5.makeLayout = elesfn$5.layout;
13652
13653 function styleCache(key, fn, ele) {
13654 var _p = ele._private;
13655 var cache = _p.styleCache = _p.styleCache || [];
13656 var val;
13657
13658 if ((val = cache[key]) != null) {
13659 return val;
13660 } else {
13661 val = cache[key] = fn(ele);
13662 return val;
13663 }
13664 }
13665
13666 function cacheStyleFunction(key, fn) {
13667 key = hashString(key);
13668 return function cachedStyleFunction(ele) {
13669 return styleCache(key, fn, ele);
13670 };
13671 }
13672
13673 function cachePrototypeStyleFunction(key, fn) {
13674 key = hashString(key);
13675
13676 var selfFn = function selfFn(ele) {
13677 return fn.call(ele);
13678 };
13679
13680 return function cachedPrototypeStyleFunction() {
13681 var ele = this[0];
13682
13683 if (ele) {
13684 return styleCache(key, selfFn, ele);
13685 }
13686 };
13687 }
13688
13689 var elesfn$4 = {
13690 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
13691 var cy = this.cy();
13692 var renderer = cy.renderer();
13693 var styleEnabled = cy.styleEnabled();
13694
13695 if (renderer && styleEnabled) {
13696 renderer.recalculateRenderedStyle(this, useCache);
13697 }
13698
13699 return this;
13700 },
13701 dirtyStyleCache: function dirtyStyleCache() {
13702 var cy = this.cy();
13703
13704 var dirty = function dirty(ele) {
13705 return ele._private.styleCache = null;
13706 };
13707
13708 if (cy.hasCompoundNodes()) {
13709 var eles;
13710 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
13711 eles.merge(eles.connectedEdges());
13712 eles.forEach(dirty);
13713 } else {
13714 this.forEach(function (ele) {
13715 dirty(ele);
13716 ele.connectedEdges().forEach(dirty);
13717 });
13718 }
13719
13720 return this;
13721 },
13722 // fully updates (recalculates) the style for the elements
13723 updateStyle: function updateStyle(notifyRenderer) {
13724 var cy = this._private.cy;
13725
13726 if (!cy.styleEnabled()) {
13727 return this;
13728 }
13729
13730 if (cy.batching()) {
13731 var bEles = cy._private.batchStyleEles;
13732 bEles.merge(this);
13733 return this; // chaining and exit early when batching
13734 }
13735
13736 var hasCompounds = cy.hasCompoundNodes();
13737 var updatedEles = this;
13738 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
13739
13740 if (hasCompounds) {
13741 // then add everything up and down for compound selector checks
13742 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
13743 } // let changedEles = style.apply( updatedEles );
13744
13745
13746 var changedEles = updatedEles;
13747
13748 if (notifyRenderer) {
13749 changedEles.emitAndNotify('style'); // let renderer know we changed style
13750 } else {
13751 changedEles.emit('style'); // just fire the event
13752 }
13753
13754 updatedEles.forEach(function (ele) {
13755 return ele._private.styleDirty = true;
13756 });
13757 return this; // chaining
13758 },
13759 // private: clears dirty flag and recalculates style
13760 cleanStyle: function cleanStyle() {
13761 var cy = this.cy();
13762
13763 if (!cy.styleEnabled()) {
13764 return;
13765 }
13766
13767 for (var i = 0; i < this.length; i++) {
13768 var ele = this[i];
13769
13770 if (ele._private.styleDirty) {
13771 // n.b. this flag should be set before apply() to avoid potential infinite recursion
13772 ele._private.styleDirty = false;
13773 cy.style().apply(ele);
13774 }
13775 }
13776 },
13777 // get the internal parsed style object for the specified property
13778 parsedStyle: function parsedStyle(property) {
13779 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13780 var ele = this[0];
13781 var cy = ele.cy();
13782
13783 if (!cy.styleEnabled()) {
13784 return;
13785 }
13786
13787 if (ele) {
13788 this.cleanStyle();
13789 var overriddenStyle = ele._private.style[property];
13790
13791 if (overriddenStyle != null) {
13792 return overriddenStyle;
13793 } else if (includeNonDefault) {
13794 return cy.style().getDefaultProperty(property);
13795 } else {
13796 return null;
13797 }
13798 }
13799 },
13800 numericStyle: function numericStyle(property) {
13801 var ele = this[0];
13802
13803 if (!ele.cy().styleEnabled()) {
13804 return;
13805 }
13806
13807 if (ele) {
13808 var pstyle = ele.pstyle(property);
13809 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
13810 }
13811 },
13812 numericStyleUnits: function numericStyleUnits(property) {
13813 var ele = this[0];
13814
13815 if (!ele.cy().styleEnabled()) {
13816 return;
13817 }
13818
13819 if (ele) {
13820 return ele.pstyle(property).units;
13821 }
13822 },
13823 // get the specified css property as a rendered value (i.e. on-screen value)
13824 // or get the whole rendered style if no property specified (NB doesn't allow setting)
13825 renderedStyle: function renderedStyle(property) {
13826 var cy = this.cy();
13827
13828 if (!cy.styleEnabled()) {
13829 return this;
13830 }
13831
13832 var ele = this[0];
13833
13834 if (ele) {
13835 return cy.style().getRenderedStyle(ele, property);
13836 }
13837 },
13838 // read the calculated css style of the element or override the style (via a bypass)
13839 style: function style(name, value) {
13840 var cy = this.cy();
13841
13842 if (!cy.styleEnabled()) {
13843 return this;
13844 }
13845
13846 var updateTransitions = false;
13847 var style = cy.style();
13848
13849 if (plainObject(name)) {
13850 // then extend the bypass
13851 var props = name;
13852 style.applyBypass(this, props, updateTransitions);
13853 this.emitAndNotify('style'); // let the renderer know we've updated style
13854 } else if (string(name)) {
13855 if (value === undefined) {
13856 // then get the property from the style
13857 var ele = this[0];
13858
13859 if (ele) {
13860 return style.getStylePropertyValue(ele, name);
13861 } else {
13862 // empty collection => can't get any value
13863 return;
13864 }
13865 } else {
13866 // then set the bypass with the property value
13867 style.applyBypass(this, name, value, updateTransitions);
13868 this.emitAndNotify('style'); // let the renderer know we've updated style
13869 }
13870 } else if (name === undefined) {
13871 var _ele = this[0];
13872
13873 if (_ele) {
13874 return style.getRawStyle(_ele);
13875 } else {
13876 // empty collection => can't get any value
13877 return;
13878 }
13879 }
13880
13881 return this; // chaining
13882 },
13883 removeStyle: function removeStyle(names) {
13884 var cy = this.cy();
13885
13886 if (!cy.styleEnabled()) {
13887 return this;
13888 }
13889
13890 var updateTransitions = false;
13891 var style = cy.style();
13892 var eles = this;
13893
13894 if (names === undefined) {
13895 for (var i = 0; i < eles.length; i++) {
13896 var ele = eles[i];
13897 style.removeAllBypasses(ele, updateTransitions);
13898 }
13899 } else {
13900 names = names.split(/\s+/);
13901
13902 for (var _i = 0; _i < eles.length; _i++) {
13903 var _ele2 = eles[_i];
13904 style.removeBypasses(_ele2, names, updateTransitions);
13905 }
13906 }
13907
13908 this.emitAndNotify('style'); // let the renderer know we've updated style
13909
13910 return this; // chaining
13911 },
13912 show: function show() {
13913 this.css('display', 'element');
13914 return this; // chaining
13915 },
13916 hide: function hide() {
13917 this.css('display', 'none');
13918 return this; // chaining
13919 },
13920 effectiveOpacity: function effectiveOpacity() {
13921 var cy = this.cy();
13922
13923 if (!cy.styleEnabled()) {
13924 return 1;
13925 }
13926
13927 var hasCompoundNodes = cy.hasCompoundNodes();
13928 var ele = this[0];
13929
13930 if (ele) {
13931 var _p = ele._private;
13932 var parentOpacity = ele.pstyle('opacity').value;
13933
13934 if (!hasCompoundNodes) {
13935 return parentOpacity;
13936 }
13937
13938 var parents = !_p.data.parent ? null : ele.parents();
13939
13940 if (parents) {
13941 for (var i = 0; i < parents.length; i++) {
13942 var parent = parents[i];
13943 var opacity = parent.pstyle('opacity').value;
13944 parentOpacity = opacity * parentOpacity;
13945 }
13946 }
13947
13948 return parentOpacity;
13949 }
13950 },
13951 transparent: function transparent() {
13952 var cy = this.cy();
13953
13954 if (!cy.styleEnabled()) {
13955 return false;
13956 }
13957
13958 var ele = this[0];
13959 var hasCompoundNodes = ele.cy().hasCompoundNodes();
13960
13961 if (ele) {
13962 if (!hasCompoundNodes) {
13963 return ele.pstyle('opacity').value === 0;
13964 } else {
13965 return ele.effectiveOpacity() === 0;
13966 }
13967 }
13968 },
13969 backgrounding: function backgrounding() {
13970 var cy = this.cy();
13971
13972 if (!cy.styleEnabled()) {
13973 return false;
13974 }
13975
13976 var ele = this[0];
13977 return ele._private.backgrounding ? true : false;
13978 }
13979 };
13980
13981 function checkCompound(ele, parentOk) {
13982 var _p = ele._private;
13983 var parents = _p.data.parent ? ele.parents() : null;
13984
13985 if (parents) {
13986 for (var i = 0; i < parents.length; i++) {
13987 var parent = parents[i];
13988
13989 if (!parentOk(parent)) {
13990 return false;
13991 }
13992 }
13993 }
13994
13995 return true;
13996 }
13997
13998 function defineDerivedStateFunction(specs) {
13999 var ok = specs.ok;
14000 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
14001 var parentOk = specs.parentOk || specs.ok;
14002 return function () {
14003 var cy = this.cy();
14004
14005 if (!cy.styleEnabled()) {
14006 return true;
14007 }
14008
14009 var ele = this[0];
14010 var hasCompoundNodes = cy.hasCompoundNodes();
14011
14012 if (ele) {
14013 var _p = ele._private;
14014
14015 if (!ok(ele)) {
14016 return false;
14017 }
14018
14019 if (ele.isNode()) {
14020 return !hasCompoundNodes || checkCompound(ele, parentOk);
14021 } else {
14022 var src = _p.source;
14023 var tgt = _p.target;
14024 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
14025 }
14026 }
14027 };
14028 }
14029
14030 var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
14031 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
14032 });
14033 elesfn$4.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
14034 ok: eleTakesUpSpace
14035 }));
14036 var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
14037 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
14038 });
14039 var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
14040 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
14041 });
14042 elesfn$4.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
14043 ok: eleInteractive,
14044 parentOk: parentInteractive,
14045 edgeOkViaNode: eleTakesUpSpace
14046 }));
14047
14048 elesfn$4.noninteractive = function () {
14049 var ele = this[0];
14050
14051 if (ele) {
14052 return !ele.interactive();
14053 }
14054 };
14055
14056 var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
14057 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
14058 });
14059 var edgeVisibleViaNode = eleTakesUpSpace;
14060 elesfn$4.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
14061 ok: eleVisible,
14062 edgeOkViaNode: edgeVisibleViaNode
14063 }));
14064
14065 elesfn$4.hidden = function () {
14066 var ele = this[0];
14067
14068 if (ele) {
14069 return !ele.visible();
14070 }
14071 };
14072
14073 elesfn$4.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
14074 if (!this.cy().styleEnabled()) {
14075 return false;
14076 }
14077
14078 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
14079 });
14080 elesfn$4.bypass = elesfn$4.css = elesfn$4.style;
14081 elesfn$4.renderedCss = elesfn$4.renderedStyle;
14082 elesfn$4.removeBypass = elesfn$4.removeCss = elesfn$4.removeStyle;
14083 elesfn$4.pstyle = elesfn$4.parsedStyle;
14084
14085 var elesfn$3 = {};
14086
14087 function defineSwitchFunction(params) {
14088 return function () {
14089 var args = arguments;
14090 var changedEles = []; // e.g. cy.nodes().select( data, handler )
14091
14092 if (args.length === 2) {
14093 var data = args[0];
14094 var handler = args[1];
14095 this.on(params.event, data, handler);
14096 } // e.g. cy.nodes().select( handler )
14097 else if (args.length === 1 && fn$6(args[0])) {
14098 var _handler = args[0];
14099 this.on(params.event, _handler);
14100 } // e.g. cy.nodes().select()
14101 // e.g. (private) cy.nodes().select(['tapselect'])
14102 else if (args.length === 0 || args.length === 1 && array(args[0])) {
14103 var addlEvents = args.length === 1 ? args[0] : null;
14104
14105 for (var i = 0; i < this.length; i++) {
14106 var ele = this[i];
14107 var able = !params.ableField || ele._private[params.ableField];
14108 var changed = ele._private[params.field] != params.value;
14109
14110 if (params.overrideAble) {
14111 var overrideAble = params.overrideAble(ele);
14112
14113 if (overrideAble !== undefined) {
14114 able = overrideAble;
14115
14116 if (!overrideAble) {
14117 return this;
14118 } // to save cycles assume not able for all on override
14119
14120 }
14121 }
14122
14123 if (able) {
14124 ele._private[params.field] = params.value;
14125
14126 if (changed) {
14127 changedEles.push(ele);
14128 }
14129 }
14130 }
14131
14132 var changedColl = this.spawn(changedEles);
14133 changedColl.updateStyle(); // change of state => possible change of style
14134
14135 changedColl.emit(params.event);
14136
14137 if (addlEvents) {
14138 changedColl.emit(addlEvents);
14139 }
14140 }
14141
14142 return this;
14143 };
14144 }
14145
14146 function defineSwitchSet(params) {
14147 elesfn$3[params.field] = function () {
14148 var ele = this[0];
14149
14150 if (ele) {
14151 if (params.overrideField) {
14152 var val = params.overrideField(ele);
14153
14154 if (val !== undefined) {
14155 return val;
14156 }
14157 }
14158
14159 return ele._private[params.field];
14160 }
14161 };
14162
14163 elesfn$3[params.on] = defineSwitchFunction({
14164 event: params.on,
14165 field: params.field,
14166 ableField: params.ableField,
14167 overrideAble: params.overrideAble,
14168 value: true
14169 });
14170 elesfn$3[params.off] = defineSwitchFunction({
14171 event: params.off,
14172 field: params.field,
14173 ableField: params.ableField,
14174 overrideAble: params.overrideAble,
14175 value: false
14176 });
14177 }
14178
14179 defineSwitchSet({
14180 field: 'locked',
14181 overrideField: function overrideField(ele) {
14182 return ele.cy().autolock() ? true : undefined;
14183 },
14184 on: 'lock',
14185 off: 'unlock'
14186 });
14187 defineSwitchSet({
14188 field: 'grabbable',
14189 overrideField: function overrideField(ele) {
14190 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
14191 },
14192 on: 'grabify',
14193 off: 'ungrabify'
14194 });
14195 defineSwitchSet({
14196 field: 'selected',
14197 ableField: 'selectable',
14198 overrideAble: function overrideAble(ele) {
14199 return ele.cy().autounselectify() ? false : undefined;
14200 },
14201 on: 'select',
14202 off: 'unselect'
14203 });
14204 defineSwitchSet({
14205 field: 'selectable',
14206 overrideField: function overrideField(ele) {
14207 return ele.cy().autounselectify() ? false : undefined;
14208 },
14209 on: 'selectify',
14210 off: 'unselectify'
14211 });
14212 elesfn$3.deselect = elesfn$3.unselect;
14213
14214 elesfn$3.grabbed = function () {
14215 var ele = this[0];
14216
14217 if (ele) {
14218 return ele._private.grabbed;
14219 }
14220 };
14221
14222 defineSwitchSet({
14223 field: 'active',
14224 on: 'activate',
14225 off: 'unactivate'
14226 });
14227 defineSwitchSet({
14228 field: 'pannable',
14229 on: 'panify',
14230 off: 'unpanify'
14231 });
14232
14233 elesfn$3.inactive = function () {
14234 var ele = this[0];
14235
14236 if (ele) {
14237 return !ele._private.active;
14238 }
14239 };
14240
14241 var elesfn$2 = {}; // DAG functions
14242 ////////////////
14243
14244 var defineDagExtremity = function defineDagExtremity(params) {
14245 return function dagExtremityImpl(selector) {
14246 var eles = this;
14247 var ret = [];
14248
14249 for (var i = 0; i < eles.length; i++) {
14250 var ele = eles[i];
14251
14252 if (!ele.isNode()) {
14253 continue;
14254 }
14255
14256 var disqualified = false;
14257 var edges = ele.connectedEdges();
14258
14259 for (var j = 0; j < edges.length; j++) {
14260 var edge = edges[j];
14261 var src = edge.source();
14262 var tgt = edge.target();
14263
14264 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
14265 disqualified = true;
14266 break;
14267 }
14268 }
14269
14270 if (!disqualified) {
14271 ret.push(ele);
14272 }
14273 }
14274
14275 return this.spawn(ret, true).filter(selector);
14276 };
14277 };
14278
14279 var defineDagOneHop = function defineDagOneHop(params) {
14280 return function (selector) {
14281 var eles = this;
14282 var oEles = [];
14283
14284 for (var i = 0; i < eles.length; i++) {
14285 var ele = eles[i];
14286
14287 if (!ele.isNode()) {
14288 continue;
14289 }
14290
14291 var edges = ele.connectedEdges();
14292
14293 for (var j = 0; j < edges.length; j++) {
14294 var edge = edges[j];
14295 var src = edge.source();
14296 var tgt = edge.target();
14297
14298 if (params.outgoing && src === ele) {
14299 oEles.push(edge);
14300 oEles.push(tgt);
14301 } else if (params.incoming && tgt === ele) {
14302 oEles.push(edge);
14303 oEles.push(src);
14304 }
14305 }
14306 }
14307
14308 return this.spawn(oEles, true).filter(selector);
14309 };
14310 };
14311
14312 var defineDagAllHops = function defineDagAllHops(params) {
14313 return function (selector) {
14314 var eles = this;
14315 var sEles = [];
14316 var sElesIds = {};
14317
14318 for (;;) {
14319 var next = params.outgoing ? eles.outgoers() : eles.incomers();
14320
14321 if (next.length === 0) {
14322 break;
14323 } // done if none left
14324
14325
14326 var newNext = false;
14327
14328 for (var i = 0; i < next.length; i++) {
14329 var n = next[i];
14330 var nid = n.id();
14331
14332 if (!sElesIds[nid]) {
14333 sElesIds[nid] = true;
14334 sEles.push(n);
14335 newNext = true;
14336 }
14337 }
14338
14339 if (!newNext) {
14340 break;
14341 } // done if touched all outgoers already
14342
14343
14344 eles = next;
14345 }
14346
14347 return this.spawn(sEles, true).filter(selector);
14348 };
14349 };
14350
14351 elesfn$2.clearTraversalCache = function () {
14352 for (var i = 0; i < this.length; i++) {
14353 this[i]._private.traversalCache = null;
14354 }
14355 };
14356
14357 extend(elesfn$2, {
14358 // get the root nodes in the DAG
14359 roots: defineDagExtremity({
14360 noIncomingEdges: true
14361 }),
14362 // get the leaf nodes in the DAG
14363 leaves: defineDagExtremity({
14364 noOutgoingEdges: true
14365 }),
14366 // normally called children in graph theory
14367 // these nodes =edges=> outgoing nodes
14368 outgoers: cache(defineDagOneHop({
14369 outgoing: true
14370 }), 'outgoers'),
14371 // aka DAG descendants
14372 successors: defineDagAllHops({
14373 outgoing: true
14374 }),
14375 // normally called parents in graph theory
14376 // these nodes <=edges= incoming nodes
14377 incomers: cache(defineDagOneHop({
14378 incoming: true
14379 }), 'incomers'),
14380 // aka DAG ancestors
14381 predecessors: defineDagAllHops({
14382 incoming: true
14383 })
14384 }); // Neighbourhood functions
14385 //////////////////////////
14386
14387 extend(elesfn$2, {
14388 neighborhood: cache(function (selector) {
14389 var elements = [];
14390 var nodes = this.nodes();
14391
14392 for (var i = 0; i < nodes.length; i++) {
14393 // for all nodes
14394 var node = nodes[i];
14395 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
14396
14397 for (var j = 0; j < connectedEdges.length; j++) {
14398 var edge = connectedEdges[j];
14399 var src = edge.source();
14400 var tgt = edge.target();
14401 var otherNode = node === src ? tgt : src; // need check in case of loop
14402
14403 if (otherNode.length > 0) {
14404 elements.push(otherNode[0]); // add node 1 hop away
14405 } // add connected edge
14406
14407
14408 elements.push(edge[0]);
14409 }
14410 }
14411
14412 return this.spawn(elements, true).filter(selector);
14413 }, 'neighborhood'),
14414 closedNeighborhood: function closedNeighborhood(selector) {
14415 return this.neighborhood().add(this).filter(selector);
14416 },
14417 openNeighborhood: function openNeighborhood(selector) {
14418 return this.neighborhood(selector);
14419 }
14420 }); // aliases
14421
14422 elesfn$2.neighbourhood = elesfn$2.neighborhood;
14423 elesfn$2.closedNeighbourhood = elesfn$2.closedNeighborhood;
14424 elesfn$2.openNeighbourhood = elesfn$2.openNeighborhood; // Edge functions
14425 /////////////////
14426
14427 extend(elesfn$2, {
14428 source: cache(function sourceImpl(selector) {
14429 var ele = this[0];
14430 var src;
14431
14432 if (ele) {
14433 src = ele._private.source || ele.cy().collection();
14434 }
14435
14436 return src && selector ? src.filter(selector) : src;
14437 }, 'source'),
14438 target: cache(function targetImpl(selector) {
14439 var ele = this[0];
14440 var tgt;
14441
14442 if (ele) {
14443 tgt = ele._private.target || ele.cy().collection();
14444 }
14445
14446 return tgt && selector ? tgt.filter(selector) : tgt;
14447 }, 'target'),
14448 sources: defineSourceFunction({
14449 attr: 'source'
14450 }),
14451 targets: defineSourceFunction({
14452 attr: 'target'
14453 })
14454 });
14455
14456 function defineSourceFunction(params) {
14457 return function sourceImpl(selector) {
14458 var sources = [];
14459
14460 for (var i = 0; i < this.length; i++) {
14461 var ele = this[i];
14462 var src = ele._private[params.attr];
14463
14464 if (src) {
14465 sources.push(src);
14466 }
14467 }
14468
14469 return this.spawn(sources, true).filter(selector);
14470 };
14471 }
14472
14473 extend(elesfn$2, {
14474 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
14475 edgesTo: cache(defineEdgesWithFunction({
14476 thisIsSrc: true
14477 }), 'edgesTo')
14478 });
14479
14480 function defineEdgesWithFunction(params) {
14481 return function edgesWithImpl(otherNodes) {
14482 var elements = [];
14483 var cy = this._private.cy;
14484 var p = params || {}; // get elements if a selector is specified
14485
14486 if (string(otherNodes)) {
14487 otherNodes = cy.$(otherNodes);
14488 }
14489
14490 for (var h = 0; h < otherNodes.length; h++) {
14491 var edges = otherNodes[h]._private.edges;
14492
14493 for (var i = 0; i < edges.length; i++) {
14494 var edge = edges[i];
14495 var edgeData = edge._private.data;
14496 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
14497 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
14498 var edgeConnectsThisAndOther = thisToOther || otherToThis;
14499
14500 if (!edgeConnectsThisAndOther) {
14501 continue;
14502 }
14503
14504 if (p.thisIsSrc || p.thisIsTgt) {
14505 if (p.thisIsSrc && !thisToOther) {
14506 continue;
14507 }
14508
14509 if (p.thisIsTgt && !otherToThis) {
14510 continue;
14511 }
14512 }
14513
14514 elements.push(edge);
14515 }
14516 }
14517
14518 return this.spawn(elements, true);
14519 };
14520 }
14521
14522 extend(elesfn$2, {
14523 connectedEdges: cache(function (selector) {
14524 var retEles = [];
14525 var eles = this;
14526
14527 for (var i = 0; i < eles.length; i++) {
14528 var node = eles[i];
14529
14530 if (!node.isNode()) {
14531 continue;
14532 }
14533
14534 var edges = node._private.edges;
14535
14536 for (var j = 0; j < edges.length; j++) {
14537 var edge = edges[j];
14538 retEles.push(edge);
14539 }
14540 }
14541
14542 return this.spawn(retEles, true).filter(selector);
14543 }, 'connectedEdges'),
14544 connectedNodes: cache(function (selector) {
14545 var retEles = [];
14546 var eles = this;
14547
14548 for (var i = 0; i < eles.length; i++) {
14549 var edge = eles[i];
14550
14551 if (!edge.isEdge()) {
14552 continue;
14553 }
14554
14555 retEles.push(edge.source()[0]);
14556 retEles.push(edge.target()[0]);
14557 }
14558
14559 return this.spawn(retEles, true).filter(selector);
14560 }, 'connectedNodes'),
14561 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
14562 codirectedEdges: cache(defineParallelEdgesFunction({
14563 codirected: true
14564 }), 'codirectedEdges')
14565 });
14566
14567 function defineParallelEdgesFunction(params) {
14568 var defaults = {
14569 codirected: false
14570 };
14571 params = extend({}, defaults, params);
14572 return function parallelEdgesImpl(selector) {
14573 // micro-optimised for renderer
14574 var elements = [];
14575 var edges = this.edges();
14576 var p = params; // look at all the edges in the collection
14577
14578 for (var i = 0; i < edges.length; i++) {
14579 var edge1 = edges[i];
14580 var edge1_p = edge1._private;
14581 var src1 = edge1_p.source;
14582 var srcid1 = src1._private.data.id;
14583 var tgtid1 = edge1_p.data.target;
14584 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
14585
14586 for (var j = 0; j < srcEdges1.length; j++) {
14587 var edge2 = srcEdges1[j];
14588 var edge2data = edge2._private.data;
14589 var tgtid2 = edge2data.target;
14590 var srcid2 = edge2data.source;
14591 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
14592 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
14593
14594 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
14595 elements.push(edge2);
14596 }
14597 }
14598 }
14599
14600 return this.spawn(elements, true).filter(selector);
14601 };
14602 } // Misc functions
14603 /////////////////
14604
14605
14606 extend(elesfn$2, {
14607 components: function components(root) {
14608 var self = this;
14609 var cy = self.cy();
14610 var visited = cy.collection();
14611 var unvisited = root == null ? self.nodes() : root.nodes();
14612 var components = [];
14613
14614 if (root != null && unvisited.empty()) {
14615 // root may contain only edges
14616 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
14617 }
14618
14619 var visitInComponent = function visitInComponent(node, component) {
14620 visited.merge(node);
14621 unvisited.unmerge(node);
14622 component.merge(node);
14623 };
14624
14625 if (unvisited.empty()) {
14626 return self.spawn();
14627 }
14628
14629 var _loop = function _loop() {
14630 // each iteration yields a component
14631 var cmpt = cy.collection();
14632 components.push(cmpt);
14633 var root = unvisited[0];
14634 visitInComponent(root, cmpt);
14635 self.bfs({
14636 directed: false,
14637 roots: root,
14638 visit: function visit(v) {
14639 return visitInComponent(v, cmpt);
14640 }
14641 });
14642 cmpt.forEach(function (node) {
14643 node.connectedEdges().forEach(function (e) {
14644 // connectedEdges() usually cached
14645 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
14646 // has() is cheap
14647 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
14648 }
14649 });
14650 });
14651 };
14652
14653 do {
14654 _loop();
14655 } while (unvisited.length > 0);
14656
14657 return components;
14658 },
14659 component: function component() {
14660 var ele = this[0];
14661 return ele.cy().mutableElements().components(ele)[0];
14662 }
14663 });
14664 elesfn$2.componentsOf = elesfn$2.components;
14665
14666 var Collection = function Collection(cy, elements) {
14667 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
14668 var removed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
14669
14670 if (cy === undefined) {
14671 error('A collection must have a reference to the core');
14672 return;
14673 }
14674
14675 var map = new Map$2();
14676 var createdElements = false;
14677
14678 if (!elements) {
14679 elements = [];
14680 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
14681 createdElements = true; // make elements from json and restore all at once later
14682
14683 var eles = [];
14684 var elesIds = new Set$1();
14685
14686 for (var i = 0, l = elements.length; i < l; i++) {
14687 var json = elements[i];
14688
14689 if (json.data == null) {
14690 json.data = {};
14691 }
14692
14693 var _data = json.data; // make sure newly created elements have valid ids
14694
14695 if (_data.id == null) {
14696 _data.id = uuid();
14697 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
14698 continue; // can't create element if prior id already exists
14699 }
14700
14701 var ele = new Element(cy, json, false);
14702 eles.push(ele);
14703 elesIds.add(_data.id);
14704 }
14705
14706 elements = eles;
14707 }
14708
14709 this.length = 0;
14710
14711 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
14712 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
14713
14714 if (element$1 == null) {
14715 continue;
14716 }
14717
14718 var id = element$1._private.data.id;
14719
14720 if (!unique || !map.has(id)) {
14721 if (unique) {
14722 map.set(id, {
14723 index: this.length,
14724 ele: element$1
14725 });
14726 }
14727
14728 this[this.length] = element$1;
14729 this.length++;
14730 }
14731 }
14732
14733 this._private = {
14734 eles: this,
14735 cy: cy,
14736
14737 get map() {
14738 if (this.lazyMap == null) {
14739 this.rebuildMap();
14740 }
14741
14742 return this.lazyMap;
14743 },
14744
14745 set map(m) {
14746 this.lazyMap = m;
14747 },
14748
14749 rebuildMap: function rebuildMap() {
14750 var m = this.lazyMap = new Map$2();
14751 var eles = this.eles;
14752
14753 for (var _i2 = 0; _i2 < eles.length; _i2++) {
14754 var _ele = eles[_i2];
14755 m.set(_ele.id(), {
14756 index: _i2,
14757 ele: _ele
14758 });
14759 }
14760 }
14761 };
14762
14763 if (unique) {
14764 this._private.map = map;
14765 } // restore the elements if we created them from json
14766
14767
14768 if (createdElements && !removed) {
14769 this.restore();
14770 }
14771 }; // Functions
14772 ////////////////////////////////////////////////////////////////////////////////////////////////////
14773 // keep the prototypes in sync (an element has the same functions as a collection)
14774 // and use elefn and elesfn as shorthands to the prototypes
14775
14776
14777 var elesfn$1 = Element.prototype = Collection.prototype = Object.create(Array.prototype);
14778
14779 elesfn$1.instanceString = function () {
14780 return 'collection';
14781 };
14782
14783 elesfn$1.spawn = function (eles, unique) {
14784 return new Collection(this.cy(), eles, unique);
14785 };
14786
14787 elesfn$1.spawnSelf = function () {
14788 return this.spawn(this);
14789 };
14790
14791 elesfn$1.cy = function () {
14792 return this._private.cy;
14793 };
14794
14795 elesfn$1.renderer = function () {
14796 return this._private.cy.renderer();
14797 };
14798
14799 elesfn$1.element = function () {
14800 return this[0];
14801 };
14802
14803 elesfn$1.collection = function () {
14804 if (collection(this)) {
14805 return this;
14806 } else {
14807 // an element
14808 return new Collection(this._private.cy, [this]);
14809 }
14810 };
14811
14812 elesfn$1.unique = function () {
14813 return new Collection(this._private.cy, this, true);
14814 };
14815
14816 elesfn$1.hasElementWithId = function (id) {
14817 id = '' + id; // id must be string
14818
14819 return this._private.map.has(id);
14820 };
14821
14822 elesfn$1.getElementById = function (id) {
14823 id = '' + id; // id must be string
14824
14825 var cy = this._private.cy;
14826
14827 var entry = this._private.map.get(id);
14828
14829 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
14830 };
14831
14832 elesfn$1.$id = elesfn$1.getElementById;
14833
14834 elesfn$1.poolIndex = function () {
14835 var cy = this._private.cy;
14836 var eles = cy._private.elements;
14837 var id = this[0]._private.data.id;
14838 return eles._private.map.get(id).index;
14839 };
14840
14841 elesfn$1.indexOf = function (ele) {
14842 var id = ele[0]._private.data.id;
14843 return this._private.map.get(id).index;
14844 };
14845
14846 elesfn$1.indexOfId = function (id) {
14847 id = '' + id; // id must be string
14848
14849 return this._private.map.get(id).index;
14850 };
14851
14852 elesfn$1.json = function (obj) {
14853 var ele = this.element();
14854 var cy = this.cy();
14855
14856 if (ele == null && obj) {
14857 return this;
14858 } // can't set to no eles
14859
14860
14861 if (ele == null) {
14862 return undefined;
14863 } // can't get from no eles
14864
14865
14866 var p = ele._private;
14867
14868 if (plainObject(obj)) {
14869 // set
14870 cy.startBatch();
14871
14872 if (obj.data) {
14873 ele.data(obj.data);
14874 var _data2 = p.data;
14875
14876 if (ele.isEdge()) {
14877 // source and target are immutable via data()
14878 var move = false;
14879 var spec = {};
14880 var src = obj.data.source;
14881 var tgt = obj.data.target;
14882
14883 if (src != null && src != _data2.source) {
14884 spec.source = '' + src; // id must be string
14885
14886 move = true;
14887 }
14888
14889 if (tgt != null && tgt != _data2.target) {
14890 spec.target = '' + tgt; // id must be string
14891
14892 move = true;
14893 }
14894
14895 if (move) {
14896 ele = ele.move(spec);
14897 }
14898 } else {
14899 // parent is immutable via data()
14900 var newParentValSpecd = ('parent' in obj.data);
14901 var parent = obj.data.parent;
14902
14903 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
14904 if (parent === undefined) {
14905 // can't set undefined imperatively, so use null
14906 parent = null;
14907 }
14908
14909 if (parent != null) {
14910 parent = '' + parent; // id must be string
14911 }
14912
14913 ele = ele.move({
14914 parent: parent
14915 });
14916 }
14917 }
14918 }
14919
14920 if (obj.position) {
14921 ele.position(obj.position);
14922 } // ignore group -- immutable
14923
14924
14925 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
14926 var obj_k = obj[k];
14927
14928 if (obj_k != null && obj_k !== p[k]) {
14929 if (obj_k) {
14930 ele[trueFnName]();
14931 } else {
14932 ele[falseFnName]();
14933 }
14934 }
14935 };
14936
14937 checkSwitch('removed', 'remove', 'restore');
14938 checkSwitch('selected', 'select', 'unselect');
14939 checkSwitch('selectable', 'selectify', 'unselectify');
14940 checkSwitch('locked', 'lock', 'unlock');
14941 checkSwitch('grabbable', 'grabify', 'ungrabify');
14942 checkSwitch('pannable', 'panify', 'unpanify');
14943
14944 if (obj.classes != null) {
14945 ele.classes(obj.classes);
14946 }
14947
14948 cy.endBatch();
14949 return this;
14950 } else if (obj === undefined) {
14951 // get
14952 var json = {
14953 data: copy(p.data),
14954 position: copy(p.position),
14955 group: p.group,
14956 removed: p.removed,
14957 selected: p.selected,
14958 selectable: p.selectable,
14959 locked: p.locked,
14960 grabbable: p.grabbable,
14961 pannable: p.pannable,
14962 classes: null
14963 };
14964 json.classes = '';
14965 var i = 0;
14966 p.classes.forEach(function (cls) {
14967 return json.classes += i++ === 0 ? cls : ' ' + cls;
14968 });
14969 return json;
14970 }
14971 };
14972
14973 elesfn$1.jsons = function () {
14974 var jsons = [];
14975
14976 for (var i = 0; i < this.length; i++) {
14977 var ele = this[i];
14978 var json = ele.json();
14979 jsons.push(json);
14980 }
14981
14982 return jsons;
14983 };
14984
14985 elesfn$1.clone = function () {
14986 var cy = this.cy();
14987 var elesArr = [];
14988
14989 for (var i = 0; i < this.length; i++) {
14990 var ele = this[i];
14991 var json = ele.json();
14992 var clone = new Element(cy, json, false); // NB no restore
14993
14994 elesArr.push(clone);
14995 }
14996
14997 return new Collection(cy, elesArr);
14998 };
14999
15000 elesfn$1.copy = elesfn$1.clone;
15001
15002 elesfn$1.restore = function () {
15003 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
15004 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
15005 var self = this;
15006 var cy = self.cy();
15007 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
15008 // restore the nodes first
15009
15010 var nodes = [];
15011 var edges = [];
15012 var elements;
15013
15014 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
15015 var ele = self[_i3];
15016
15017 if (addToPool && !ele.removed()) {
15018 // don't need to handle this ele
15019 continue;
15020 } // keep nodes first in the array and edges after
15021
15022
15023 if (ele.isNode()) {
15024 // put to front of array if node
15025 nodes.push(ele);
15026 } else {
15027 // put to end of array if edge
15028 edges.push(ele);
15029 }
15030 }
15031
15032 elements = nodes.concat(edges);
15033 var i;
15034
15035 var removeFromElements = function removeFromElements() {
15036 elements.splice(i, 1);
15037 i--;
15038 }; // now, restore each element
15039
15040
15041 for (i = 0; i < elements.length; i++) {
15042 var _ele2 = elements[i];
15043 var _private = _ele2._private;
15044 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
15045
15046 _ele2.clearTraversalCache(); // set id and validate
15047
15048
15049 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
15050 _data3.id = uuid();
15051 } else if (number$1(_data3.id)) {
15052 _data3.id = '' + _data3.id; // now it's a string
15053 } else if (emptyString(_data3.id) || !string(_data3.id)) {
15054 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
15055
15056 removeFromElements();
15057 continue;
15058 } else if (cy.hasElementWithId(_data3.id)) {
15059 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
15060
15061 removeFromElements();
15062 continue;
15063 }
15064
15065 var id = _data3.id; // id is finalised, now let's keep a ref
15066
15067 if (_ele2.isNode()) {
15068 // extra checks for nodes
15069 var pos = _private.position; // make sure the nodes have a defined position
15070
15071 if (pos.x == null) {
15072 pos.x = 0;
15073 }
15074
15075 if (pos.y == null) {
15076 pos.y = 0;
15077 }
15078 }
15079
15080 if (_ele2.isEdge()) {
15081 // extra checks for edges
15082 var edge = _ele2;
15083 var fields = ['source', 'target'];
15084 var fieldsLength = fields.length;
15085 var badSourceOrTarget = false;
15086
15087 for (var j = 0; j < fieldsLength; j++) {
15088 var field = fields[j];
15089 var val = _data3[field];
15090
15091 if (number$1(val)) {
15092 val = _data3[field] = '' + _data3[field]; // now string
15093 }
15094
15095 if (val == null || val === '') {
15096 // can't create if source or target is not defined properly
15097 error('Can not create edge `' + id + '` with unspecified ' + field);
15098 badSourceOrTarget = true;
15099 } else if (!cy.hasElementWithId(val)) {
15100 // can't create edge if one of its nodes doesn't exist
15101 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
15102 badSourceOrTarget = true;
15103 }
15104 }
15105
15106 if (badSourceOrTarget) {
15107 removeFromElements();
15108 continue;
15109 } // can't create this
15110
15111
15112 var src = cy.getElementById(_data3.source);
15113 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
15114
15115 if (src.same(tgt)) {
15116 src._private.edges.push(edge);
15117 } else {
15118 src._private.edges.push(edge);
15119
15120 tgt._private.edges.push(edge);
15121 }
15122
15123 edge._private.source = src;
15124 edge._private.target = tgt;
15125 } // if is edge
15126 // create mock ids / indexes maps for element so it can be used like collections
15127
15128
15129 _private.map = new Map$2();
15130
15131 _private.map.set(id, {
15132 ele: _ele2,
15133 index: 0
15134 });
15135
15136 _private.removed = false;
15137
15138 if (addToPool) {
15139 cy.addToPool(_ele2);
15140 }
15141 } // for each element
15142 // do compound node sanity checks
15143
15144
15145 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
15146 // each node
15147 var node = nodes[_i4];
15148 var _data4 = node._private.data;
15149
15150 if (number$1(_data4.parent)) {
15151 // then automake string
15152 _data4.parent = '' + _data4.parent;
15153 }
15154
15155 var parentId = _data4.parent;
15156 var specifiedParent = parentId != null;
15157
15158 if (specifiedParent || node._private.parent) {
15159 var parent = node._private.parent ? cy.collection().merge(node._private.parent) : cy.getElementById(parentId);
15160
15161 if (parent.empty()) {
15162 // non-existant parent; just remove it
15163 _data4.parent = undefined;
15164 } else if (parent[0].removed()) {
15165 warn('Node added with missing parent, reference to parent removed');
15166 _data4.parent = undefined;
15167 node._private.parent = null;
15168 } else {
15169 var selfAsParent = false;
15170 var ancestor = parent;
15171
15172 while (!ancestor.empty()) {
15173 if (node.same(ancestor)) {
15174 // mark self as parent and remove from data
15175 selfAsParent = true;
15176 _data4.parent = undefined; // remove parent reference
15177 // exit or we loop forever
15178
15179 break;
15180 }
15181
15182 ancestor = ancestor.parent();
15183 }
15184
15185 if (!selfAsParent) {
15186 // connect with children
15187 parent[0]._private.children.push(node);
15188
15189 node._private.parent = parent[0]; // let the core know we have a compound graph
15190
15191 cy_p.hasCompoundNodes = true;
15192 }
15193 } // else
15194
15195 } // if specified parent
15196
15197 } // for each node
15198
15199
15200 if (elements.length > 0) {
15201 var restored = elements.length === self.length ? self : new Collection(cy, elements);
15202
15203 for (var _i5 = 0; _i5 < restored.length; _i5++) {
15204 var _ele3 = restored[_i5];
15205
15206 if (_ele3.isNode()) {
15207 continue;
15208 } // adding an edge invalidates the traversal caches for the parallel edges
15209
15210
15211 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
15212
15213
15214 _ele3.source().clearTraversalCache();
15215
15216 _ele3.target().clearTraversalCache();
15217 }
15218
15219 var toUpdateStyle;
15220
15221 if (cy_p.hasCompoundNodes) {
15222 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
15223 } else {
15224 toUpdateStyle = restored;
15225 }
15226
15227 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
15228
15229 if (notifyRenderer) {
15230 restored.emitAndNotify('add');
15231 } else if (addToPool) {
15232 restored.emit('add');
15233 }
15234 }
15235
15236 return self; // chainability
15237 };
15238
15239 elesfn$1.removed = function () {
15240 var ele = this[0];
15241 return ele && ele._private.removed;
15242 };
15243
15244 elesfn$1.inside = function () {
15245 var ele = this[0];
15246 return ele && !ele._private.removed;
15247 };
15248
15249 elesfn$1.remove = function () {
15250 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
15251 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
15252 var self = this;
15253 var elesToRemove = [];
15254 var elesToRemoveIds = {};
15255 var cy = self._private.cy; // add connected edges
15256
15257 function addConnectedEdges(node) {
15258 var edges = node._private.edges;
15259
15260 for (var i = 0; i < edges.length; i++) {
15261 add(edges[i]);
15262 }
15263 } // add descendant nodes
15264
15265
15266 function addChildren(node) {
15267 var children = node._private.children;
15268
15269 for (var i = 0; i < children.length; i++) {
15270 add(children[i]);
15271 }
15272 }
15273
15274 function add(ele) {
15275 var alreadyAdded = elesToRemoveIds[ele.id()];
15276
15277 if (removeFromPool && ele.removed() || alreadyAdded) {
15278 return;
15279 } else {
15280 elesToRemoveIds[ele.id()] = true;
15281 }
15282
15283 if (ele.isNode()) {
15284 elesToRemove.push(ele); // nodes are removed last
15285
15286 addConnectedEdges(ele);
15287 addChildren(ele);
15288 } else {
15289 elesToRemove.unshift(ele); // edges are removed first
15290 }
15291 } // make the list of elements to remove
15292 // (may be removing more than specified due to connected edges etc)
15293
15294
15295 for (var i = 0, l = self.length; i < l; i++) {
15296 var ele = self[i];
15297 add(ele);
15298 }
15299
15300 function removeEdgeRef(node, edge) {
15301 var connectedEdges = node._private.edges;
15302 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
15303
15304 node.clearTraversalCache();
15305 }
15306
15307 function removeParallelRef(pllEdge) {
15308 // removing an edge invalidates the traversal caches for the parallel edges
15309 pllEdge.clearTraversalCache();
15310 }
15311
15312 var alteredParents = [];
15313 alteredParents.ids = {};
15314
15315 function removeChildRef(parent, ele) {
15316 ele = ele[0];
15317 parent = parent[0];
15318 var children = parent._private.children;
15319 var pid = parent.id();
15320 removeFromArray(children, ele); // remove parent => child ref
15321
15322 ele._private.parent = null; // remove child => parent ref
15323
15324 if (!alteredParents.ids[pid]) {
15325 alteredParents.ids[pid] = true;
15326 alteredParents.push(parent);
15327 }
15328 }
15329
15330 self.dirtyCompoundBoundsCache();
15331
15332 if (removeFromPool) {
15333 cy.removeFromPool(elesToRemove); // remove from core pool
15334 }
15335
15336 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
15337 var _ele4 = elesToRemove[_i6];
15338
15339 if (_ele4.isEdge()) {
15340 // remove references to this edge in its connected nodes
15341 var src = _ele4.source()[0];
15342
15343 var tgt = _ele4.target()[0];
15344
15345 removeEdgeRef(src, _ele4);
15346 removeEdgeRef(tgt, _ele4);
15347
15348 var pllEdges = _ele4.parallelEdges();
15349
15350 for (var j = 0; j < pllEdges.length; j++) {
15351 var pllEdge = pllEdges[j];
15352 removeParallelRef(pllEdge);
15353
15354 if (pllEdge.isBundledBezier()) {
15355 pllEdge.dirtyBoundingBoxCache();
15356 }
15357 }
15358 } else {
15359 // remove reference to parent
15360 var parent = _ele4.parent();
15361
15362 if (parent.length !== 0) {
15363 removeChildRef(parent, _ele4);
15364 }
15365 }
15366
15367 if (removeFromPool) {
15368 // mark as removed
15369 _ele4._private.removed = true;
15370 }
15371 } // check to see if we have a compound graph or not
15372
15373
15374 var elesStillInside = cy._private.elements;
15375 cy._private.hasCompoundNodes = false;
15376
15377 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
15378 var _ele5 = elesStillInside[_i7];
15379
15380 if (_ele5.isParent()) {
15381 cy._private.hasCompoundNodes = true;
15382 break;
15383 }
15384 }
15385
15386 var removedElements = new Collection(this.cy(), elesToRemove);
15387
15388 if (removedElements.size() > 0) {
15389 // must manually notify since trigger won't do this automatically once removed
15390 if (notifyRenderer) {
15391 removedElements.emitAndNotify('remove');
15392 } else if (removeFromPool) {
15393 removedElements.emit('remove');
15394 }
15395 } // the parents who were modified by the removal need their style updated
15396
15397
15398 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
15399 var _ele6 = alteredParents[_i8];
15400
15401 if (!removeFromPool || !_ele6.removed()) {
15402 _ele6.updateStyle();
15403 }
15404 }
15405
15406 return removedElements;
15407 };
15408
15409 elesfn$1.move = function (struct) {
15410 var cy = this._private.cy;
15411 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
15412 // (our calls to remove/restore do not remove from the graph or make events)
15413
15414 var notifyRenderer = false;
15415 var modifyPool = false;
15416
15417 var toString = function toString(id) {
15418 return id == null ? id : '' + id;
15419 }; // id must be string
15420
15421
15422 if (struct.source !== undefined || struct.target !== undefined) {
15423 var srcId = toString(struct.source);
15424 var tgtId = toString(struct.target);
15425 var srcExists = srcId != null && cy.hasElementWithId(srcId);
15426 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
15427
15428 if (srcExists || tgtExists) {
15429 cy.batch(function () {
15430 // avoid duplicate style updates
15431 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
15432
15433 eles.emitAndNotify('moveout');
15434
15435 for (var i = 0; i < eles.length; i++) {
15436 var ele = eles[i];
15437 var _data5 = ele._private.data;
15438
15439 if (ele.isEdge()) {
15440 if (srcExists) {
15441 _data5.source = srcId;
15442 }
15443
15444 if (tgtExists) {
15445 _data5.target = tgtId;
15446 }
15447 }
15448 }
15449
15450 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
15451 });
15452 eles.emitAndNotify('move');
15453 }
15454 } else if (struct.parent !== undefined) {
15455 // move node to new parent
15456 var parentId = toString(struct.parent);
15457 var parentExists = parentId === null || cy.hasElementWithId(parentId);
15458
15459 if (parentExists) {
15460 var pidToAssign = parentId === null ? undefined : parentId;
15461 cy.batch(function () {
15462 // avoid duplicate style updates
15463 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
15464
15465 updated.emitAndNotify('moveout');
15466
15467 for (var i = 0; i < eles.length; i++) {
15468 var ele = eles[i];
15469 var _data6 = ele._private.data;
15470
15471 if (ele.isNode()) {
15472 _data6.parent = pidToAssign;
15473 }
15474 }
15475
15476 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
15477 });
15478 eles.emitAndNotify('move');
15479 }
15480 }
15481
15482 return this;
15483 };
15484
15485 [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) {
15486 extend(elesfn$1, props);
15487 });
15488
15489 var corefn$9 = {
15490 add: function add(opts) {
15491 var elements;
15492 var cy = this; // add the elements
15493
15494 if (elementOrCollection(opts)) {
15495 var eles = opts;
15496
15497 if (eles._private.cy === cy) {
15498 // same instance => just restore
15499 elements = eles.restore();
15500 } else {
15501 // otherwise, copy from json
15502 var jsons = [];
15503
15504 for (var i = 0; i < eles.length; i++) {
15505 var ele = eles[i];
15506 jsons.push(ele.json());
15507 }
15508
15509 elements = new Collection(cy, jsons);
15510 }
15511 } // specify an array of options
15512 else if (array(opts)) {
15513 var _jsons = opts;
15514 elements = new Collection(cy, _jsons);
15515 } // specify via opts.nodes and opts.edges
15516 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
15517 var elesByGroup = opts;
15518 var _jsons2 = [];
15519 var grs = ['nodes', 'edges'];
15520
15521 for (var _i = 0, il = grs.length; _i < il; _i++) {
15522 var group = grs[_i];
15523 var elesArray = elesByGroup[group];
15524
15525 if (array(elesArray)) {
15526 for (var j = 0, jl = elesArray.length; j < jl; j++) {
15527 var json = extend({
15528 group: group
15529 }, elesArray[j]);
15530
15531 _jsons2.push(json);
15532 }
15533 }
15534 }
15535
15536 elements = new Collection(cy, _jsons2);
15537 } // specify options for one element
15538 else {
15539 var _json = opts;
15540 elements = new Element(cy, _json).collection();
15541 }
15542
15543 return elements;
15544 },
15545 remove: function remove(collection) {
15546 if (elementOrCollection(collection)) ; else if (string(collection)) {
15547 var selector = collection;
15548 collection = this.$(selector);
15549 }
15550
15551 return collection.remove();
15552 }
15553 };
15554
15555 /* global Float32Array */
15556
15557 /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
15558 function generateCubicBezier(mX1, mY1, mX2, mY2) {
15559 var NEWTON_ITERATIONS = 4,
15560 NEWTON_MIN_SLOPE = 0.001,
15561 SUBDIVISION_PRECISION = 0.0000001,
15562 SUBDIVISION_MAX_ITERATIONS = 10,
15563 kSplineTableSize = 11,
15564 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
15565 float32ArraySupported = typeof Float32Array !== 'undefined';
15566 /* Must contain four arguments. */
15567
15568 if (arguments.length !== 4) {
15569 return false;
15570 }
15571 /* Arguments must be numbers. */
15572
15573
15574 for (var i = 0; i < 4; ++i) {
15575 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
15576 return false;
15577 }
15578 }
15579 /* X values must be in the [0, 1] range. */
15580
15581
15582 mX1 = Math.min(mX1, 1);
15583 mX2 = Math.min(mX2, 1);
15584 mX1 = Math.max(mX1, 0);
15585 mX2 = Math.max(mX2, 0);
15586 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
15587
15588 function A(aA1, aA2) {
15589 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
15590 }
15591
15592 function B(aA1, aA2) {
15593 return 3.0 * aA2 - 6.0 * aA1;
15594 }
15595
15596 function C(aA1) {
15597 return 3.0 * aA1;
15598 }
15599
15600 function calcBezier(aT, aA1, aA2) {
15601 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
15602 }
15603
15604 function getSlope(aT, aA1, aA2) {
15605 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
15606 }
15607
15608 function newtonRaphsonIterate(aX, aGuessT) {
15609 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
15610 var currentSlope = getSlope(aGuessT, mX1, mX2);
15611
15612 if (currentSlope === 0.0) {
15613 return aGuessT;
15614 }
15615
15616 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
15617 aGuessT -= currentX / currentSlope;
15618 }
15619
15620 return aGuessT;
15621 }
15622
15623 function calcSampleValues() {
15624 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
15625 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
15626 }
15627 }
15628
15629 function binarySubdivide(aX, aA, aB) {
15630 var currentX,
15631 currentT,
15632 i = 0;
15633
15634 do {
15635 currentT = aA + (aB - aA) / 2.0;
15636 currentX = calcBezier(currentT, mX1, mX2) - aX;
15637
15638 if (currentX > 0.0) {
15639 aB = currentT;
15640 } else {
15641 aA = currentT;
15642 }
15643 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
15644
15645 return currentT;
15646 }
15647
15648 function getTForX(aX) {
15649 var intervalStart = 0.0,
15650 currentSample = 1,
15651 lastSample = kSplineTableSize - 1;
15652
15653 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
15654 intervalStart += kSampleStepSize;
15655 }
15656
15657 --currentSample;
15658 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
15659 guessForT = intervalStart + dist * kSampleStepSize,
15660 initialSlope = getSlope(guessForT, mX1, mX2);
15661
15662 if (initialSlope >= NEWTON_MIN_SLOPE) {
15663 return newtonRaphsonIterate(aX, guessForT);
15664 } else if (initialSlope === 0.0) {
15665 return guessForT;
15666 } else {
15667 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
15668 }
15669 }
15670
15671 var _precomputed = false;
15672
15673 function precompute() {
15674 _precomputed = true;
15675
15676 if (mX1 !== mY1 || mX2 !== mY2) {
15677 calcSampleValues();
15678 }
15679 }
15680
15681 var f = function f(aX) {
15682 if (!_precomputed) {
15683 precompute();
15684 }
15685
15686 if (mX1 === mY1 && mX2 === mY2) {
15687 return aX;
15688 }
15689
15690 if (aX === 0) {
15691 return 0;
15692 }
15693
15694 if (aX === 1) {
15695 return 1;
15696 }
15697
15698 return calcBezier(getTForX(aX), mY1, mY2);
15699 };
15700
15701 f.getControlPoints = function () {
15702 return [{
15703 x: mX1,
15704 y: mY1
15705 }, {
15706 x: mX2,
15707 y: mY2
15708 }];
15709 };
15710
15711 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
15712
15713 f.toString = function () {
15714 return str;
15715 };
15716
15717 return f;
15718 }
15719
15720 /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
15721
15722 /* 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
15723 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
15724 var generateSpringRK4 = function () {
15725 function springAccelerationForState(state) {
15726 return -state.tension * state.x - state.friction * state.v;
15727 }
15728
15729 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
15730 var state = {
15731 x: initialState.x + derivative.dx * dt,
15732 v: initialState.v + derivative.dv * dt,
15733 tension: initialState.tension,
15734 friction: initialState.friction
15735 };
15736 return {
15737 dx: state.v,
15738 dv: springAccelerationForState(state)
15739 };
15740 }
15741
15742 function springIntegrateState(state, dt) {
15743 var a = {
15744 dx: state.v,
15745 dv: springAccelerationForState(state)
15746 },
15747 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
15748 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
15749 d = springEvaluateStateWithDerivative(state, dt, c),
15750 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
15751 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
15752 state.x = state.x + dxdt * dt;
15753 state.v = state.v + dvdt * dt;
15754 return state;
15755 }
15756
15757 return function springRK4Factory(tension, friction, duration) {
15758 var initState = {
15759 x: -1,
15760 v: 0,
15761 tension: null,
15762 friction: null
15763 },
15764 path = [0],
15765 time_lapsed = 0,
15766 tolerance = 1 / 10000,
15767 DT = 16 / 1000,
15768 have_duration,
15769 dt,
15770 last_state;
15771 tension = parseFloat(tension) || 500;
15772 friction = parseFloat(friction) || 20;
15773 duration = duration || null;
15774 initState.tension = tension;
15775 initState.friction = friction;
15776 have_duration = duration !== null;
15777 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
15778
15779 if (have_duration) {
15780 /* Run the simulation without a duration. */
15781 time_lapsed = springRK4Factory(tension, friction);
15782 /* Compute the adjusted time delta. */
15783
15784 dt = time_lapsed / duration * DT;
15785 } else {
15786 dt = DT;
15787 }
15788
15789 for (;;) {
15790 /* Next/step function .*/
15791 last_state = springIntegrateState(last_state || initState, dt);
15792 /* Store the position. */
15793
15794 path.push(1 + last_state.x);
15795 time_lapsed += 16;
15796 /* If the change threshold is reached, break. */
15797
15798 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
15799 break;
15800 }
15801 }
15802 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
15803 computed path and returns a snapshot of the position according to a given percentComplete. */
15804
15805
15806 return !have_duration ? time_lapsed : function (percentComplete) {
15807 return path[percentComplete * (path.length - 1) | 0];
15808 };
15809 };
15810 }();
15811
15812 var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
15813 var bezier = generateCubicBezier(t1, p1, t2, p2);
15814 return function (start, end, percent) {
15815 return start + (end - start) * bezier(percent);
15816 };
15817 };
15818
15819 var easings = {
15820 'linear': function linear(start, end, percent) {
15821 return start + (end - start) * percent;
15822 },
15823 // default easings
15824 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
15825 'ease-in': cubicBezier(0.42, 0, 1, 1),
15826 'ease-out': cubicBezier(0, 0, 0.58, 1),
15827 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
15828 // sine
15829 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
15830 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
15831 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
15832 // quad
15833 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
15834 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
15835 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
15836 // cubic
15837 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
15838 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
15839 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
15840 // quart
15841 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
15842 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
15843 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
15844 // quint
15845 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
15846 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
15847 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
15848 // expo
15849 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
15850 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
15851 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
15852 // circ
15853 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
15854 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
15855 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
15856 // user param easings...
15857 'spring': function spring(tension, friction, duration) {
15858 if (duration === 0) {
15859 // can't get a spring w/ duration 0
15860 return easings.linear; // duration 0 => jump to end so impl doesn't matter
15861 }
15862
15863 var spring = generateSpringRK4(tension, friction, duration);
15864 return function (start, end, percent) {
15865 return start + (end - start) * spring(percent);
15866 };
15867 },
15868 'cubic-bezier': cubicBezier
15869 };
15870
15871 function getEasedValue(type, start, end, percent, easingFn) {
15872 if (percent === 1) {
15873 return end;
15874 }
15875
15876 if (start === end) {
15877 return end;
15878 }
15879
15880 var val = easingFn(start, end, percent);
15881
15882 if (type == null) {
15883 return val;
15884 }
15885
15886 if (type.roundValue || type.color) {
15887 val = Math.round(val);
15888 }
15889
15890 if (type.min !== undefined) {
15891 val = Math.max(val, type.min);
15892 }
15893
15894 if (type.max !== undefined) {
15895 val = Math.min(val, type.max);
15896 }
15897
15898 return val;
15899 }
15900
15901 function getValue(prop, spec) {
15902 if (prop.pfValue != null || prop.value != null) {
15903 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
15904 return prop.pfValue;
15905 } else {
15906 return prop.value;
15907 }
15908 } else {
15909 return prop;
15910 }
15911 }
15912
15913 function ease(startProp, endProp, percent, easingFn, propSpec) {
15914 var type = propSpec != null ? propSpec.type : null;
15915
15916 if (percent < 0) {
15917 percent = 0;
15918 } else if (percent > 1) {
15919 percent = 1;
15920 }
15921
15922 var start = getValue(startProp, propSpec);
15923 var end = getValue(endProp, propSpec);
15924
15925 if (number$1(start) && number$1(end)) {
15926 return getEasedValue(type, start, end, percent, easingFn);
15927 } else if (array(start) && array(end)) {
15928 var easedArr = [];
15929
15930 for (var i = 0; i < end.length; i++) {
15931 var si = start[i];
15932 var ei = end[i];
15933
15934 if (si != null && ei != null) {
15935 var val = getEasedValue(type, si, ei, percent, easingFn);
15936 easedArr.push(val);
15937 } else {
15938 easedArr.push(ei);
15939 }
15940 }
15941
15942 return easedArr;
15943 }
15944
15945 return undefined;
15946 }
15947
15948 function step$1(self, ani, now, isCore) {
15949 var isEles = !isCore;
15950 var _p = self._private;
15951 var ani_p = ani._private;
15952 var pEasing = ani_p.easing;
15953 var startTime = ani_p.startTime;
15954 var cy = isCore ? self : self.cy();
15955 var style = cy.style();
15956
15957 if (!ani_p.easingImpl) {
15958 if (pEasing == null) {
15959 // use default
15960 ani_p.easingImpl = easings['linear'];
15961 } else {
15962 // then define w/ name
15963 var easingVals;
15964
15965 if (string(pEasing)) {
15966 var easingProp = style.parse('transition-timing-function', pEasing);
15967 easingVals = easingProp.value;
15968 } else {
15969 // then assume preparsed array
15970 easingVals = pEasing;
15971 }
15972
15973 var name, args;
15974
15975 if (string(easingVals)) {
15976 name = easingVals;
15977 args = [];
15978 } else {
15979 name = easingVals[1];
15980 args = easingVals.slice(2).map(function (n) {
15981 return +n;
15982 });
15983 }
15984
15985 if (args.length > 0) {
15986 // create with args
15987 if (name === 'spring') {
15988 args.push(ani_p.duration); // need duration to generate spring
15989 }
15990
15991 ani_p.easingImpl = easings[name].apply(null, args);
15992 } else {
15993 // static impl by name
15994 ani_p.easingImpl = easings[name];
15995 }
15996 }
15997 }
15998
15999 var easing = ani_p.easingImpl;
16000 var percent;
16001
16002 if (ani_p.duration === 0) {
16003 percent = 1;
16004 } else {
16005 percent = (now - startTime) / ani_p.duration;
16006 }
16007
16008 if (ani_p.applying) {
16009 percent = ani_p.progress;
16010 }
16011
16012 if (percent < 0) {
16013 percent = 0;
16014 } else if (percent > 1) {
16015 percent = 1;
16016 }
16017
16018 if (ani_p.delay == null) {
16019 // then update
16020 var startPos = ani_p.startPosition;
16021 var endPos = ani_p.position;
16022
16023 if (endPos && isEles && !self.locked()) {
16024 var newPos = {};
16025
16026 if (valid(startPos.x, endPos.x)) {
16027 newPos.x = ease(startPos.x, endPos.x, percent, easing);
16028 }
16029
16030 if (valid(startPos.y, endPos.y)) {
16031 newPos.y = ease(startPos.y, endPos.y, percent, easing);
16032 }
16033
16034 self.position(newPos);
16035 }
16036
16037 var startPan = ani_p.startPan;
16038 var endPan = ani_p.pan;
16039 var pan = _p.pan;
16040 var animatingPan = endPan != null && isCore;
16041
16042 if (animatingPan) {
16043 if (valid(startPan.x, endPan.x)) {
16044 pan.x = ease(startPan.x, endPan.x, percent, easing);
16045 }
16046
16047 if (valid(startPan.y, endPan.y)) {
16048 pan.y = ease(startPan.y, endPan.y, percent, easing);
16049 }
16050
16051 self.emit('pan');
16052 }
16053
16054 var startZoom = ani_p.startZoom;
16055 var endZoom = ani_p.zoom;
16056 var animatingZoom = endZoom != null && isCore;
16057
16058 if (animatingZoom) {
16059 if (valid(startZoom, endZoom)) {
16060 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
16061 }
16062
16063 self.emit('zoom');
16064 }
16065
16066 if (animatingPan || animatingZoom) {
16067 self.emit('viewport');
16068 }
16069
16070 var props = ani_p.style;
16071
16072 if (props && props.length > 0 && isEles) {
16073 for (var i = 0; i < props.length; i++) {
16074 var prop = props[i];
16075 var _name = prop.name;
16076 var end = prop;
16077 var start = ani_p.startStyle[_name];
16078 var propSpec = style.properties[start.name];
16079 var easedVal = ease(start, end, percent, easing, propSpec);
16080 style.overrideBypass(self, _name, easedVal);
16081 } // for props
16082
16083
16084 self.emit('style');
16085 } // if
16086
16087 }
16088
16089 ani_p.progress = percent;
16090 return percent;
16091 }
16092
16093 function valid(start, end) {
16094 if (start == null || end == null) {
16095 return false;
16096 }
16097
16098 if (number$1(start) && number$1(end)) {
16099 return true;
16100 } else if (start && end) {
16101 return true;
16102 }
16103
16104 return false;
16105 }
16106
16107 function startAnimation(self, ani, now, isCore) {
16108 var ani_p = ani._private;
16109 ani_p.started = true;
16110 ani_p.startTime = now - ani_p.progress * ani_p.duration;
16111 }
16112
16113 function stepAll(now, cy) {
16114 var eles = cy._private.aniEles;
16115 var doneEles = [];
16116
16117 function stepOne(ele, isCore) {
16118 var _p = ele._private;
16119 var current = _p.animation.current;
16120 var queue = _p.animation.queue;
16121 var ranAnis = false; // if nothing currently animating, get something from the queue
16122
16123 if (current.length === 0) {
16124 var next = queue.shift();
16125
16126 if (next) {
16127 current.push(next);
16128 }
16129 }
16130
16131 var callbacks = function callbacks(_callbacks) {
16132 for (var j = _callbacks.length - 1; j >= 0; j--) {
16133 var cb = _callbacks[j];
16134 cb();
16135 }
16136
16137 _callbacks.splice(0, _callbacks.length);
16138 }; // step and remove if done
16139
16140
16141 for (var i = current.length - 1; i >= 0; i--) {
16142 var ani = current[i];
16143 var ani_p = ani._private;
16144
16145 if (ani_p.stopped) {
16146 current.splice(i, 1);
16147 ani_p.hooked = false;
16148 ani_p.playing = false;
16149 ani_p.started = false;
16150 callbacks(ani_p.frames);
16151 continue;
16152 }
16153
16154 if (!ani_p.playing && !ani_p.applying) {
16155 continue;
16156 } // an apply() while playing shouldn't do anything
16157
16158
16159 if (ani_p.playing && ani_p.applying) {
16160 ani_p.applying = false;
16161 }
16162
16163 if (!ani_p.started) {
16164 startAnimation(ele, ani, now);
16165 }
16166
16167 step$1(ele, ani, now, isCore);
16168
16169 if (ani_p.applying) {
16170 ani_p.applying = false;
16171 }
16172
16173 callbacks(ani_p.frames);
16174
16175 if (ani_p.step != null) {
16176 ani_p.step(now);
16177 }
16178
16179 if (ani.completed()) {
16180 current.splice(i, 1);
16181 ani_p.hooked = false;
16182 ani_p.playing = false;
16183 ani_p.started = false;
16184 callbacks(ani_p.completes);
16185 }
16186
16187 ranAnis = true;
16188 }
16189
16190 if (!isCore && current.length === 0 && queue.length === 0) {
16191 doneEles.push(ele);
16192 }
16193
16194 return ranAnis;
16195 } // stepElement
16196 // handle all eles
16197
16198
16199 var ranEleAni = false;
16200
16201 for (var e = 0; e < eles.length; e++) {
16202 var ele = eles[e];
16203 var handledThisEle = stepOne(ele);
16204 ranEleAni = ranEleAni || handledThisEle;
16205 } // each element
16206
16207
16208 var ranCoreAni = stepOne(cy, true); // notify renderer
16209
16210 if (ranEleAni || ranCoreAni) {
16211 if (eles.length > 0) {
16212 cy.notify('draw', eles);
16213 } else {
16214 cy.notify('draw');
16215 }
16216 } // remove elements from list of currently animating if its queues are empty
16217
16218
16219 eles.unmerge(doneEles);
16220 cy.emit('step');
16221 } // stepAll
16222
16223 var corefn$8 = {
16224 // pull in animation functions
16225 animate: define.animate(),
16226 animation: define.animation(),
16227 animated: define.animated(),
16228 clearQueue: define.clearQueue(),
16229 delay: define.delay(),
16230 delayAnimation: define.delayAnimation(),
16231 stop: define.stop(),
16232 addToAnimationPool: function addToAnimationPool(eles) {
16233 var cy = this;
16234
16235 if (!cy.styleEnabled()) {
16236 return;
16237 } // save cycles when no style used
16238
16239
16240 cy._private.aniEles.merge(eles);
16241 },
16242 stopAnimationLoop: function stopAnimationLoop() {
16243 this._private.animationsRunning = false;
16244 },
16245 startAnimationLoop: function startAnimationLoop() {
16246 var cy = this;
16247 cy._private.animationsRunning = true;
16248
16249 if (!cy.styleEnabled()) {
16250 return;
16251 } // save cycles when no style used
16252 // NB the animation loop will exec in headless environments if style enabled
16253 // and explicit cy.destroy() is necessary to stop the loop
16254
16255
16256 function headlessStep() {
16257 if (!cy._private.animationsRunning) {
16258 return;
16259 }
16260
16261 requestAnimationFrame(function animationStep(now) {
16262 stepAll(now, cy);
16263 headlessStep();
16264 });
16265 }
16266
16267 var renderer = cy.renderer();
16268
16269 if (renderer && renderer.beforeRender) {
16270 // let the renderer schedule animations
16271 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
16272 stepAll(now, cy);
16273 }, renderer.beforeRenderPriorities.animations);
16274 } else {
16275 // manage the animation loop ourselves
16276 headlessStep(); // first call
16277 }
16278 }
16279 };
16280
16281 var emitterOptions = {
16282 qualifierCompare: function qualifierCompare(selector1, selector2) {
16283 if (selector1 == null || selector2 == null) {
16284 return selector1 == null && selector2 == null;
16285 } else {
16286 return selector1.sameText(selector2);
16287 }
16288 },
16289 eventMatches: function eventMatches(cy, listener, eventObj) {
16290 var selector = listener.qualifier;
16291
16292 if (selector != null) {
16293 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
16294 }
16295
16296 return true;
16297 },
16298 addEventFields: function addEventFields(cy, evt) {
16299 evt.cy = cy;
16300 evt.target = cy;
16301 },
16302 callbackContext: function callbackContext(cy, listener, eventObj) {
16303 return listener.qualifier != null ? eventObj.target : cy;
16304 }
16305 };
16306
16307 var argSelector = function argSelector(arg) {
16308 if (string(arg)) {
16309 return new Selector(arg);
16310 } else {
16311 return arg;
16312 }
16313 };
16314
16315 var elesfn = {
16316 createEmitter: function createEmitter() {
16317 var _p = this._private;
16318
16319 if (!_p.emitter) {
16320 _p.emitter = new Emitter(emitterOptions, this);
16321 }
16322
16323 return this;
16324 },
16325 emitter: function emitter() {
16326 return this._private.emitter;
16327 },
16328 on: function on(events, selector, callback) {
16329 this.emitter().on(events, argSelector(selector), callback);
16330 return this;
16331 },
16332 removeListener: function removeListener(events, selector, callback) {
16333 this.emitter().removeListener(events, argSelector(selector), callback);
16334 return this;
16335 },
16336 removeAllListeners: function removeAllListeners() {
16337 this.emitter().removeAllListeners();
16338 return this;
16339 },
16340 one: function one(events, selector, callback) {
16341 this.emitter().one(events, argSelector(selector), callback);
16342 return this;
16343 },
16344 once: function once(events, selector, callback) {
16345 this.emitter().one(events, argSelector(selector), callback);
16346 return this;
16347 },
16348 emit: function emit(events, extraParams) {
16349 this.emitter().emit(events, extraParams);
16350 return this;
16351 },
16352 emitAndNotify: function emitAndNotify(event, eles) {
16353 this.emit(event);
16354 this.notify(event, eles);
16355 return this;
16356 }
16357 };
16358 define.eventAliasesOn(elesfn);
16359
16360 var corefn$7 = {
16361 png: function png(options) {
16362 var renderer = this._private.renderer;
16363 options = options || {};
16364 return renderer.png(options);
16365 },
16366 jpg: function jpg(options) {
16367 var renderer = this._private.renderer;
16368 options = options || {};
16369 options.bg = options.bg || '#fff';
16370 return renderer.jpg(options);
16371 }
16372 };
16373 corefn$7.jpeg = corefn$7.jpg;
16374
16375 var corefn$6 = {
16376 layout: function layout(options) {
16377 var cy = this;
16378
16379 if (options == null) {
16380 error('Layout options must be specified to make a layout');
16381 return;
16382 }
16383
16384 if (options.name == null) {
16385 error('A `name` must be specified to make a layout');
16386 return;
16387 }
16388
16389 var name = options.name;
16390 var Layout = cy.extension('layout', name);
16391
16392 if (Layout == null) {
16393 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
16394 return;
16395 }
16396
16397 var eles;
16398
16399 if (string(options.eles)) {
16400 eles = cy.$(options.eles);
16401 } else {
16402 eles = options.eles != null ? options.eles : cy.$();
16403 }
16404
16405 var layout = new Layout(extend({}, options, {
16406 cy: cy,
16407 eles: eles
16408 }));
16409 return layout;
16410 }
16411 };
16412 corefn$6.createLayout = corefn$6.makeLayout = corefn$6.layout;
16413
16414 var corefn$5 = {
16415 notify: function notify(eventName, eventEles) {
16416 var _p = this._private;
16417
16418 if (this.batching()) {
16419 _p.batchNotifications = _p.batchNotifications || {};
16420 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
16421
16422 if (eventEles != null) {
16423 eles.merge(eventEles);
16424 }
16425
16426 return; // notifications are disabled during batching
16427 }
16428
16429 if (!_p.notificationsEnabled) {
16430 return;
16431 } // exit on disabled
16432
16433
16434 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
16435
16436 if (this.destroyed() || !renderer) {
16437 return;
16438 }
16439
16440 renderer.notify(eventName, eventEles);
16441 },
16442 notifications: function notifications(bool) {
16443 var p = this._private;
16444
16445 if (bool === undefined) {
16446 return p.notificationsEnabled;
16447 } else {
16448 p.notificationsEnabled = bool ? true : false;
16449 }
16450
16451 return this;
16452 },
16453 noNotifications: function noNotifications(callback) {
16454 this.notifications(false);
16455 callback();
16456 this.notifications(true);
16457 },
16458 batching: function batching() {
16459 return this._private.batchCount > 0;
16460 },
16461 startBatch: function startBatch() {
16462 var _p = this._private;
16463
16464 if (_p.batchCount == null) {
16465 _p.batchCount = 0;
16466 }
16467
16468 if (_p.batchCount === 0) {
16469 _p.batchStyleEles = this.collection();
16470 _p.batchNotifications = {};
16471 }
16472
16473 _p.batchCount++;
16474 return this;
16475 },
16476 endBatch: function endBatch() {
16477 var _p = this._private;
16478
16479 if (_p.batchCount === 0) {
16480 return this;
16481 }
16482
16483 _p.batchCount--;
16484
16485 if (_p.batchCount === 0) {
16486 // update style for dirty eles
16487 _p.batchStyleEles.updateStyle();
16488
16489 var renderer = this.renderer(); // notify the renderer of queued eles and event types
16490
16491 Object.keys(_p.batchNotifications).forEach(function (eventName) {
16492 var eles = _p.batchNotifications[eventName];
16493
16494 if (eles.empty()) {
16495 renderer.notify(eventName);
16496 } else {
16497 renderer.notify(eventName, eles);
16498 }
16499 });
16500 }
16501
16502 return this;
16503 },
16504 batch: function batch(callback) {
16505 this.startBatch();
16506 callback();
16507 this.endBatch();
16508 return this;
16509 },
16510 // for backwards compatibility
16511 batchData: function batchData(map) {
16512 var cy = this;
16513 return this.batch(function () {
16514 var ids = Object.keys(map);
16515
16516 for (var i = 0; i < ids.length; i++) {
16517 var id = ids[i];
16518 var data = map[id];
16519 var ele = cy.getElementById(id);
16520 ele.data(data);
16521 }
16522 });
16523 }
16524 };
16525
16526 var rendererDefaults = defaults$g({
16527 hideEdgesOnViewport: false,
16528 textureOnViewport: false,
16529 motionBlur: false,
16530 motionBlurOpacity: 0.05,
16531 pixelRatio: undefined,
16532 desktopTapThreshold: 4,
16533 touchTapThreshold: 8,
16534 wheelSensitivity: 1,
16535 debug: false,
16536 showFps: false
16537 });
16538 var corefn$4 = {
16539 renderTo: function renderTo(context, zoom, pan, pxRatio) {
16540 var r = this._private.renderer;
16541 r.renderTo(context, zoom, pan, pxRatio);
16542 return this;
16543 },
16544 renderer: function renderer() {
16545 return this._private.renderer;
16546 },
16547 forceRender: function forceRender() {
16548 this.notify('draw');
16549 return this;
16550 },
16551 resize: function resize() {
16552 this.invalidateSize();
16553 this.emitAndNotify('resize');
16554 return this;
16555 },
16556 initRenderer: function initRenderer(options) {
16557 var cy = this;
16558 var RendererProto = cy.extension('renderer', options.name);
16559
16560 if (RendererProto == null) {
16561 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
16562 return;
16563 }
16564
16565 if (options.wheelSensitivity !== undefined) {
16566 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.");
16567 }
16568
16569 var rOpts = rendererDefaults(options);
16570 rOpts.cy = cy;
16571 cy._private.renderer = new RendererProto(rOpts);
16572 this.notify('init');
16573 },
16574 destroyRenderer: function destroyRenderer() {
16575 var cy = this;
16576 cy.notify('destroy'); // destroy the renderer
16577
16578 var domEle = cy.container();
16579
16580 if (domEle) {
16581 domEle._cyreg = null;
16582
16583 while (domEle.childNodes.length > 0) {
16584 domEle.removeChild(domEle.childNodes[0]);
16585 }
16586 }
16587
16588 cy._private.renderer = null; // to be extra safe, remove the ref
16589
16590 cy.mutableElements().forEach(function (ele) {
16591 var _p = ele._private;
16592 _p.rscratch = {};
16593 _p.rstyle = {};
16594 _p.animation.current = [];
16595 _p.animation.queue = [];
16596 });
16597 },
16598 onRender: function onRender(fn) {
16599 return this.on('render', fn);
16600 },
16601 offRender: function offRender(fn) {
16602 return this.off('render', fn);
16603 }
16604 };
16605 corefn$4.invalidateDimensions = corefn$4.resize;
16606
16607 var corefn$3 = {
16608 // get a collection
16609 // - empty collection on no args
16610 // - collection of elements in the graph on selector arg
16611 // - guarantee a returned collection when elements or collection specified
16612 collection: function collection(eles, opts) {
16613 if (string(eles)) {
16614 return this.$(eles);
16615 } else if (elementOrCollection(eles)) {
16616 return eles.collection();
16617 } else if (array(eles)) {
16618 if (!opts) {
16619 opts = {};
16620 }
16621
16622 return new Collection(this, eles, opts.unique, opts.removed);
16623 }
16624
16625 return new Collection(this);
16626 },
16627 nodes: function nodes(selector) {
16628 var nodes = this.$(function (ele) {
16629 return ele.isNode();
16630 });
16631
16632 if (selector) {
16633 return nodes.filter(selector);
16634 }
16635
16636 return nodes;
16637 },
16638 edges: function edges(selector) {
16639 var edges = this.$(function (ele) {
16640 return ele.isEdge();
16641 });
16642
16643 if (selector) {
16644 return edges.filter(selector);
16645 }
16646
16647 return edges;
16648 },
16649 // search the graph like jQuery
16650 $: function $(selector) {
16651 var eles = this._private.elements;
16652
16653 if (selector) {
16654 return eles.filter(selector);
16655 } else {
16656 return eles.spawnSelf();
16657 }
16658 },
16659 mutableElements: function mutableElements() {
16660 return this._private.elements;
16661 }
16662 }; // aliases
16663
16664 corefn$3.elements = corefn$3.filter = corefn$3.$;
16665
16666 var styfn$8 = {}; // keys for style blocks, e.g. ttfftt
16667
16668 var TRUE = 't';
16669 var FALSE = 'f'; // (potentially expensive calculation)
16670 // apply the style to the element based on
16671 // - its bypass
16672 // - what selectors match it
16673
16674 styfn$8.apply = function (eles) {
16675 var self = this;
16676 var _p = self._private;
16677 var cy = _p.cy;
16678 var updatedEles = cy.collection();
16679
16680 for (var ie = 0; ie < eles.length; ie++) {
16681 var ele = eles[ie];
16682 var cxtMeta = self.getContextMeta(ele);
16683
16684 if (cxtMeta.empty) {
16685 continue;
16686 }
16687
16688 var cxtStyle = self.getContextStyle(cxtMeta);
16689 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
16690
16691 if (ele._private.appliedInitStyle) {
16692 self.updateTransitions(ele, app.diffProps);
16693 } else {
16694 ele._private.appliedInitStyle = true;
16695 }
16696
16697 var hintsDiff = self.updateStyleHints(ele);
16698
16699 if (hintsDiff) {
16700 updatedEles.push(ele);
16701 }
16702 } // for elements
16703
16704
16705 return updatedEles;
16706 };
16707
16708 styfn$8.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
16709 var self = this;
16710 var cache = self._private.propDiffs = self._private.propDiffs || {};
16711 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
16712 var cachedVal = cache[dualCxtKey];
16713
16714 if (cachedVal) {
16715 return cachedVal;
16716 }
16717
16718 var diffProps = [];
16719 var addedProp = {};
16720
16721 for (var i = 0; i < self.length; i++) {
16722 var cxt = self[i];
16723 var oldHasCxt = oldCxtKey[i] === TRUE;
16724 var newHasCxt = newCxtKey[i] === TRUE;
16725 var cxtHasDiffed = oldHasCxt !== newHasCxt;
16726 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
16727
16728 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
16729 var props = void 0;
16730
16731 if (cxtHasDiffed && cxtHasMappedProps) {
16732 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
16733 } else if (cxtHasDiffed) {
16734 props = cxt.properties; // need to check them all
16735 } else if (cxtHasMappedProps) {
16736 props = cxt.mappedProperties; // only need to check mapped
16737 }
16738
16739 for (var j = 0; j < props.length; j++) {
16740 var prop = props[j];
16741 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
16742 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
16743 // is cached)
16744
16745 var laterCxtOverrides = false;
16746
16747 for (var k = i + 1; k < self.length; k++) {
16748 var laterCxt = self[k];
16749 var hasLaterCxt = newCxtKey[k] === TRUE;
16750
16751 if (!hasLaterCxt) {
16752 continue;
16753 } // can't override unless the context is active
16754
16755
16756 laterCxtOverrides = laterCxt.properties[prop.name] != null;
16757
16758 if (laterCxtOverrides) {
16759 break;
16760 } // exit early as long as one later context overrides
16761
16762 }
16763
16764 if (!addedProp[name] && !laterCxtOverrides) {
16765 addedProp[name] = true;
16766 diffProps.push(name);
16767 }
16768 } // for props
16769
16770 } // if
16771
16772 } // for contexts
16773
16774
16775 cache[dualCxtKey] = diffProps;
16776 return diffProps;
16777 };
16778
16779 styfn$8.getContextMeta = function (ele) {
16780 var self = this;
16781 var cxtKey = '';
16782 var diffProps;
16783 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
16784
16785 for (var i = 0; i < self.length; i++) {
16786 var context = self[i];
16787 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
16788
16789 if (contextSelectorMatches) {
16790 cxtKey += TRUE;
16791 } else {
16792 cxtKey += FALSE;
16793 }
16794 } // for context
16795
16796
16797 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
16798 ele._private.styleCxtKey = cxtKey;
16799 return {
16800 key: cxtKey,
16801 diffPropNames: diffProps,
16802 empty: diffProps.length === 0
16803 };
16804 }; // gets a computed ele style object based on matched contexts
16805
16806
16807 styfn$8.getContextStyle = function (cxtMeta) {
16808 var cxtKey = cxtMeta.key;
16809 var self = this;
16810 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
16811
16812 if (cxtStyles[cxtKey]) {
16813 return cxtStyles[cxtKey];
16814 }
16815
16816 var style = {
16817 _private: {
16818 key: cxtKey
16819 }
16820 };
16821
16822 for (var i = 0; i < self.length; i++) {
16823 var cxt = self[i];
16824 var hasCxt = cxtKey[i] === TRUE;
16825
16826 if (!hasCxt) {
16827 continue;
16828 }
16829
16830 for (var j = 0; j < cxt.properties.length; j++) {
16831 var prop = cxt.properties[j];
16832 style[prop.name] = prop;
16833 }
16834 }
16835
16836 cxtStyles[cxtKey] = style;
16837 return style;
16838 };
16839
16840 styfn$8.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
16841 var self = this;
16842 var diffProps = cxtMeta.diffPropNames;
16843 var retDiffProps = {};
16844 var types = self.types;
16845
16846 for (var i = 0; i < diffProps.length; i++) {
16847 var diffPropName = diffProps[i];
16848 var cxtProp = cxtStyle[diffPropName];
16849 var eleProp = ele.pstyle(diffPropName);
16850
16851 if (!cxtProp) {
16852 // no context prop means delete
16853 if (!eleProp) {
16854 continue; // no existing prop means nothing needs to be removed
16855 // nb affects initial application on mapped values like control-point-distances
16856 } else if (eleProp.bypass) {
16857 cxtProp = {
16858 name: diffPropName,
16859 deleteBypassed: true
16860 };
16861 } else {
16862 cxtProp = {
16863 name: diffPropName,
16864 "delete": true
16865 };
16866 }
16867 } // save cycles when the context prop doesn't need to be applied
16868
16869
16870 if (eleProp === cxtProp) {
16871 continue;
16872 } // save cycles when a mapped context prop doesn't need to be applied
16873
16874
16875 if (cxtProp.mapped === types.fn // context prop is function mapper
16876 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
16877 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
16878 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
16879 ) {
16880 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
16881 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
16882
16883 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
16884
16885 if (fnValue === mapping.prevFnValue) {
16886 continue;
16887 }
16888 }
16889
16890 var retDiffProp = retDiffProps[diffPropName] = {
16891 prev: eleProp
16892 };
16893 self.applyParsedProperty(ele, cxtProp);
16894 retDiffProp.next = ele.pstyle(diffPropName);
16895
16896 if (retDiffProp.next && retDiffProp.next.bypass) {
16897 retDiffProp.next = retDiffProp.next.bypassed;
16898 }
16899 }
16900
16901 return {
16902 diffProps: retDiffProps
16903 };
16904 };
16905
16906 styfn$8.updateStyleHints = function (ele) {
16907 var _p = ele._private;
16908 var self = this;
16909 var propNames = self.propertyGroupNames;
16910 var propGrKeys = self.propertyGroupKeys;
16911
16912 var propHash = function propHash(ele, propNames, seedKey) {
16913 return self.getPropertiesHash(ele, propNames, seedKey);
16914 };
16915
16916 var oldStyleKey = _p.styleKey;
16917
16918 if (ele.removed()) {
16919 return false;
16920 }
16921
16922 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
16923 // but lazily -- only use non-default prop values to reduce the number of hashes
16924 //
16925
16926 var overriddenStyles = ele._private.style;
16927 propNames = Object.keys(overriddenStyles);
16928
16929 for (var i = 0; i < propGrKeys.length; i++) {
16930 var grKey = propGrKeys[i];
16931 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
16932 }
16933
16934 var updateGrKey1 = function updateGrKey1(val, grKey) {
16935 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
16936 };
16937
16938 var updateGrKey2 = function updateGrKey2(val, grKey) {
16939 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
16940 };
16941
16942 var updateGrKey = function updateGrKey(val, grKey) {
16943 updateGrKey1(val, grKey);
16944 updateGrKey2(val, grKey);
16945 };
16946
16947 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
16948 for (var j = 0; j < strVal.length; j++) {
16949 var ch = strVal.charCodeAt(j);
16950 updateGrKey1(ch, grKey);
16951 updateGrKey2(ch, grKey);
16952 }
16953 }; // - hashing works on 32 bit ints b/c we use bitwise ops
16954 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
16955 // - raise up small numbers so more significant digits are seen by hashing
16956 // - make small numbers larger than a normal value to avoid collisions
16957 // - works in practice and it's relatively cheap
16958
16959
16960 var N = 2000000000;
16961
16962 var cleanNum = function cleanNum(val) {
16963 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
16964 };
16965
16966 for (var _i = 0; _i < propNames.length; _i++) {
16967 var name = propNames[_i];
16968 var parsedProp = overriddenStyles[name];
16969
16970 if (parsedProp == null) {
16971 continue;
16972 }
16973
16974 var propInfo = this.properties[name];
16975 var type = propInfo.type;
16976 var _grKey = propInfo.groupKey;
16977 var normalizedNumberVal = void 0;
16978
16979 if (propInfo.hashOverride != null) {
16980 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
16981 } else if (parsedProp.pfValue != null) {
16982 normalizedNumberVal = parsedProp.pfValue;
16983 } // might not be a number if it allows enums
16984
16985
16986 var numberVal = propInfo.enums == null ? parsedProp.value : null;
16987 var haveNormNum = normalizedNumberVal != null;
16988 var haveUnitedNum = numberVal != null;
16989 var haveNum = haveNormNum || haveUnitedNum;
16990 var units = parsedProp.units; // numbers are cheaper to hash than strings
16991 // 1 hash op vs n hash ops (for length n string)
16992
16993 if (type.number && haveNum && !type.multiple) {
16994 var v = haveNormNum ? normalizedNumberVal : numberVal;
16995 updateGrKey(cleanNum(v), _grKey);
16996
16997 if (!haveNormNum && units != null) {
16998 updateGrKeyWStr(units, _grKey);
16999 }
17000 } else {
17001 updateGrKeyWStr(parsedProp.strValue, _grKey);
17002 }
17003 } // overall style key
17004 //
17005
17006
17007 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
17008
17009 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
17010 var _grKey2 = propGrKeys[_i2];
17011 var grHash = _p.styleKeys[_grKey2];
17012 hash[0] = hashInt(grHash[0], hash[0]);
17013 hash[1] = hashIntAlt(grHash[1], hash[1]);
17014 }
17015
17016 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
17017 //
17018
17019 var sk = _p.styleKeys;
17020 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
17021 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
17022 _p.labelKey = combineHashesArray(labelKeys);
17023 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
17024
17025 if (!isNode) {
17026 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
17027 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
17028 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
17029 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
17030 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
17031 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
17032 } // node
17033 //
17034
17035
17036 if (isNode) {
17037 var _p$styleKeys = _p.styleKeys,
17038 nodeBody = _p$styleKeys.nodeBody,
17039 nodeBorder = _p$styleKeys.nodeBorder,
17040 backgroundImage = _p$styleKeys.backgroundImage,
17041 compound = _p$styleKeys.compound,
17042 pie = _p$styleKeys.pie;
17043 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
17044 return k != null;
17045 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
17046 _p.nodeKey = combineHashesArray(nodeKeys);
17047 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
17048 }
17049
17050 return oldStyleKey !== _p.styleKey;
17051 };
17052
17053 styfn$8.clearStyleHints = function (ele) {
17054 var _p = ele._private;
17055 _p.styleCxtKey = '';
17056 _p.styleKeys = {};
17057 _p.styleKey = null;
17058 _p.labelKey = null;
17059 _p.labelStyleKey = null;
17060 _p.sourceLabelKey = null;
17061 _p.sourceLabelStyleKey = null;
17062 _p.targetLabelKey = null;
17063 _p.targetLabelStyleKey = null;
17064 _p.nodeKey = null;
17065 _p.hasPie = null;
17066 }; // apply a property to the style (for internal use)
17067 // returns whether application was successful
17068 //
17069 // now, this function flattens the property, and here's how:
17070 //
17071 // for parsedProp:{ bypass: true, deleteBypass: true }
17072 // no property is generated, instead the bypass property in the
17073 // element's style is replaced by what's pointed to by the `bypassed`
17074 // field in the bypass property (i.e. restoring the property the
17075 // bypass was overriding)
17076 //
17077 // for parsedProp:{ mapped: truthy }
17078 // the generated flattenedProp:{ mapping: prop }
17079 //
17080 // for parsedProp:{ bypass: true }
17081 // the generated flattenedProp:{ bypassed: parsedProp }
17082
17083
17084 styfn$8.applyParsedProperty = function (ele, parsedProp) {
17085 var self = this;
17086 var prop = parsedProp;
17087 var style = ele._private.style;
17088 var flatProp;
17089 var types = self.types;
17090 var type = self.properties[prop.name].type;
17091 var propIsBypass = prop.bypass;
17092 var origProp = style[prop.name];
17093 var origPropIsBypass = origProp && origProp.bypass;
17094 var _p = ele._private;
17095 var flatPropMapping = 'mapping';
17096
17097 var getVal = function getVal(p) {
17098 if (p == null) {
17099 return null;
17100 } else if (p.pfValue != null) {
17101 return p.pfValue;
17102 } else {
17103 return p.value;
17104 }
17105 };
17106
17107 var checkTriggers = function checkTriggers() {
17108 var fromVal = getVal(origProp);
17109 var toVal = getVal(prop);
17110 self.checkTriggers(ele, prop.name, fromVal, toVal);
17111 };
17112
17113 if (prop && prop.name.substr(0, 3) === 'pie') {
17114 warn('The pie style properties are deprecated. Create charts using background images instead.');
17115 } // edge sanity checks to prevent the client from making serious mistakes
17116
17117
17118 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
17119 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
17120 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
17121 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
17122 }
17123
17124 if (prop["delete"]) {
17125 // delete the property and use the default value on falsey value
17126 style[prop.name] = undefined;
17127 checkTriggers();
17128 return true;
17129 }
17130
17131 if (prop.deleteBypassed) {
17132 // delete the property that the
17133 if (!origProp) {
17134 checkTriggers();
17135 return true; // can't delete if no prop
17136 } else if (origProp.bypass) {
17137 // delete bypassed
17138 origProp.bypassed = undefined;
17139 checkTriggers();
17140 return true;
17141 } else {
17142 return false; // we're unsuccessful deleting the bypassed
17143 }
17144 } // check if we need to delete the current bypass
17145
17146
17147 if (prop.deleteBypass) {
17148 // then this property is just here to indicate we need to delete
17149 if (!origProp) {
17150 checkTriggers();
17151 return true; // property is already not defined
17152 } else if (origProp.bypass) {
17153 // then replace the bypass property with the original
17154 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
17155 style[prop.name] = origProp.bypassed;
17156 checkTriggers();
17157 return true;
17158 } else {
17159 return false; // we're unsuccessful deleting the bypass
17160 }
17161 }
17162
17163 var printMappingErr = function printMappingErr() {
17164 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');
17165 }; // put the property in the style objects
17166
17167
17168 switch (prop.mapped) {
17169 // flatten the property if mapped
17170 case types.mapData:
17171 {
17172 // flatten the field (e.g. data.foo.bar)
17173 var fields = prop.field.split('.');
17174 var fieldVal = _p.data;
17175
17176 for (var i = 0; i < fields.length && fieldVal; i++) {
17177 var field = fields[i];
17178 fieldVal = fieldVal[field];
17179 }
17180
17181 if (fieldVal == null) {
17182 printMappingErr();
17183 return false;
17184 }
17185
17186 var percent;
17187
17188 if (!number$1(fieldVal)) {
17189 // then don't apply and fall back on the existing style
17190 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
17191 return false;
17192 } else {
17193 var fieldWidth = prop.fieldMax - prop.fieldMin;
17194
17195 if (fieldWidth === 0) {
17196 // safety check -- not strictly necessary as no props of zero range should be passed here
17197 percent = 0;
17198 } else {
17199 percent = (fieldVal - prop.fieldMin) / fieldWidth;
17200 }
17201 } // make sure to bound percent value
17202
17203
17204 if (percent < 0) {
17205 percent = 0;
17206 } else if (percent > 1) {
17207 percent = 1;
17208 }
17209
17210 if (type.color) {
17211 var r1 = prop.valueMin[0];
17212 var r2 = prop.valueMax[0];
17213 var g1 = prop.valueMin[1];
17214 var g2 = prop.valueMax[1];
17215 var b1 = prop.valueMin[2];
17216 var b2 = prop.valueMax[2];
17217 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
17218 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
17219 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)];
17220 flatProp = {
17221 // colours are simple, so just create the flat property instead of expensive string parsing
17222 bypass: prop.bypass,
17223 // we're a bypass if the mapping property is a bypass
17224 name: prop.name,
17225 value: clr,
17226 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
17227 };
17228 } else if (type.number) {
17229 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
17230 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
17231 } else {
17232 return false; // can only map to colours and numbers
17233 }
17234
17235 if (!flatProp) {
17236 // if we can't flatten the property, then don't apply the property and fall back on the existing style
17237 printMappingErr();
17238 return false;
17239 }
17240
17241 flatProp.mapping = prop; // keep a reference to the mapping
17242
17243 prop = flatProp; // the flattened (mapped) property is the one we want
17244
17245 break;
17246 }
17247 // direct mapping
17248
17249 case types.data:
17250 {
17251 // flatten the field (e.g. data.foo.bar)
17252 var _fields = prop.field.split('.');
17253
17254 var _fieldVal = _p.data;
17255
17256 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
17257 var _field = _fields[_i3];
17258 _fieldVal = _fieldVal[_field];
17259 }
17260
17261 if (_fieldVal != null) {
17262 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
17263 }
17264
17265 if (!flatProp) {
17266 // if we can't flatten the property, then don't apply and fall back on the existing style
17267 printMappingErr();
17268 return false;
17269 }
17270
17271 flatProp.mapping = prop; // keep a reference to the mapping
17272
17273 prop = flatProp; // the flattened (mapped) property is the one we want
17274
17275 break;
17276 }
17277
17278 case types.fn:
17279 {
17280 var fn = prop.value;
17281 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
17282
17283 prop.prevFnValue = fnRetVal;
17284
17285 if (fnRetVal == null) {
17286 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
17287 return false;
17288 }
17289
17290 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
17291
17292 if (!flatProp) {
17293 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
17294 return false;
17295 }
17296
17297 flatProp.mapping = copy(prop); // keep a reference to the mapping
17298
17299 prop = flatProp; // the flattened (mapped) property is the one we want
17300
17301 break;
17302 }
17303
17304 case undefined:
17305 break;
17306 // just set the property
17307
17308 default:
17309 return false;
17310 // not a valid mapping
17311 } // if the property is a bypass property, then link the resultant property to the original one
17312
17313
17314 if (propIsBypass) {
17315 if (origPropIsBypass) {
17316 // then this bypass overrides the existing one
17317 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
17318 } else {
17319 // then link the orig prop to the new bypass
17320 prop.bypassed = origProp;
17321 }
17322
17323 style[prop.name] = prop; // and set
17324 } else {
17325 // prop is not bypass
17326 if (origPropIsBypass) {
17327 // then keep the orig prop (since it's a bypass) and link to the new prop
17328 origProp.bypassed = prop;
17329 } else {
17330 // then just replace the old prop with the new one
17331 style[prop.name] = prop;
17332 }
17333 }
17334
17335 checkTriggers();
17336 return true;
17337 };
17338
17339 styfn$8.cleanElements = function (eles, keepBypasses) {
17340 for (var i = 0; i < eles.length; i++) {
17341 var ele = eles[i];
17342 this.clearStyleHints(ele);
17343 ele.dirtyCompoundBoundsCache();
17344 ele.dirtyBoundingBoxCache();
17345
17346 if (!keepBypasses) {
17347 ele._private.style = {};
17348 } else {
17349 var style = ele._private.style;
17350 var propNames = Object.keys(style);
17351
17352 for (var j = 0; j < propNames.length; j++) {
17353 var propName = propNames[j];
17354 var eleProp = style[propName];
17355
17356 if (eleProp != null) {
17357 if (eleProp.bypass) {
17358 eleProp.bypassed = null;
17359 } else {
17360 style[propName] = null;
17361 }
17362 }
17363 }
17364 }
17365 }
17366 }; // updates the visual style for all elements (useful for manual style modification after init)
17367
17368
17369 styfn$8.update = function () {
17370 var cy = this._private.cy;
17371 var eles = cy.mutableElements();
17372 eles.updateStyle();
17373 }; // diffProps : { name => { prev, next } }
17374
17375
17376 styfn$8.updateTransitions = function (ele, diffProps) {
17377 var self = this;
17378 var _p = ele._private;
17379 var props = ele.pstyle('transition-property').value;
17380 var duration = ele.pstyle('transition-duration').pfValue;
17381 var delay = ele.pstyle('transition-delay').pfValue;
17382
17383 if (props.length > 0 && duration > 0) {
17384 var style = {}; // build up the style to animate towards
17385
17386 var anyPrev = false;
17387
17388 for (var i = 0; i < props.length; i++) {
17389 var prop = props[i];
17390 var styProp = ele.pstyle(prop);
17391 var diffProp = diffProps[prop];
17392
17393 if (!diffProp) {
17394 continue;
17395 }
17396
17397 var prevProp = diffProp.prev;
17398 var fromProp = prevProp;
17399 var toProp = diffProp.next != null ? diffProp.next : styProp;
17400 var diff = false;
17401 var initVal = void 0;
17402 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
17403
17404 if (!fromProp) {
17405 continue;
17406 } // consider px values
17407
17408
17409 if (number$1(fromProp.pfValue) && number$1(toProp.pfValue)) {
17410 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
17411
17412 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
17413 } else if (number$1(fromProp.value) && number$1(toProp.value)) {
17414 diff = toProp.value - fromProp.value; // nonzero is truthy
17415
17416 initVal = fromProp.value + initDt * diff; // consider colour values
17417 } else if (array(fromProp.value) && array(toProp.value)) {
17418 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
17419 initVal = fromProp.strValue;
17420 } // the previous value is good for an animation only if it's different
17421
17422
17423 if (diff) {
17424 style[prop] = toProp.strValue; // to val
17425
17426 this.applyBypass(ele, prop, initVal); // from val
17427
17428 anyPrev = true;
17429 }
17430 } // end if props allow ani
17431 // can't transition if there's nothing previous to transition from
17432
17433
17434 if (!anyPrev) {
17435 return;
17436 }
17437
17438 _p.transitioning = true;
17439 new Promise$1(function (resolve) {
17440 if (delay > 0) {
17441 ele.delayAnimation(delay).play().promise().then(resolve);
17442 } else {
17443 resolve();
17444 }
17445 }).then(function () {
17446 return ele.animation({
17447 style: style,
17448 duration: duration,
17449 easing: ele.pstyle('transition-timing-function').value,
17450 queue: false
17451 }).play().promise();
17452 }).then(function () {
17453 // if( !isBypass ){
17454 self.removeBypasses(ele, props);
17455 ele.emitAndNotify('style'); // }
17456
17457 _p.transitioning = false;
17458 });
17459 } else if (_p.transitioning) {
17460 this.removeBypasses(ele, props);
17461 ele.emitAndNotify('style');
17462 _p.transitioning = false;
17463 }
17464 };
17465
17466 styfn$8.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
17467 var prop = this.properties[name];
17468 var triggerCheck = getTrigger(prop);
17469
17470 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
17471 onTrigger(prop);
17472 }
17473 };
17474
17475 styfn$8.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
17476 var _this = this;
17477
17478 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
17479 return prop.triggersZOrder;
17480 }, function () {
17481 _this._private.cy.notify('zorder', ele);
17482 });
17483 };
17484
17485 styfn$8.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
17486 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
17487 return prop.triggersBounds;
17488 }, function (prop) {
17489 ele.dirtyCompoundBoundsCache();
17490 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
17491 // then dirty the pll edge bb cache as well
17492
17493 if ( // only for beziers -- so performance of other edges isn't affected
17494 prop.triggersBoundsOfParallelBeziers && (name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') || name === 'display' && (fromValue === 'none' || toValue === 'none'))) {
17495 ele.parallelEdges().forEach(function (pllEdge) {
17496 if (pllEdge.isBundledBezier()) {
17497 pllEdge.dirtyBoundingBoxCache();
17498 }
17499 });
17500 }
17501 });
17502 };
17503
17504 styfn$8.checkTriggers = function (ele, name, fromValue, toValue) {
17505 ele.dirtyStyleCache();
17506 this.checkZOrderTrigger(ele, name, fromValue, toValue);
17507 this.checkBoundsTrigger(ele, name, fromValue, toValue);
17508 };
17509
17510 var styfn$7 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
17511 // returns true iff application was successful for at least 1 specified property
17512
17513 styfn$7.applyBypass = function (eles, name, value, updateTransitions) {
17514 var self = this;
17515 var props = [];
17516 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
17517
17518 if (name === '*' || name === '**') {
17519 // apply to all property names
17520 if (value !== undefined) {
17521 for (var i = 0; i < self.properties.length; i++) {
17522 var prop = self.properties[i];
17523 var _name = prop.name;
17524 var parsedProp = this.parse(_name, value, true);
17525
17526 if (parsedProp) {
17527 props.push(parsedProp);
17528 }
17529 }
17530 }
17531 } else if (string(name)) {
17532 // then parse the single property
17533 var _parsedProp = this.parse(name, value, true);
17534
17535 if (_parsedProp) {
17536 props.push(_parsedProp);
17537 }
17538 } else if (plainObject(name)) {
17539 // then parse each property
17540 var specifiedProps = name;
17541 updateTransitions = value;
17542 var names = Object.keys(specifiedProps);
17543
17544 for (var _i = 0; _i < names.length; _i++) {
17545 var _name2 = names[_i];
17546 var _value = specifiedProps[_name2];
17547
17548 if (_value === undefined) {
17549 // try camel case name too
17550 _value = specifiedProps[dash2camel(_name2)];
17551 }
17552
17553 if (_value !== undefined) {
17554 var _parsedProp2 = this.parse(_name2, _value, true);
17555
17556 if (_parsedProp2) {
17557 props.push(_parsedProp2);
17558 }
17559 }
17560 }
17561 } else {
17562 // can't do anything without well defined properties
17563 return false;
17564 } // we've failed if there are no valid properties
17565
17566
17567 if (props.length === 0) {
17568 return false;
17569 } // now, apply the bypass properties on the elements
17570
17571
17572 var ret = false; // return true if at least one succesful bypass applied
17573
17574 for (var _i2 = 0; _i2 < eles.length; _i2++) {
17575 // for each ele
17576 var ele = eles[_i2];
17577 var diffProps = {};
17578 var diffProp = void 0;
17579
17580 for (var j = 0; j < props.length; j++) {
17581 // for each prop
17582 var _prop = props[j];
17583
17584 if (updateTransitions) {
17585 var prevProp = ele.pstyle(_prop.name);
17586 diffProp = diffProps[_prop.name] = {
17587 prev: prevProp
17588 };
17589 }
17590
17591 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
17592
17593 if (updateTransitions) {
17594 diffProp.next = ele.pstyle(_prop.name);
17595 }
17596 } // for props
17597
17598
17599 if (ret) {
17600 this.updateStyleHints(ele);
17601 }
17602
17603 if (updateTransitions) {
17604 this.updateTransitions(ele, diffProps, isBypass);
17605 }
17606 } // for eles
17607
17608
17609 return ret;
17610 }; // only useful in specific cases like animation
17611
17612
17613 styfn$7.overrideBypass = function (eles, name, value) {
17614 name = camel2dash(name);
17615
17616 for (var i = 0; i < eles.length; i++) {
17617 var ele = eles[i];
17618 var prop = ele._private.style[name];
17619 var type = this.properties[name].type;
17620 var isColor = type.color;
17621 var isMulti = type.mutiple;
17622 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
17623
17624 if (!prop || !prop.bypass) {
17625 // need a bypass if one doesn't exist
17626 this.applyBypass(ele, name, value);
17627 } else {
17628 prop.value = value;
17629
17630 if (prop.pfValue != null) {
17631 prop.pfValue = value;
17632 }
17633
17634 if (isColor) {
17635 prop.strValue = 'rgb(' + value.join(',') + ')';
17636 } else if (isMulti) {
17637 prop.strValue = value.join(' ');
17638 } else {
17639 prop.strValue = '' + value;
17640 }
17641
17642 this.updateStyleHints(ele);
17643 }
17644
17645 this.checkTriggers(ele, name, oldValue, value);
17646 }
17647 };
17648
17649 styfn$7.removeAllBypasses = function (eles, updateTransitions) {
17650 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
17651 };
17652
17653 styfn$7.removeBypasses = function (eles, props, updateTransitions) {
17654 var isBypass = true;
17655
17656 for (var j = 0; j < eles.length; j++) {
17657 var ele = eles[j];
17658 var diffProps = {};
17659
17660 for (var i = 0; i < props.length; i++) {
17661 var name = props[i];
17662 var prop = this.properties[name];
17663 var prevProp = ele.pstyle(prop.name);
17664
17665 if (!prevProp || !prevProp.bypass) {
17666 // if a bypass doesn't exist for the prop, nothing needs to be removed
17667 continue;
17668 }
17669
17670 var value = ''; // empty => remove bypass
17671
17672 var parsedProp = this.parse(name, value, true);
17673 var diffProp = diffProps[prop.name] = {
17674 prev: prevProp
17675 };
17676 this.applyParsedProperty(ele, parsedProp);
17677 diffProp.next = ele.pstyle(prop.name);
17678 } // for props
17679
17680
17681 this.updateStyleHints(ele);
17682
17683 if (updateTransitions) {
17684 this.updateTransitions(ele, diffProps, isBypass);
17685 }
17686 } // for eles
17687
17688 };
17689
17690 var styfn$6 = {}; // gets what an em size corresponds to in pixels relative to a dom element
17691
17692 styfn$6.getEmSizeInPixels = function () {
17693 var px = this.containerCss('font-size');
17694
17695 if (px != null) {
17696 return parseFloat(px);
17697 } else {
17698 return 1; // for headless
17699 }
17700 }; // gets css property from the core container
17701
17702
17703 styfn$6.containerCss = function (propName) {
17704 var cy = this._private.cy;
17705 var domElement = cy.container();
17706
17707 if (window$1 && domElement && window$1.getComputedStyle) {
17708 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
17709 }
17710 };
17711
17712 var styfn$5 = {}; // gets the rendered style for an element
17713
17714 styfn$5.getRenderedStyle = function (ele, prop) {
17715 if (prop) {
17716 return this.getStylePropertyValue(ele, prop, true);
17717 } else {
17718 return this.getRawStyle(ele, true);
17719 }
17720 }; // gets the raw style for an element
17721
17722
17723 styfn$5.getRawStyle = function (ele, isRenderedVal) {
17724 var self = this;
17725 ele = ele[0]; // insure it's an element
17726
17727 if (ele) {
17728 var rstyle = {};
17729
17730 for (var i = 0; i < self.properties.length; i++) {
17731 var prop = self.properties[i];
17732 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
17733
17734 if (val != null) {
17735 rstyle[prop.name] = val;
17736 rstyle[dash2camel(prop.name)] = val;
17737 }
17738 }
17739
17740 return rstyle;
17741 }
17742 };
17743
17744 styfn$5.getIndexedStyle = function (ele, property, subproperty, index) {
17745 var pstyle = ele.pstyle(property)[subproperty][index];
17746 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
17747 };
17748
17749 styfn$5.getStylePropertyValue = function (ele, propName, isRenderedVal) {
17750 var self = this;
17751 ele = ele[0]; // insure it's an element
17752
17753 if (ele) {
17754 var prop = self.properties[propName];
17755
17756 if (prop.alias) {
17757 prop = prop.pointsTo;
17758 }
17759
17760 var type = prop.type;
17761 var styleProp = ele.pstyle(prop.name);
17762
17763 if (styleProp) {
17764 var value = styleProp.value,
17765 units = styleProp.units,
17766 strValue = styleProp.strValue;
17767
17768 if (isRenderedVal && type.number && value != null && number$1(value)) {
17769 var zoom = ele.cy().zoom();
17770
17771 var getRenderedValue = function getRenderedValue(val) {
17772 return val * zoom;
17773 };
17774
17775 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
17776 return getRenderedValue(val) + units;
17777 };
17778
17779 var isArrayValue = array(value);
17780 var haveUnits = isArrayValue ? units.every(function (u) {
17781 return u != null;
17782 }) : units != null;
17783
17784 if (haveUnits) {
17785 if (isArrayValue) {
17786 return value.map(function (v, i) {
17787 return getValueStringWithUnits(v, units[i]);
17788 }).join(' ');
17789 } else {
17790 return getValueStringWithUnits(value, units);
17791 }
17792 } else {
17793 if (isArrayValue) {
17794 return value.map(function (v) {
17795 return string(v) ? v : '' + getRenderedValue(v);
17796 }).join(' ');
17797 } else {
17798 return '' + getRenderedValue(value);
17799 }
17800 }
17801 } else if (strValue != null) {
17802 return strValue;
17803 }
17804 }
17805
17806 return null;
17807 }
17808 };
17809
17810 styfn$5.getAnimationStartStyle = function (ele, aniProps) {
17811 var rstyle = {};
17812
17813 for (var i = 0; i < aniProps.length; i++) {
17814 var aniProp = aniProps[i];
17815 var name = aniProp.name;
17816 var styleProp = ele.pstyle(name);
17817
17818 if (styleProp !== undefined) {
17819 // then make a prop of it
17820 if (plainObject(styleProp)) {
17821 styleProp = this.parse(name, styleProp.strValue);
17822 } else {
17823 styleProp = this.parse(name, styleProp);
17824 }
17825 }
17826
17827 if (styleProp) {
17828 rstyle[name] = styleProp;
17829 }
17830 }
17831
17832 return rstyle;
17833 };
17834
17835 styfn$5.getPropsList = function (propsObj) {
17836 var self = this;
17837 var rstyle = [];
17838 var style = propsObj;
17839 var props = self.properties;
17840
17841 if (style) {
17842 var names = Object.keys(style);
17843
17844 for (var i = 0; i < names.length; i++) {
17845 var name = names[i];
17846 var val = style[name];
17847 var prop = props[name] || props[camel2dash(name)];
17848 var styleProp = this.parse(prop.name, val);
17849
17850 if (styleProp) {
17851 rstyle.push(styleProp);
17852 }
17853 }
17854 }
17855
17856 return rstyle;
17857 };
17858
17859 styfn$5.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
17860 var hash = seed.slice();
17861 var name, val, strVal, chVal;
17862 var i, j;
17863
17864 for (i = 0; i < propNames.length; i++) {
17865 name = propNames[i];
17866 val = ele.pstyle(name, false);
17867
17868 if (val == null) {
17869 continue;
17870 } else if (val.pfValue != null) {
17871 hash[0] = hashInt(chVal, hash[0]);
17872 hash[1] = hashIntAlt(chVal, hash[1]);
17873 } else {
17874 strVal = val.strValue;
17875
17876 for (j = 0; j < strVal.length; j++) {
17877 chVal = strVal.charCodeAt(j);
17878 hash[0] = hashInt(chVal, hash[0]);
17879 hash[1] = hashIntAlt(chVal, hash[1]);
17880 }
17881 }
17882 }
17883
17884 return hash;
17885 };
17886
17887 styfn$5.getPropertiesHash = styfn$5.getNonDefaultPropertiesHash;
17888
17889 var styfn$4 = {};
17890
17891 styfn$4.appendFromJson = function (json) {
17892 var style = this;
17893
17894 for (var i = 0; i < json.length; i++) {
17895 var context = json[i];
17896 var selector = context.selector;
17897 var props = context.style || context.css;
17898 var names = Object.keys(props);
17899 style.selector(selector); // apply selector
17900
17901 for (var j = 0; j < names.length; j++) {
17902 var name = names[j];
17903 var value = props[name];
17904 style.css(name, value); // apply property
17905 }
17906 }
17907
17908 return style;
17909 }; // accessible cy.style() function
17910
17911
17912 styfn$4.fromJson = function (json) {
17913 var style = this;
17914 style.resetToDefault();
17915 style.appendFromJson(json);
17916 return style;
17917 }; // get json from cy.style() api
17918
17919
17920 styfn$4.json = function () {
17921 var json = [];
17922
17923 for (var i = this.defaultLength; i < this.length; i++) {
17924 var cxt = this[i];
17925 var selector = cxt.selector;
17926 var props = cxt.properties;
17927 var css = {};
17928
17929 for (var j = 0; j < props.length; j++) {
17930 var prop = props[j];
17931 css[prop.name] = prop.strValue;
17932 }
17933
17934 json.push({
17935 selector: !selector ? 'core' : selector.toString(),
17936 style: css
17937 });
17938 }
17939
17940 return json;
17941 };
17942
17943 var styfn$3 = {};
17944
17945 styfn$3.appendFromString = function (string) {
17946 var self = this;
17947 var style = this;
17948 var remaining = '' + string;
17949 var selAndBlockStr;
17950 var blockRem;
17951 var propAndValStr; // remove comments from the style string
17952
17953 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
17954
17955 function removeSelAndBlockFromRemaining() {
17956 // remove the parsed selector and block from the remaining text to parse
17957 if (remaining.length > selAndBlockStr.length) {
17958 remaining = remaining.substr(selAndBlockStr.length);
17959 } else {
17960 remaining = '';
17961 }
17962 }
17963
17964 function removePropAndValFromRem() {
17965 // remove the parsed property and value from the remaining block text to parse
17966 if (blockRem.length > propAndValStr.length) {
17967 blockRem = blockRem.substr(propAndValStr.length);
17968 } else {
17969 blockRem = '';
17970 }
17971 }
17972
17973 for (;;) {
17974 var nothingLeftToParse = remaining.match(/^\s*$/);
17975
17976 if (nothingLeftToParse) {
17977 break;
17978 }
17979
17980 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
17981
17982 if (!selAndBlock) {
17983 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
17984 break;
17985 }
17986
17987 selAndBlockStr = selAndBlock[0]; // parse the selector
17988
17989 var selectorStr = selAndBlock[1];
17990
17991 if (selectorStr !== 'core') {
17992 var selector = new Selector(selectorStr);
17993
17994 if (selector.invalid) {
17995 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
17996
17997 removeSelAndBlockFromRemaining();
17998 continue;
17999 }
18000 } // parse the block of properties and values
18001
18002
18003 var blockStr = selAndBlock[2];
18004 var invalidBlock = false;
18005 blockRem = blockStr;
18006 var props = [];
18007
18008 for (;;) {
18009 var _nothingLeftToParse = blockRem.match(/^\s*$/);
18010
18011 if (_nothingLeftToParse) {
18012 break;
18013 }
18014
18015 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)(?:\s*;|\s*$)/);
18016
18017 if (!propAndVal) {
18018 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
18019 invalidBlock = true;
18020 break;
18021 }
18022
18023 propAndValStr = propAndVal[0];
18024 var propStr = propAndVal[1];
18025 var valStr = propAndVal[2];
18026 var prop = self.properties[propStr];
18027
18028 if (!prop) {
18029 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
18030
18031 removePropAndValFromRem();
18032 continue;
18033 }
18034
18035 var parsedProp = style.parse(propStr, valStr);
18036
18037 if (!parsedProp) {
18038 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
18039
18040 removePropAndValFromRem();
18041 continue;
18042 }
18043
18044 props.push({
18045 name: propStr,
18046 val: valStr
18047 });
18048 removePropAndValFromRem();
18049 }
18050
18051 if (invalidBlock) {
18052 removeSelAndBlockFromRemaining();
18053 break;
18054 } // put the parsed block in the style
18055
18056
18057 style.selector(selectorStr);
18058
18059 for (var i = 0; i < props.length; i++) {
18060 var _prop = props[i];
18061 style.css(_prop.name, _prop.val);
18062 }
18063
18064 removeSelAndBlockFromRemaining();
18065 }
18066
18067 return style;
18068 };
18069
18070 styfn$3.fromString = function (string) {
18071 var style = this;
18072 style.resetToDefault();
18073 style.appendFromString(string);
18074 return style;
18075 };
18076
18077 var styfn$2 = {};
18078
18079 (function () {
18080 var number$1 = number;
18081 var rgba = rgbaNoBackRefs;
18082 var hsla = hslaNoBackRefs;
18083 var hex3$1 = hex3;
18084 var hex6$1 = hex6;
18085
18086 var data = function data(prefix) {
18087 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
18088 };
18089
18090 var mapData = function mapData(prefix) {
18091 var mapArg = number$1 + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
18092 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number$1 + ')\\s*\\,\\s*(' + number$1 + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
18093 };
18094
18095 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
18096
18097 styfn$2.types = {
18098 time: {
18099 number: true,
18100 min: 0,
18101 units: 's|ms',
18102 implicitUnits: 'ms'
18103 },
18104 percent: {
18105 number: true,
18106 min: 0,
18107 max: 100,
18108 units: '%',
18109 implicitUnits: '%'
18110 },
18111 percentages: {
18112 number: true,
18113 min: 0,
18114 max: 100,
18115 units: '%',
18116 implicitUnits: '%',
18117 multiple: true
18118 },
18119 zeroOneNumber: {
18120 number: true,
18121 min: 0,
18122 max: 1,
18123 unitless: true
18124 },
18125 zeroOneNumbers: {
18126 number: true,
18127 min: 0,
18128 max: 1,
18129 unitless: true,
18130 multiple: true
18131 },
18132 nOneOneNumber: {
18133 number: true,
18134 min: -1,
18135 max: 1,
18136 unitless: true
18137 },
18138 nonNegativeInt: {
18139 number: true,
18140 min: 0,
18141 integer: true,
18142 unitless: true
18143 },
18144 position: {
18145 enums: ['parent', 'origin']
18146 },
18147 nodeSize: {
18148 number: true,
18149 min: 0,
18150 enums: ['label']
18151 },
18152 number: {
18153 number: true,
18154 unitless: true
18155 },
18156 numbers: {
18157 number: true,
18158 unitless: true,
18159 multiple: true
18160 },
18161 positiveNumber: {
18162 number: true,
18163 unitless: true,
18164 min: 0,
18165 strictMin: true
18166 },
18167 size: {
18168 number: true,
18169 min: 0
18170 },
18171 bidirectionalSize: {
18172 number: true
18173 },
18174 // allows negative
18175 bidirectionalSizeMaybePercent: {
18176 number: true,
18177 allowPercent: true
18178 },
18179 // allows negative
18180 bidirectionalSizes: {
18181 number: true,
18182 multiple: true
18183 },
18184 // allows negative
18185 sizeMaybePercent: {
18186 number: true,
18187 min: 0,
18188 allowPercent: true
18189 },
18190 axisDirection: {
18191 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
18192 },
18193 paddingRelativeTo: {
18194 enums: ['width', 'height', 'average', 'min', 'max']
18195 },
18196 bgWH: {
18197 number: true,
18198 min: 0,
18199 allowPercent: true,
18200 enums: ['auto'],
18201 multiple: true
18202 },
18203 bgPos: {
18204 number: true,
18205 allowPercent: true,
18206 multiple: true
18207 },
18208 bgRelativeTo: {
18209 enums: ['inner', 'include-padding'],
18210 multiple: true
18211 },
18212 bgRepeat: {
18213 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
18214 multiple: true
18215 },
18216 bgFit: {
18217 enums: ['none', 'contain', 'cover'],
18218 multiple: true
18219 },
18220 bgCrossOrigin: {
18221 enums: ['anonymous', 'use-credentials'],
18222 multiple: true
18223 },
18224 bgClip: {
18225 enums: ['none', 'node'],
18226 multiple: true
18227 },
18228 bgContainment: {
18229 enums: ['inside', 'over'],
18230 multiple: true
18231 },
18232 color: {
18233 color: true
18234 },
18235 colors: {
18236 color: true,
18237 multiple: true
18238 },
18239 fill: {
18240 enums: ['solid', 'linear-gradient', 'radial-gradient']
18241 },
18242 bool: {
18243 enums: ['yes', 'no']
18244 },
18245 bools: {
18246 enums: ['yes', 'no'],
18247 multiple: true
18248 },
18249 lineStyle: {
18250 enums: ['solid', 'dotted', 'dashed']
18251 },
18252 lineCap: {
18253 enums: ['butt', 'round', 'square']
18254 },
18255 borderStyle: {
18256 enums: ['solid', 'dotted', 'dashed', 'double']
18257 },
18258 curveStyle: {
18259 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'straight-triangle', 'taxi']
18260 },
18261 fontFamily: {
18262 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
18263 },
18264 fontStyle: {
18265 enums: ['italic', 'normal', 'oblique']
18266 },
18267 fontWeight: {
18268 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
18269 },
18270 textDecoration: {
18271 enums: ['none', 'underline', 'overline', 'line-through']
18272 },
18273 textTransform: {
18274 enums: ['none', 'uppercase', 'lowercase']
18275 },
18276 textWrap: {
18277 enums: ['none', 'wrap', 'ellipsis']
18278 },
18279 textOverflowWrap: {
18280 enums: ['whitespace', 'anywhere']
18281 },
18282 textBackgroundShape: {
18283 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
18284 },
18285 nodeShape: {
18286 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']
18287 },
18288 overlayShape: {
18289 enums: ['roundrectangle', 'round-rectangle', 'ellipse']
18290 },
18291 compoundIncludeLabels: {
18292 enums: ['include', 'exclude']
18293 },
18294 arrowShape: {
18295 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
18296 },
18297 arrowFill: {
18298 enums: ['filled', 'hollow']
18299 },
18300 display: {
18301 enums: ['element', 'none']
18302 },
18303 visibility: {
18304 enums: ['hidden', 'visible']
18305 },
18306 zCompoundDepth: {
18307 enums: ['bottom', 'orphan', 'auto', 'top']
18308 },
18309 zIndexCompare: {
18310 enums: ['auto', 'manual']
18311 },
18312 valign: {
18313 enums: ['top', 'center', 'bottom']
18314 },
18315 halign: {
18316 enums: ['left', 'center', 'right']
18317 },
18318 justification: {
18319 enums: ['left', 'center', 'right', 'auto']
18320 },
18321 text: {
18322 string: true
18323 },
18324 data: {
18325 mapping: true,
18326 regex: data('data')
18327 },
18328 layoutData: {
18329 mapping: true,
18330 regex: data('layoutData')
18331 },
18332 scratch: {
18333 mapping: true,
18334 regex: data('scratch')
18335 },
18336 mapData: {
18337 mapping: true,
18338 regex: mapData('mapData')
18339 },
18340 mapLayoutData: {
18341 mapping: true,
18342 regex: mapData('mapLayoutData')
18343 },
18344 mapScratch: {
18345 mapping: true,
18346 regex: mapData('mapScratch')
18347 },
18348 fn: {
18349 mapping: true,
18350 fn: true
18351 },
18352 url: {
18353 regexes: urlRegexes,
18354 singleRegexMatchValue: true
18355 },
18356 urls: {
18357 regexes: urlRegexes,
18358 singleRegexMatchValue: true,
18359 multiple: true
18360 },
18361 propList: {
18362 propList: true
18363 },
18364 angle: {
18365 number: true,
18366 units: 'deg|rad',
18367 implicitUnits: 'rad'
18368 },
18369 textRotation: {
18370 number: true,
18371 units: 'deg|rad',
18372 implicitUnits: 'rad',
18373 enums: ['none', 'autorotate']
18374 },
18375 polygonPointList: {
18376 number: true,
18377 multiple: true,
18378 evenMultiple: true,
18379 min: -1,
18380 max: 1,
18381 unitless: true
18382 },
18383 edgeDistances: {
18384 enums: ['intersection', 'node-position']
18385 },
18386 edgeEndpoint: {
18387 number: true,
18388 multiple: true,
18389 units: '%|px|em|deg|rad',
18390 implicitUnits: 'px',
18391 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
18392 singleEnum: true,
18393 validate: function validate(valArr, unitsArr) {
18394 switch (valArr.length) {
18395 case 2:
18396 // can be % or px only
18397 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
18398
18399 case 1:
18400 // can be enum, deg, or rad only
18401 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
18402
18403 default:
18404 return false;
18405 }
18406 }
18407 },
18408 easing: {
18409 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*\\)$'],
18410 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']
18411 },
18412 gradientDirection: {
18413 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
18414 ]
18415 },
18416 boundsExpansion: {
18417 number: true,
18418 multiple: true,
18419 min: 0,
18420 validate: function validate(valArr) {
18421 var length = valArr.length;
18422 return length === 1 || length === 2 || length === 4;
18423 }
18424 }
18425 };
18426 var diff = {
18427 zeroNonZero: function zeroNonZero(val1, val2) {
18428 if ((val1 == null || val2 == null) && val1 !== val2) {
18429 return true; // null cases could represent any value
18430 }
18431
18432 if (val1 == 0 && val2 != 0) {
18433 return true;
18434 } else if (val1 != 0 && val2 == 0) {
18435 return true;
18436 } else {
18437 return false;
18438 }
18439 },
18440 any: function any(val1, val2) {
18441 return val1 != val2;
18442 },
18443 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
18444 var empty1 = emptyString(str1);
18445 var empty2 = emptyString(str2);
18446 return empty1 && !empty2 || !empty1 && empty2;
18447 }
18448 }; // define visual style properties
18449 //
18450 // - n.b. adding a new group of props may require updates to updateStyleHints()
18451 // - adding new props to an existing group gets handled automatically
18452
18453 var t = styfn$2.types;
18454 var mainLabel = [{
18455 name: 'label',
18456 type: t.text,
18457 triggersBounds: diff.any,
18458 triggersZOrder: diff.emptyNonEmpty
18459 }, {
18460 name: 'text-rotation',
18461 type: t.textRotation,
18462 triggersBounds: diff.any
18463 }, {
18464 name: 'text-margin-x',
18465 type: t.bidirectionalSize,
18466 triggersBounds: diff.any
18467 }, {
18468 name: 'text-margin-y',
18469 type: t.bidirectionalSize,
18470 triggersBounds: diff.any
18471 }];
18472 var sourceLabel = [{
18473 name: 'source-label',
18474 type: t.text,
18475 triggersBounds: diff.any
18476 }, {
18477 name: 'source-text-rotation',
18478 type: t.textRotation,
18479 triggersBounds: diff.any
18480 }, {
18481 name: 'source-text-margin-x',
18482 type: t.bidirectionalSize,
18483 triggersBounds: diff.any
18484 }, {
18485 name: 'source-text-margin-y',
18486 type: t.bidirectionalSize,
18487 triggersBounds: diff.any
18488 }, {
18489 name: 'source-text-offset',
18490 type: t.size,
18491 triggersBounds: diff.any
18492 }];
18493 var targetLabel = [{
18494 name: 'target-label',
18495 type: t.text,
18496 triggersBounds: diff.any
18497 }, {
18498 name: 'target-text-rotation',
18499 type: t.textRotation,
18500 triggersBounds: diff.any
18501 }, {
18502 name: 'target-text-margin-x',
18503 type: t.bidirectionalSize,
18504 triggersBounds: diff.any
18505 }, {
18506 name: 'target-text-margin-y',
18507 type: t.bidirectionalSize,
18508 triggersBounds: diff.any
18509 }, {
18510 name: 'target-text-offset',
18511 type: t.size,
18512 triggersBounds: diff.any
18513 }];
18514 var labelDimensions = [{
18515 name: 'font-family',
18516 type: t.fontFamily,
18517 triggersBounds: diff.any
18518 }, {
18519 name: 'font-style',
18520 type: t.fontStyle,
18521 triggersBounds: diff.any
18522 }, {
18523 name: 'font-weight',
18524 type: t.fontWeight,
18525 triggersBounds: diff.any
18526 }, {
18527 name: 'font-size',
18528 type: t.size,
18529 triggersBounds: diff.any
18530 }, {
18531 name: 'text-transform',
18532 type: t.textTransform,
18533 triggersBounds: diff.any
18534 }, {
18535 name: 'text-wrap',
18536 type: t.textWrap,
18537 triggersBounds: diff.any
18538 }, {
18539 name: 'text-overflow-wrap',
18540 type: t.textOverflowWrap,
18541 triggersBounds: diff.any
18542 }, {
18543 name: 'text-max-width',
18544 type: t.size,
18545 triggersBounds: diff.any
18546 }, {
18547 name: 'text-outline-width',
18548 type: t.size,
18549 triggersBounds: diff.any
18550 }, {
18551 name: 'line-height',
18552 type: t.positiveNumber,
18553 triggersBounds: diff.any
18554 }];
18555 var commonLabel = [{
18556 name: 'text-valign',
18557 type: t.valign,
18558 triggersBounds: diff.any
18559 }, {
18560 name: 'text-halign',
18561 type: t.halign,
18562 triggersBounds: diff.any
18563 }, {
18564 name: 'color',
18565 type: t.color
18566 }, {
18567 name: 'text-outline-color',
18568 type: t.color
18569 }, {
18570 name: 'text-outline-opacity',
18571 type: t.zeroOneNumber
18572 }, {
18573 name: 'text-background-color',
18574 type: t.color
18575 }, {
18576 name: 'text-background-opacity',
18577 type: t.zeroOneNumber
18578 }, {
18579 name: 'text-background-padding',
18580 type: t.size,
18581 triggersBounds: diff.any
18582 }, {
18583 name: 'text-border-opacity',
18584 type: t.zeroOneNumber
18585 }, {
18586 name: 'text-border-color',
18587 type: t.color
18588 }, {
18589 name: 'text-border-width',
18590 type: t.size,
18591 triggersBounds: diff.any
18592 }, {
18593 name: 'text-border-style',
18594 type: t.borderStyle,
18595 triggersBounds: diff.any
18596 }, {
18597 name: 'text-background-shape',
18598 type: t.textBackgroundShape,
18599 triggersBounds: diff.any
18600 }, {
18601 name: 'text-justification',
18602 type: t.justification
18603 }];
18604 var behavior = [{
18605 name: 'events',
18606 type: t.bool
18607 }, {
18608 name: 'text-events',
18609 type: t.bool
18610 }];
18611 var visibility = [{
18612 name: 'display',
18613 type: t.display,
18614 triggersZOrder: diff.any,
18615 triggersBounds: diff.any,
18616 triggersBoundsOfParallelBeziers: true
18617 }, {
18618 name: 'visibility',
18619 type: t.visibility,
18620 triggersZOrder: diff.any
18621 }, {
18622 name: 'opacity',
18623 type: t.zeroOneNumber,
18624 triggersZOrder: diff.zeroNonZero
18625 }, {
18626 name: 'text-opacity',
18627 type: t.zeroOneNumber
18628 }, {
18629 name: 'min-zoomed-font-size',
18630 type: t.size
18631 }, {
18632 name: 'z-compound-depth',
18633 type: t.zCompoundDepth,
18634 triggersZOrder: diff.any
18635 }, {
18636 name: 'z-index-compare',
18637 type: t.zIndexCompare,
18638 triggersZOrder: diff.any
18639 }, {
18640 name: 'z-index',
18641 type: t.nonNegativeInt,
18642 triggersZOrder: diff.any
18643 }];
18644 var overlay = [{
18645 name: 'overlay-padding',
18646 type: t.size,
18647 triggersBounds: diff.any
18648 }, {
18649 name: 'overlay-color',
18650 type: t.color
18651 }, {
18652 name: 'overlay-opacity',
18653 type: t.zeroOneNumber,
18654 triggersBounds: diff.zeroNonZero
18655 }, {
18656 name: 'overlay-shape',
18657 type: t.overlayShape,
18658 triggersBounds: diff.any
18659 }];
18660 var underlay = [{
18661 name: 'underlay-padding',
18662 type: t.size,
18663 triggersBounds: diff.any
18664 }, {
18665 name: 'underlay-color',
18666 type: t.color
18667 }, {
18668 name: 'underlay-opacity',
18669 type: t.zeroOneNumber,
18670 triggersBounds: diff.zeroNonZero
18671 }, {
18672 name: 'underlay-shape',
18673 type: t.overlayShape,
18674 triggersBounds: diff.any
18675 }];
18676 var transition = [{
18677 name: 'transition-property',
18678 type: t.propList
18679 }, {
18680 name: 'transition-duration',
18681 type: t.time
18682 }, {
18683 name: 'transition-delay',
18684 type: t.time
18685 }, {
18686 name: 'transition-timing-function',
18687 type: t.easing
18688 }];
18689
18690 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
18691 if (parsedProp.value === 'label') {
18692 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
18693 } else {
18694 return parsedProp.pfValue;
18695 }
18696 };
18697
18698 var nodeBody = [{
18699 name: 'height',
18700 type: t.nodeSize,
18701 triggersBounds: diff.any,
18702 hashOverride: nodeSizeHashOverride
18703 }, {
18704 name: 'width',
18705 type: t.nodeSize,
18706 triggersBounds: diff.any,
18707 hashOverride: nodeSizeHashOverride
18708 }, {
18709 name: 'shape',
18710 type: t.nodeShape,
18711 triggersBounds: diff.any
18712 }, {
18713 name: 'shape-polygon-points',
18714 type: t.polygonPointList,
18715 triggersBounds: diff.any
18716 }, {
18717 name: 'background-color',
18718 type: t.color
18719 }, {
18720 name: 'background-fill',
18721 type: t.fill
18722 }, {
18723 name: 'background-opacity',
18724 type: t.zeroOneNumber
18725 }, {
18726 name: 'background-blacken',
18727 type: t.nOneOneNumber
18728 }, {
18729 name: 'background-gradient-stop-colors',
18730 type: t.colors
18731 }, {
18732 name: 'background-gradient-stop-positions',
18733 type: t.percentages
18734 }, {
18735 name: 'background-gradient-direction',
18736 type: t.gradientDirection
18737 }, {
18738 name: 'padding',
18739 type: t.sizeMaybePercent,
18740 triggersBounds: diff.any
18741 }, {
18742 name: 'padding-relative-to',
18743 type: t.paddingRelativeTo,
18744 triggersBounds: diff.any
18745 }, {
18746 name: 'bounds-expansion',
18747 type: t.boundsExpansion,
18748 triggersBounds: diff.any
18749 }];
18750 var nodeBorder = [{
18751 name: 'border-color',
18752 type: t.color
18753 }, {
18754 name: 'border-opacity',
18755 type: t.zeroOneNumber
18756 }, {
18757 name: 'border-width',
18758 type: t.size,
18759 triggersBounds: diff.any
18760 }, {
18761 name: 'border-style',
18762 type: t.borderStyle
18763 }];
18764 var backgroundImage = [{
18765 name: 'background-image',
18766 type: t.urls
18767 }, {
18768 name: 'background-image-crossorigin',
18769 type: t.bgCrossOrigin
18770 }, {
18771 name: 'background-image-opacity',
18772 type: t.zeroOneNumbers
18773 }, {
18774 name: 'background-image-containment',
18775 type: t.bgContainment
18776 }, {
18777 name: 'background-image-smoothing',
18778 type: t.bools
18779 }, {
18780 name: 'background-position-x',
18781 type: t.bgPos
18782 }, {
18783 name: 'background-position-y',
18784 type: t.bgPos
18785 }, {
18786 name: 'background-width-relative-to',
18787 type: t.bgRelativeTo
18788 }, {
18789 name: 'background-height-relative-to',
18790 type: t.bgRelativeTo
18791 }, {
18792 name: 'background-repeat',
18793 type: t.bgRepeat
18794 }, {
18795 name: 'background-fit',
18796 type: t.bgFit
18797 }, {
18798 name: 'background-clip',
18799 type: t.bgClip
18800 }, {
18801 name: 'background-width',
18802 type: t.bgWH
18803 }, {
18804 name: 'background-height',
18805 type: t.bgWH
18806 }, {
18807 name: 'background-offset-x',
18808 type: t.bgPos
18809 }, {
18810 name: 'background-offset-y',
18811 type: t.bgPos
18812 }];
18813 var compound = [{
18814 name: 'position',
18815 type: t.position,
18816 triggersBounds: diff.any
18817 }, {
18818 name: 'compound-sizing-wrt-labels',
18819 type: t.compoundIncludeLabels,
18820 triggersBounds: diff.any
18821 }, {
18822 name: 'min-width',
18823 type: t.size,
18824 triggersBounds: diff.any
18825 }, {
18826 name: 'min-width-bias-left',
18827 type: t.sizeMaybePercent,
18828 triggersBounds: diff.any
18829 }, {
18830 name: 'min-width-bias-right',
18831 type: t.sizeMaybePercent,
18832 triggersBounds: diff.any
18833 }, {
18834 name: 'min-height',
18835 type: t.size,
18836 triggersBounds: diff.any
18837 }, {
18838 name: 'min-height-bias-top',
18839 type: t.sizeMaybePercent,
18840 triggersBounds: diff.any
18841 }, {
18842 name: 'min-height-bias-bottom',
18843 type: t.sizeMaybePercent,
18844 triggersBounds: diff.any
18845 }];
18846 var edgeLine = [{
18847 name: 'line-style',
18848 type: t.lineStyle
18849 }, {
18850 name: 'line-color',
18851 type: t.color
18852 }, {
18853 name: 'line-fill',
18854 type: t.fill
18855 }, {
18856 name: 'line-cap',
18857 type: t.lineCap
18858 }, {
18859 name: 'line-opacity',
18860 type: t.zeroOneNumber
18861 }, {
18862 name: 'line-dash-pattern',
18863 type: t.numbers
18864 }, {
18865 name: 'line-dash-offset',
18866 type: t.number
18867 }, {
18868 name: 'line-gradient-stop-colors',
18869 type: t.colors
18870 }, {
18871 name: 'line-gradient-stop-positions',
18872 type: t.percentages
18873 }, {
18874 name: 'curve-style',
18875 type: t.curveStyle,
18876 triggersBounds: diff.any,
18877 triggersBoundsOfParallelBeziers: true
18878 }, {
18879 name: 'haystack-radius',
18880 type: t.zeroOneNumber,
18881 triggersBounds: diff.any
18882 }, {
18883 name: 'source-endpoint',
18884 type: t.edgeEndpoint,
18885 triggersBounds: diff.any
18886 }, {
18887 name: 'target-endpoint',
18888 type: t.edgeEndpoint,
18889 triggersBounds: diff.any
18890 }, {
18891 name: 'control-point-step-size',
18892 type: t.size,
18893 triggersBounds: diff.any
18894 }, {
18895 name: 'control-point-distances',
18896 type: t.bidirectionalSizes,
18897 triggersBounds: diff.any
18898 }, {
18899 name: 'control-point-weights',
18900 type: t.numbers,
18901 triggersBounds: diff.any
18902 }, {
18903 name: 'segment-distances',
18904 type: t.bidirectionalSizes,
18905 triggersBounds: diff.any
18906 }, {
18907 name: 'segment-weights',
18908 type: t.numbers,
18909 triggersBounds: diff.any
18910 }, {
18911 name: 'taxi-turn',
18912 type: t.bidirectionalSizeMaybePercent,
18913 triggersBounds: diff.any
18914 }, {
18915 name: 'taxi-turn-min-distance',
18916 type: t.size,
18917 triggersBounds: diff.any
18918 }, {
18919 name: 'taxi-direction',
18920 type: t.axisDirection,
18921 triggersBounds: diff.any
18922 }, {
18923 name: 'edge-distances',
18924 type: t.edgeDistances,
18925 triggersBounds: diff.any
18926 }, {
18927 name: 'arrow-scale',
18928 type: t.positiveNumber,
18929 triggersBounds: diff.any
18930 }, {
18931 name: 'loop-direction',
18932 type: t.angle,
18933 triggersBounds: diff.any
18934 }, {
18935 name: 'loop-sweep',
18936 type: t.angle,
18937 triggersBounds: diff.any
18938 }, {
18939 name: 'source-distance-from-node',
18940 type: t.size,
18941 triggersBounds: diff.any
18942 }, {
18943 name: 'target-distance-from-node',
18944 type: t.size,
18945 triggersBounds: diff.any
18946 }];
18947 var ghost = [{
18948 name: 'ghost',
18949 type: t.bool,
18950 triggersBounds: diff.any
18951 }, {
18952 name: 'ghost-offset-x',
18953 type: t.bidirectionalSize,
18954 triggersBounds: diff.any
18955 }, {
18956 name: 'ghost-offset-y',
18957 type: t.bidirectionalSize,
18958 triggersBounds: diff.any
18959 }, {
18960 name: 'ghost-opacity',
18961 type: t.zeroOneNumber
18962 }];
18963 var core = [{
18964 name: 'selection-box-color',
18965 type: t.color
18966 }, {
18967 name: 'selection-box-opacity',
18968 type: t.zeroOneNumber
18969 }, {
18970 name: 'selection-box-border-color',
18971 type: t.color
18972 }, {
18973 name: 'selection-box-border-width',
18974 type: t.size
18975 }, {
18976 name: 'active-bg-color',
18977 type: t.color
18978 }, {
18979 name: 'active-bg-opacity',
18980 type: t.zeroOneNumber
18981 }, {
18982 name: 'active-bg-size',
18983 type: t.size
18984 }, {
18985 name: 'outside-texture-bg-color',
18986 type: t.color
18987 }, {
18988 name: 'outside-texture-bg-opacity',
18989 type: t.zeroOneNumber
18990 }]; // pie backgrounds for nodes
18991
18992 var pie = [];
18993 styfn$2.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
18994
18995 pie.push({
18996 name: 'pie-size',
18997 type: t.sizeMaybePercent
18998 });
18999
19000 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
19001 pie.push({
19002 name: 'pie-' + i + '-background-color',
19003 type: t.color
19004 });
19005 pie.push({
19006 name: 'pie-' + i + '-background-size',
19007 type: t.percent
19008 });
19009 pie.push({
19010 name: 'pie-' + i + '-background-opacity',
19011 type: t.zeroOneNumber
19012 });
19013 } // edge arrows
19014
19015
19016 var edgeArrow = [];
19017 var arrowPrefixes = styfn$2.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
19018 [{
19019 name: 'arrow-shape',
19020 type: t.arrowShape,
19021 triggersBounds: diff.any
19022 }, {
19023 name: 'arrow-color',
19024 type: t.color
19025 }, {
19026 name: 'arrow-fill',
19027 type: t.arrowFill
19028 }].forEach(function (prop) {
19029 arrowPrefixes.forEach(function (prefix) {
19030 var name = prefix + '-' + prop.name;
19031 var type = prop.type,
19032 triggersBounds = prop.triggersBounds;
19033 edgeArrow.push({
19034 name: name,
19035 type: type,
19036 triggersBounds: triggersBounds
19037 });
19038 });
19039 }, {});
19040 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);
19041 var propGroups = styfn$2.propertyGroups = {
19042 // common to all eles
19043 behavior: behavior,
19044 transition: transition,
19045 visibility: visibility,
19046 overlay: overlay,
19047 underlay: underlay,
19048 ghost: ghost,
19049 // labels
19050 commonLabel: commonLabel,
19051 labelDimensions: labelDimensions,
19052 mainLabel: mainLabel,
19053 sourceLabel: sourceLabel,
19054 targetLabel: targetLabel,
19055 // node props
19056 nodeBody: nodeBody,
19057 nodeBorder: nodeBorder,
19058 backgroundImage: backgroundImage,
19059 pie: pie,
19060 compound: compound,
19061 // edge props
19062 edgeLine: edgeLine,
19063 edgeArrow: edgeArrow,
19064 core: core
19065 };
19066 var propGroupNames = styfn$2.propertyGroupNames = {};
19067 var propGroupKeys = styfn$2.propertyGroupKeys = Object.keys(propGroups);
19068 propGroupKeys.forEach(function (key) {
19069 propGroupNames[key] = propGroups[key].map(function (prop) {
19070 return prop.name;
19071 });
19072 propGroups[key].forEach(function (prop) {
19073 return prop.groupKey = key;
19074 });
19075 }); // define aliases
19076
19077 var aliases = styfn$2.aliases = [{
19078 name: 'content',
19079 pointsTo: 'label'
19080 }, {
19081 name: 'control-point-distance',
19082 pointsTo: 'control-point-distances'
19083 }, {
19084 name: 'control-point-weight',
19085 pointsTo: 'control-point-weights'
19086 }, {
19087 name: 'edge-text-rotation',
19088 pointsTo: 'text-rotation'
19089 }, {
19090 name: 'padding-left',
19091 pointsTo: 'padding'
19092 }, {
19093 name: 'padding-right',
19094 pointsTo: 'padding'
19095 }, {
19096 name: 'padding-top',
19097 pointsTo: 'padding'
19098 }, {
19099 name: 'padding-bottom',
19100 pointsTo: 'padding'
19101 }]; // list of property names
19102
19103 styfn$2.propertyNames = props.map(function (p) {
19104 return p.name;
19105 }); // allow access of properties by name ( e.g. style.properties.height )
19106
19107 for (var _i = 0; _i < props.length; _i++) {
19108 var prop = props[_i];
19109 props[prop.name] = prop; // allow lookup by name
19110 } // map aliases
19111
19112
19113 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
19114 var alias = aliases[_i2];
19115 var pointsToProp = props[alias.pointsTo];
19116 var aliasProp = {
19117 name: alias.name,
19118 alias: true,
19119 pointsTo: pointsToProp
19120 }; // add alias prop for parsing
19121
19122 props.push(aliasProp);
19123 props[alias.name] = aliasProp; // allow lookup by name
19124 }
19125 })();
19126
19127 styfn$2.getDefaultProperty = function (name) {
19128 return this.getDefaultProperties()[name];
19129 };
19130
19131 styfn$2.getDefaultProperties = function () {
19132 var _p = this._private;
19133
19134 if (_p.defaultProperties != null) {
19135 return _p.defaultProperties;
19136 }
19137
19138 var rawProps = extend({
19139 // core props
19140 'selection-box-color': '#ddd',
19141 'selection-box-opacity': 0.65,
19142 'selection-box-border-color': '#aaa',
19143 'selection-box-border-width': 1,
19144 'active-bg-color': 'black',
19145 'active-bg-opacity': 0.15,
19146 'active-bg-size': 30,
19147 'outside-texture-bg-color': '#000',
19148 'outside-texture-bg-opacity': 0.125,
19149 // common node/edge props
19150 'events': 'yes',
19151 'text-events': 'no',
19152 'text-valign': 'top',
19153 'text-halign': 'center',
19154 'text-justification': 'auto',
19155 'line-height': 1,
19156 'color': '#000',
19157 'text-outline-color': '#000',
19158 'text-outline-width': 0,
19159 'text-outline-opacity': 1,
19160 'text-opacity': 1,
19161 'text-decoration': 'none',
19162 'text-transform': 'none',
19163 'text-wrap': 'none',
19164 'text-overflow-wrap': 'whitespace',
19165 'text-max-width': 9999,
19166 'text-background-color': '#000',
19167 'text-background-opacity': 0,
19168 'text-background-shape': 'rectangle',
19169 'text-background-padding': 0,
19170 'text-border-opacity': 0,
19171 'text-border-width': 0,
19172 'text-border-style': 'solid',
19173 'text-border-color': '#000',
19174 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
19175 'font-style': 'normal',
19176 'font-weight': 'normal',
19177 'font-size': 16,
19178 'min-zoomed-font-size': 0,
19179 'text-rotation': 'none',
19180 'source-text-rotation': 'none',
19181 'target-text-rotation': 'none',
19182 'visibility': 'visible',
19183 'display': 'element',
19184 'opacity': 1,
19185 'z-compound-depth': 'auto',
19186 'z-index-compare': 'auto',
19187 'z-index': 0,
19188 'label': '',
19189 'text-margin-x': 0,
19190 'text-margin-y': 0,
19191 'source-label': '',
19192 'source-text-offset': 0,
19193 'source-text-margin-x': 0,
19194 'source-text-margin-y': 0,
19195 'target-label': '',
19196 'target-text-offset': 0,
19197 'target-text-margin-x': 0,
19198 'target-text-margin-y': 0,
19199 'overlay-opacity': 0,
19200 'overlay-color': '#000',
19201 'overlay-padding': 10,
19202 'overlay-shape': 'round-rectangle',
19203 'underlay-opacity': 0,
19204 'underlay-color': '#000',
19205 'underlay-padding': 10,
19206 'underlay-shape': 'round-rectangle',
19207 'transition-property': 'none',
19208 'transition-duration': 0,
19209 'transition-delay': 0,
19210 'transition-timing-function': 'linear',
19211 // node props
19212 'background-blacken': 0,
19213 'background-color': '#999',
19214 'background-fill': 'solid',
19215 'background-opacity': 1,
19216 'background-image': 'none',
19217 'background-image-crossorigin': 'anonymous',
19218 'background-image-opacity': 1,
19219 'background-image-containment': 'inside',
19220 'background-image-smoothing': 'yes',
19221 'background-position-x': '50%',
19222 'background-position-y': '50%',
19223 'background-offset-x': 0,
19224 'background-offset-y': 0,
19225 'background-width-relative-to': 'include-padding',
19226 'background-height-relative-to': 'include-padding',
19227 'background-repeat': 'no-repeat',
19228 'background-fit': 'none',
19229 'background-clip': 'node',
19230 'background-width': 'auto',
19231 'background-height': 'auto',
19232 'border-color': '#000',
19233 'border-opacity': 1,
19234 'border-width': 0,
19235 'border-style': 'solid',
19236 'height': 30,
19237 'width': 30,
19238 'shape': 'ellipse',
19239 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
19240 'bounds-expansion': 0,
19241 // node gradient
19242 'background-gradient-direction': 'to-bottom',
19243 'background-gradient-stop-colors': '#999',
19244 'background-gradient-stop-positions': '0%',
19245 // ghost props
19246 'ghost': 'no',
19247 'ghost-offset-y': 0,
19248 'ghost-offset-x': 0,
19249 'ghost-opacity': 0,
19250 // compound props
19251 'padding': 0,
19252 'padding-relative-to': 'width',
19253 'position': 'origin',
19254 'compound-sizing-wrt-labels': 'include',
19255 'min-width': 0,
19256 'min-width-bias-left': 0,
19257 'min-width-bias-right': 0,
19258 'min-height': 0,
19259 'min-height-bias-top': 0,
19260 'min-height-bias-bottom': 0
19261 }, {
19262 // node pie bg
19263 'pie-size': '100%'
19264 }, [{
19265 name: 'pie-{{i}}-background-color',
19266 value: 'black'
19267 }, {
19268 name: 'pie-{{i}}-background-size',
19269 value: '0%'
19270 }, {
19271 name: 'pie-{{i}}-background-opacity',
19272 value: 1
19273 }].reduce(function (css, prop) {
19274 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
19275 var name = prop.name.replace('{{i}}', i);
19276 var val = prop.value;
19277 css[name] = val;
19278 }
19279
19280 return css;
19281 }, {}), {
19282 // edge props
19283 'line-style': 'solid',
19284 'line-color': '#999',
19285 'line-fill': 'solid',
19286 'line-cap': 'butt',
19287 'line-opacity': 1,
19288 'line-gradient-stop-colors': '#999',
19289 'line-gradient-stop-positions': '0%',
19290 'control-point-step-size': 40,
19291 'control-point-weights': 0.5,
19292 'segment-weights': 0.5,
19293 'segment-distances': 20,
19294 'taxi-turn': '50%',
19295 'taxi-turn-min-distance': 10,
19296 'taxi-direction': 'auto',
19297 'edge-distances': 'intersection',
19298 'curve-style': 'haystack',
19299 'haystack-radius': 0,
19300 'arrow-scale': 1,
19301 'loop-direction': '-45deg',
19302 'loop-sweep': '-90deg',
19303 'source-distance-from-node': 0,
19304 'target-distance-from-node': 0,
19305 'source-endpoint': 'outside-to-node',
19306 'target-endpoint': 'outside-to-node',
19307 'line-dash-pattern': [6, 3],
19308 'line-dash-offset': 0
19309 }, [{
19310 name: 'arrow-shape',
19311 value: 'none'
19312 }, {
19313 name: 'arrow-color',
19314 value: '#999'
19315 }, {
19316 name: 'arrow-fill',
19317 value: 'filled'
19318 }].reduce(function (css, prop) {
19319 styfn$2.arrowPrefixes.forEach(function (prefix) {
19320 var name = prefix + '-' + prop.name;
19321 var val = prop.value;
19322 css[name] = val;
19323 });
19324 return css;
19325 }, {}));
19326 var parsedProps = {};
19327
19328 for (var i = 0; i < this.properties.length; i++) {
19329 var prop = this.properties[i];
19330
19331 if (prop.pointsTo) {
19332 continue;
19333 }
19334
19335 var name = prop.name;
19336 var val = rawProps[name];
19337 var parsedProp = this.parse(name, val);
19338 parsedProps[name] = parsedProp;
19339 }
19340
19341 _p.defaultProperties = parsedProps;
19342 return _p.defaultProperties;
19343 };
19344
19345 styfn$2.addDefaultStylesheet = function () {
19346 this.selector(':parent').css({
19347 'shape': 'rectangle',
19348 'padding': 10,
19349 'background-color': '#eee',
19350 'border-color': '#ccc',
19351 'border-width': 1
19352 }).selector('edge').css({
19353 'width': 3
19354 }).selector(':loop').css({
19355 'curve-style': 'bezier'
19356 }).selector('edge:compound').css({
19357 'curve-style': 'bezier',
19358 'source-endpoint': 'outside-to-line',
19359 'target-endpoint': 'outside-to-line'
19360 }).selector(':selected').css({
19361 'background-color': '#0169D9',
19362 'line-color': '#0169D9',
19363 'source-arrow-color': '#0169D9',
19364 'target-arrow-color': '#0169D9',
19365 'mid-source-arrow-color': '#0169D9',
19366 'mid-target-arrow-color': '#0169D9'
19367 }).selector(':parent:selected').css({
19368 'background-color': '#CCE1F9',
19369 'border-color': '#aec8e5'
19370 }).selector(':active').css({
19371 'overlay-color': 'black',
19372 'overlay-padding': 10,
19373 'overlay-opacity': 0.25
19374 });
19375 this.defaultLength = this.length;
19376 };
19377
19378 var styfn$1 = {}; // a caching layer for property parsing
19379
19380 styfn$1.parse = function (name, value, propIsBypass, propIsFlat) {
19381 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
19382
19383 if (fn$6(value)) {
19384 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
19385 }
19386
19387 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
19388 var bypassKey = propIsBypass ? 't' : 'f';
19389 var valueKey = '' + value;
19390 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
19391 var propCache = self.propCache = self.propCache || [];
19392 var ret;
19393
19394 if (!(ret = propCache[argHash])) {
19395 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
19396 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
19397 // - mappings can't be shared b/c mappings are per-element
19398
19399
19400 if (propIsBypass || propIsFlat === 'mapping') {
19401 // need a copy since props are mutated later in their lifecycles
19402 ret = copy(ret);
19403
19404 if (ret) {
19405 ret.value = copy(ret.value); // because it could be an array, e.g. colour
19406 }
19407 }
19408
19409 return ret;
19410 };
19411
19412 styfn$1.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
19413 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
19414
19415 if (!prop && value != null) {
19416 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
19417 }
19418
19419 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
19420 warn('The style value of `label` is deprecated for `' + prop.name + '`');
19421 }
19422
19423 return prop;
19424 }; // parse a property; return null on invalid; return parsed property otherwise
19425 // fields :
19426 // - name : the name of the property
19427 // - value : the parsed, native-typed value of the property
19428 // - strValue : a string value that represents the property value in valid css
19429 // - bypass : true iff the property is a bypass property
19430
19431
19432 styfn$1.parseImpl = function (name, value, propIsBypass, propIsFlat) {
19433 var self = this;
19434 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
19435
19436 var property = self.properties[name];
19437 var passedValue = value;
19438 var types = self.types;
19439
19440 if (!property) {
19441 return null;
19442 } // return null on property of unknown name
19443
19444
19445 if (value === undefined) {
19446 return null;
19447 } // can't assign undefined
19448 // the property may be an alias
19449
19450
19451 if (property.alias) {
19452 property = property.pointsTo;
19453 name = property.name;
19454 }
19455
19456 var valueIsString = string(value);
19457
19458 if (valueIsString) {
19459 // trim the value to make parsing easier
19460 value = value.trim();
19461 }
19462
19463 var type = property.type;
19464
19465 if (!type) {
19466 return null;
19467 } // no type, no luck
19468 // check if bypass is null or empty string (i.e. indication to delete bypass property)
19469
19470
19471 if (propIsBypass && (value === '' || value === null)) {
19472 return {
19473 name: name,
19474 value: value,
19475 bypass: true,
19476 deleteBypass: true
19477 };
19478 } // check if value is a function used as a mapper
19479
19480
19481 if (fn$6(value)) {
19482 return {
19483 name: name,
19484 value: value,
19485 strValue: 'fn',
19486 mapped: types.fn,
19487 bypass: propIsBypass
19488 };
19489 } // check if value is mapped
19490
19491
19492 var data, mapData;
19493
19494 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))) {
19495 if (propIsBypass) {
19496 return false;
19497 } // mappers not allowed in bypass
19498
19499
19500 var mapped = types.data;
19501 return {
19502 name: name,
19503 value: data,
19504 strValue: '' + value,
19505 mapped: mapped,
19506 field: data[1],
19507 bypass: propIsBypass
19508 };
19509 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
19510 if (propIsBypass) {
19511 return false;
19512 } // mappers not allowed in bypass
19513
19514
19515 if (type.multiple) {
19516 return false;
19517 } // impossible to map to num
19518
19519
19520 var _mapped = types.mapData; // we can map only if the type is a colour or a number
19521
19522 if (!(type.color || type.number)) {
19523 return false;
19524 }
19525
19526 var valueMin = this.parse(name, mapData[4]); // parse to validate
19527
19528 if (!valueMin || valueMin.mapped) {
19529 return false;
19530 } // can't be invalid or mapped
19531
19532
19533 var valueMax = this.parse(name, mapData[5]); // parse to validate
19534
19535 if (!valueMax || valueMax.mapped) {
19536 return false;
19537 } // can't be invalid or mapped
19538 // check if valueMin and valueMax are the same
19539
19540
19541 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
19542 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
19543 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
19544 } else if (type.color) {
19545 var c1 = valueMin.value;
19546 var c2 = valueMax.value;
19547 var same = c1[0] === c2[0] // red
19548 && c1[1] === c2[1] // green
19549 && c1[2] === c2[2] // blue
19550 && ( // optional alpha
19551 c1[3] === c2[3] // same alpha outright
19552 || (c1[3] == null || c1[3] === 1 // full opacity for colour 1?
19553 ) && (c2[3] == null || c2[3] === 1) // full opacity for colour 2?
19554 );
19555
19556 if (same) {
19557 return false;
19558 } // can't make a mapper without a range
19559
19560 }
19561
19562 return {
19563 name: name,
19564 value: mapData,
19565 strValue: '' + value,
19566 mapped: _mapped,
19567 field: mapData[1],
19568 fieldMin: parseFloat(mapData[2]),
19569 // min & max are numeric
19570 fieldMax: parseFloat(mapData[3]),
19571 valueMin: valueMin.value,
19572 valueMax: valueMax.value,
19573 bypass: propIsBypass
19574 };
19575 }
19576
19577 if (type.multiple && propIsFlat !== 'multiple') {
19578 var vals;
19579
19580 if (valueIsString) {
19581 vals = value.split(/\s+/);
19582 } else if (array(value)) {
19583 vals = value;
19584 } else {
19585 vals = [value];
19586 }
19587
19588 if (type.evenMultiple && vals.length % 2 !== 0) {
19589 return null;
19590 }
19591
19592 var valArr = [];
19593 var unitsArr = [];
19594 var pfValArr = [];
19595 var strVal = '';
19596 var hasEnum = false;
19597
19598 for (var i = 0; i < vals.length; i++) {
19599 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
19600 hasEnum = hasEnum || string(p.value);
19601 valArr.push(p.value);
19602 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
19603 unitsArr.push(p.units);
19604 strVal += (i > 0 ? ' ' : '') + p.strValue;
19605 }
19606
19607 if (type.validate && !type.validate(valArr, unitsArr)) {
19608 return null;
19609 }
19610
19611 if (type.singleEnum && hasEnum) {
19612 if (valArr.length === 1 && string(valArr[0])) {
19613 return {
19614 name: name,
19615 value: valArr[0],
19616 strValue: valArr[0],
19617 bypass: propIsBypass
19618 };
19619 } else {
19620 return null;
19621 }
19622 }
19623
19624 return {
19625 name: name,
19626 value: valArr,
19627 pfValue: pfValArr,
19628 strValue: strVal,
19629 bypass: propIsBypass,
19630 units: unitsArr
19631 };
19632 } // several types also allow enums
19633
19634
19635 var checkEnums = function checkEnums() {
19636 for (var _i = 0; _i < type.enums.length; _i++) {
19637 var en = type.enums[_i];
19638
19639 if (en === value) {
19640 return {
19641 name: name,
19642 value: value,
19643 strValue: '' + value,
19644 bypass: propIsBypass
19645 };
19646 }
19647 }
19648
19649 return null;
19650 }; // check the type and return the appropriate object
19651
19652
19653 if (type.number) {
19654 var units;
19655 var implicitUnits = 'px'; // not set => px
19656
19657 if (type.units) {
19658 // use specified units if set
19659 units = type.units;
19660 }
19661
19662 if (type.implicitUnits) {
19663 implicitUnits = type.implicitUnits;
19664 }
19665
19666 if (!type.unitless) {
19667 if (valueIsString) {
19668 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
19669
19670 if (units) {
19671 unitsRegex = units;
19672 } // only allow explicit units if so set
19673
19674
19675 var match = value.match('^(' + number + ')(' + unitsRegex + ')?' + '$');
19676
19677 if (match) {
19678 value = match[1];
19679 units = match[2] || implicitUnits;
19680 }
19681 } else if (!units || type.implicitUnits) {
19682 units = implicitUnits; // implicitly px if unspecified
19683 }
19684 }
19685
19686 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
19687
19688 if (isNaN(value) && type.enums === undefined) {
19689 return null;
19690 } // check if this number type also accepts special keywords in place of numbers
19691 // (i.e. `left`, `auto`, etc)
19692
19693
19694 if (isNaN(value) && type.enums !== undefined) {
19695 value = passedValue;
19696 return checkEnums();
19697 } // check if value must be an integer
19698
19699
19700 if (type.integer && !integer(value)) {
19701 return null;
19702 } // check value is within range
19703
19704
19705 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
19706 return null;
19707 }
19708
19709 var ret = {
19710 name: name,
19711 value: value,
19712 strValue: '' + value + (units ? units : ''),
19713 units: units,
19714 bypass: propIsBypass
19715 }; // normalise value in pixels
19716
19717 if (type.unitless || units !== 'px' && units !== 'em') {
19718 ret.pfValue = value;
19719 } else {
19720 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
19721 } // normalise value in ms
19722
19723
19724 if (units === 'ms' || units === 's') {
19725 ret.pfValue = units === 'ms' ? value : 1000 * value;
19726 } // normalise value in rad
19727
19728
19729 if (units === 'deg' || units === 'rad') {
19730 ret.pfValue = units === 'rad' ? value : deg2rad(value);
19731 } // normalize value in %
19732
19733
19734 if (units === '%') {
19735 ret.pfValue = value / 100;
19736 }
19737
19738 return ret;
19739 } else if (type.propList) {
19740 var props = [];
19741 var propsStr = '' + value;
19742
19743 if (propsStr === 'none') ; else {
19744 // go over each prop
19745 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
19746
19747 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
19748 var propName = propsSplit[_i2].trim();
19749
19750 if (self.properties[propName]) {
19751 props.push(propName);
19752 } else {
19753 warn('`' + propName + '` is not a valid property name');
19754 }
19755 }
19756
19757 if (props.length === 0) {
19758 return null;
19759 }
19760 }
19761
19762 return {
19763 name: name,
19764 value: props,
19765 strValue: props.length === 0 ? 'none' : props.join(' '),
19766 bypass: propIsBypass
19767 };
19768 } else if (type.color) {
19769 var tuple = color2tuple(value);
19770
19771 if (!tuple) {
19772 return null;
19773 }
19774
19775 return {
19776 name: name,
19777 value: tuple,
19778 pfValue: tuple,
19779 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
19780 // n.b. no spaces b/c of multiple support
19781 bypass: propIsBypass
19782 };
19783 } else if (type.regex || type.regexes) {
19784 // first check enums
19785 if (type.enums) {
19786 var enumProp = checkEnums();
19787
19788 if (enumProp) {
19789 return enumProp;
19790 }
19791 }
19792
19793 var regexes = type.regexes ? type.regexes : [type.regex];
19794
19795 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
19796 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
19797
19798 var m = regex.exec(value);
19799
19800 if (m) {
19801 // regex matches
19802 return {
19803 name: name,
19804 value: type.singleRegexMatchValue ? m[1] : m,
19805 strValue: '' + value,
19806 bypass: propIsBypass
19807 };
19808 }
19809 }
19810
19811 return null; // didn't match any
19812 } else if (type.string) {
19813 // just return
19814 return {
19815 name: name,
19816 value: '' + value,
19817 strValue: '' + value,
19818 bypass: propIsBypass
19819 };
19820 } else if (type.enums) {
19821 // check enums last because it's a combo type in others
19822 return checkEnums();
19823 } else {
19824 return null; // not a type we can handle
19825 }
19826 };
19827
19828 var Style = function Style(cy) {
19829 if (!(this instanceof Style)) {
19830 return new Style(cy);
19831 }
19832
19833 if (!core(cy)) {
19834 error('A style must have a core reference');
19835 return;
19836 }
19837
19838 this._private = {
19839 cy: cy,
19840 coreStyle: {}
19841 };
19842 this.length = 0;
19843 this.resetToDefault();
19844 };
19845
19846 var styfn = Style.prototype;
19847
19848 styfn.instanceString = function () {
19849 return 'style';
19850 }; // remove all contexts
19851
19852
19853 styfn.clear = function () {
19854 var _p = this._private;
19855 var cy = _p.cy;
19856 var eles = cy.elements();
19857
19858 for (var i = 0; i < this.length; i++) {
19859 this[i] = undefined;
19860 }
19861
19862 this.length = 0;
19863 _p.contextStyles = {};
19864 _p.propDiffs = {};
19865 this.cleanElements(eles, true);
19866 eles.forEach(function (ele) {
19867 var ele_p = ele[0]._private;
19868 ele_p.styleDirty = true;
19869 ele_p.appliedInitStyle = false;
19870 });
19871 return this; // chaining
19872 };
19873
19874 styfn.resetToDefault = function () {
19875 this.clear();
19876 this.addDefaultStylesheet();
19877 return this;
19878 }; // builds a style object for the 'core' selector
19879
19880
19881 styfn.core = function (propName) {
19882 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
19883 }; // create a new context from the specified selector string and switch to that context
19884
19885
19886 styfn.selector = function (selectorStr) {
19887 // 'core' is a special case and does not need a selector
19888 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
19889 var i = this.length++; // new context means new index
19890
19891 this[i] = {
19892 selector: selector,
19893 properties: [],
19894 mappedProperties: [],
19895 index: i
19896 };
19897 return this; // chaining
19898 }; // add one or many css rules to the current context
19899
19900
19901 styfn.css = function () {
19902 var self = this;
19903 var args = arguments;
19904
19905 if (args.length === 1) {
19906 var map = args[0];
19907
19908 for (var i = 0; i < self.properties.length; i++) {
19909 var prop = self.properties[i];
19910 var mapVal = map[prop.name];
19911
19912 if (mapVal === undefined) {
19913 mapVal = map[dash2camel(prop.name)];
19914 }
19915
19916 if (mapVal !== undefined) {
19917 this.cssRule(prop.name, mapVal);
19918 }
19919 }
19920 } else if (args.length === 2) {
19921 this.cssRule(args[0], args[1]);
19922 } // do nothing if args are invalid
19923
19924
19925 return this; // chaining
19926 };
19927
19928 styfn.style = styfn.css; // add a single css rule to the current context
19929
19930 styfn.cssRule = function (name, value) {
19931 // name-value pair
19932 var property = this.parse(name, value); // add property to current context if valid
19933
19934 if (property) {
19935 var i = this.length - 1;
19936 this[i].properties.push(property);
19937 this[i].properties[property.name] = property; // allow access by name as well
19938
19939 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
19940 this._private.hasPie = true;
19941 }
19942
19943 if (property.mapped) {
19944 this[i].mappedProperties.push(property);
19945 } // add to core style if necessary
19946
19947
19948 var currentSelectorIsCore = !this[i].selector;
19949
19950 if (currentSelectorIsCore) {
19951 this._private.coreStyle[property.name] = property;
19952 }
19953 }
19954
19955 return this; // chaining
19956 };
19957
19958 styfn.append = function (style) {
19959 if (stylesheet(style)) {
19960 style.appendToStyle(this);
19961 } else if (array(style)) {
19962 this.appendFromJson(style);
19963 } else if (string(style)) {
19964 this.appendFromString(style);
19965 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
19966
19967
19968 return this;
19969 }; // static function
19970
19971
19972 Style.fromJson = function (cy, json) {
19973 var style = new Style(cy);
19974 style.fromJson(json);
19975 return style;
19976 };
19977
19978 Style.fromString = function (cy, string) {
19979 return new Style(cy).fromString(string);
19980 };
19981
19982 [styfn$8, styfn$7, styfn$6, styfn$5, styfn$4, styfn$3, styfn$2, styfn$1].forEach(function (props) {
19983 extend(styfn, props);
19984 });
19985 Style.types = styfn.types;
19986 Style.properties = styfn.properties;
19987 Style.propertyGroups = styfn.propertyGroups;
19988 Style.propertyGroupNames = styfn.propertyGroupNames;
19989 Style.propertyGroupKeys = styfn.propertyGroupKeys;
19990
19991 var corefn$2 = {
19992 style: function style(newStyle) {
19993 if (newStyle) {
19994 var s = this.setStyle(newStyle);
19995 s.update();
19996 }
19997
19998 return this._private.style;
19999 },
20000 setStyle: function setStyle(style) {
20001 var _p = this._private;
20002
20003 if (stylesheet(style)) {
20004 _p.style = style.generateStyle(this);
20005 } else if (array(style)) {
20006 _p.style = Style.fromJson(this, style);
20007 } else if (string(style)) {
20008 _p.style = Style.fromString(this, style);
20009 } else {
20010 _p.style = Style(this);
20011 }
20012
20013 return _p.style;
20014 },
20015 // e.g. cy.data() changed => recalc ele mappers
20016 updateStyle: function updateStyle() {
20017 this.mutableElements().updateStyle(); // just send to all eles
20018 }
20019 };
20020
20021 var defaultSelectionType = 'single';
20022 var corefn$1 = {
20023 autolock: function autolock(bool) {
20024 if (bool !== undefined) {
20025 this._private.autolock = bool ? true : false;
20026 } else {
20027 return this._private.autolock;
20028 }
20029
20030 return this; // chaining
20031 },
20032 autoungrabify: function autoungrabify(bool) {
20033 if (bool !== undefined) {
20034 this._private.autoungrabify = bool ? true : false;
20035 } else {
20036 return this._private.autoungrabify;
20037 }
20038
20039 return this; // chaining
20040 },
20041 autounselectify: function autounselectify(bool) {
20042 if (bool !== undefined) {
20043 this._private.autounselectify = bool ? true : false;
20044 } else {
20045 return this._private.autounselectify;
20046 }
20047
20048 return this; // chaining
20049 },
20050 selectionType: function selectionType(selType) {
20051 var _p = this._private;
20052
20053 if (_p.selectionType == null) {
20054 _p.selectionType = defaultSelectionType;
20055 }
20056
20057 if (selType !== undefined) {
20058 if (selType === 'additive' || selType === 'single') {
20059 _p.selectionType = selType;
20060 }
20061 } else {
20062 return _p.selectionType;
20063 }
20064
20065 return this;
20066 },
20067 panningEnabled: function panningEnabled(bool) {
20068 if (bool !== undefined) {
20069 this._private.panningEnabled = bool ? true : false;
20070 } else {
20071 return this._private.panningEnabled;
20072 }
20073
20074 return this; // chaining
20075 },
20076 userPanningEnabled: function userPanningEnabled(bool) {
20077 if (bool !== undefined) {
20078 this._private.userPanningEnabled = bool ? true : false;
20079 } else {
20080 return this._private.userPanningEnabled;
20081 }
20082
20083 return this; // chaining
20084 },
20085 zoomingEnabled: function zoomingEnabled(bool) {
20086 if (bool !== undefined) {
20087 this._private.zoomingEnabled = bool ? true : false;
20088 } else {
20089 return this._private.zoomingEnabled;
20090 }
20091
20092 return this; // chaining
20093 },
20094 userZoomingEnabled: function userZoomingEnabled(bool) {
20095 if (bool !== undefined) {
20096 this._private.userZoomingEnabled = bool ? true : false;
20097 } else {
20098 return this._private.userZoomingEnabled;
20099 }
20100
20101 return this; // chaining
20102 },
20103 boxSelectionEnabled: function boxSelectionEnabled(bool) {
20104 if (bool !== undefined) {
20105 this._private.boxSelectionEnabled = bool ? true : false;
20106 } else {
20107 return this._private.boxSelectionEnabled;
20108 }
20109
20110 return this; // chaining
20111 },
20112 pan: function pan() {
20113 var args = arguments;
20114 var pan = this._private.pan;
20115 var dim, val, dims, x, y;
20116
20117 switch (args.length) {
20118 case 0:
20119 // .pan()
20120 return pan;
20121
20122 case 1:
20123 if (string(args[0])) {
20124 // .pan('x')
20125 dim = args[0];
20126 return pan[dim];
20127 } else if (plainObject(args[0])) {
20128 // .pan({ x: 0, y: 100 })
20129 if (!this._private.panningEnabled) {
20130 return this;
20131 }
20132
20133 dims = args[0];
20134 x = dims.x;
20135 y = dims.y;
20136
20137 if (number$1(x)) {
20138 pan.x = x;
20139 }
20140
20141 if (number$1(y)) {
20142 pan.y = y;
20143 }
20144
20145 this.emit('pan viewport');
20146 }
20147
20148 break;
20149
20150 case 2:
20151 // .pan('x', 100)
20152 if (!this._private.panningEnabled) {
20153 return this;
20154 }
20155
20156 dim = args[0];
20157 val = args[1];
20158
20159 if ((dim === 'x' || dim === 'y') && number$1(val)) {
20160 pan[dim] = val;
20161 }
20162
20163 this.emit('pan viewport');
20164 break;
20165 // invalid
20166 }
20167
20168 this.notify('viewport');
20169 return this; // chaining
20170 },
20171 panBy: function panBy(arg0, arg1) {
20172 var args = arguments;
20173 var pan = this._private.pan;
20174 var dim, val, dims, x, y;
20175
20176 if (!this._private.panningEnabled) {
20177 return this;
20178 }
20179
20180 switch (args.length) {
20181 case 1:
20182 if (plainObject(arg0)) {
20183 // .panBy({ x: 0, y: 100 })
20184 dims = args[0];
20185 x = dims.x;
20186 y = dims.y;
20187
20188 if (number$1(x)) {
20189 pan.x += x;
20190 }
20191
20192 if (number$1(y)) {
20193 pan.y += y;
20194 }
20195
20196 this.emit('pan viewport');
20197 }
20198
20199 break;
20200
20201 case 2:
20202 // .panBy('x', 100)
20203 dim = arg0;
20204 val = arg1;
20205
20206 if ((dim === 'x' || dim === 'y') && number$1(val)) {
20207 pan[dim] += val;
20208 }
20209
20210 this.emit('pan viewport');
20211 break;
20212 // invalid
20213 }
20214
20215 this.notify('viewport');
20216 return this; // chaining
20217 },
20218 fit: function fit(elements, padding) {
20219 var viewportState = this.getFitViewport(elements, padding);
20220
20221 if (viewportState) {
20222 var _p = this._private;
20223 _p.zoom = viewportState.zoom;
20224 _p.pan = viewportState.pan;
20225 this.emit('pan zoom viewport');
20226 this.notify('viewport');
20227 }
20228
20229 return this; // chaining
20230 },
20231 getFitViewport: function getFitViewport(elements, padding) {
20232 if (number$1(elements) && padding === undefined) {
20233 // elements is optional
20234 padding = elements;
20235 elements = undefined;
20236 }
20237
20238 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
20239 return;
20240 }
20241
20242 var bb;
20243
20244 if (string(elements)) {
20245 var sel = elements;
20246 elements = this.$(sel);
20247 } else if (boundingBox(elements)) {
20248 // assume bb
20249 var bbe = elements;
20250 bb = {
20251 x1: bbe.x1,
20252 y1: bbe.y1,
20253 x2: bbe.x2,
20254 y2: bbe.y2
20255 };
20256 bb.w = bb.x2 - bb.x1;
20257 bb.h = bb.y2 - bb.y1;
20258 } else if (!elementOrCollection(elements)) {
20259 elements = this.mutableElements();
20260 }
20261
20262 if (elementOrCollection(elements) && elements.empty()) {
20263 return;
20264 } // can't fit to nothing
20265
20266
20267 bb = bb || elements.boundingBox();
20268 var w = this.width();
20269 var h = this.height();
20270 var zoom;
20271 padding = number$1(padding) ? padding : 0;
20272
20273 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
20274 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
20275
20276 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
20277 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
20278 var pan = {
20279 // now pan to middle
20280 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
20281 y: (h - zoom * (bb.y1 + bb.y2)) / 2
20282 };
20283 return {
20284 zoom: zoom,
20285 pan: pan
20286 };
20287 }
20288
20289 return;
20290 },
20291 zoomRange: function zoomRange(min, max) {
20292 var _p = this._private;
20293
20294 if (max == null) {
20295 var opts = min;
20296 min = opts.min;
20297 max = opts.max;
20298 }
20299
20300 if (number$1(min) && number$1(max) && min <= max) {
20301 _p.minZoom = min;
20302 _p.maxZoom = max;
20303 } else if (number$1(min) && max === undefined && min <= _p.maxZoom) {
20304 _p.minZoom = min;
20305 } else if (number$1(max) && min === undefined && max >= _p.minZoom) {
20306 _p.maxZoom = max;
20307 }
20308
20309 return this;
20310 },
20311 minZoom: function minZoom(zoom) {
20312 if (zoom === undefined) {
20313 return this._private.minZoom;
20314 } else {
20315 return this.zoomRange({
20316 min: zoom
20317 });
20318 }
20319 },
20320 maxZoom: function maxZoom(zoom) {
20321 if (zoom === undefined) {
20322 return this._private.maxZoom;
20323 } else {
20324 return this.zoomRange({
20325 max: zoom
20326 });
20327 }
20328 },
20329 getZoomedViewport: function getZoomedViewport(params) {
20330 var _p = this._private;
20331 var currentPan = _p.pan;
20332 var currentZoom = _p.zoom;
20333 var pos; // in rendered px
20334
20335 var zoom;
20336 var bail = false;
20337
20338 if (!_p.zoomingEnabled) {
20339 // zooming disabled
20340 bail = true;
20341 }
20342
20343 if (number$1(params)) {
20344 // then set the zoom
20345 zoom = params;
20346 } else if (plainObject(params)) {
20347 // then zoom about a point
20348 zoom = params.level;
20349
20350 if (params.position != null) {
20351 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
20352 } else if (params.renderedPosition != null) {
20353 pos = params.renderedPosition;
20354 }
20355
20356 if (pos != null && !_p.panningEnabled) {
20357 // panning disabled
20358 bail = true;
20359 }
20360 } // crop zoom
20361
20362
20363 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
20364 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
20365
20366 if (bail || !number$1(zoom) || zoom === currentZoom || pos != null && (!number$1(pos.x) || !number$1(pos.y))) {
20367 return null;
20368 }
20369
20370 if (pos != null) {
20371 // set zoom about position
20372 var pan1 = currentPan;
20373 var zoom1 = currentZoom;
20374 var zoom2 = zoom;
20375 var pan2 = {
20376 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
20377 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
20378 };
20379 return {
20380 zoomed: true,
20381 panned: true,
20382 zoom: zoom2,
20383 pan: pan2
20384 };
20385 } else {
20386 // just set the zoom
20387 return {
20388 zoomed: true,
20389 panned: false,
20390 zoom: zoom,
20391 pan: currentPan
20392 };
20393 }
20394 },
20395 zoom: function zoom(params) {
20396 if (params === undefined) {
20397 // get
20398 return this._private.zoom;
20399 } else {
20400 // set
20401 var vp = this.getZoomedViewport(params);
20402 var _p = this._private;
20403
20404 if (vp == null || !vp.zoomed) {
20405 return this;
20406 }
20407
20408 _p.zoom = vp.zoom;
20409
20410 if (vp.panned) {
20411 _p.pan.x = vp.pan.x;
20412 _p.pan.y = vp.pan.y;
20413 }
20414
20415 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
20416 this.notify('viewport');
20417 return this; // chaining
20418 }
20419 },
20420 viewport: function viewport(opts) {
20421 var _p = this._private;
20422 var zoomDefd = true;
20423 var panDefd = true;
20424 var events = []; // to trigger
20425
20426 var zoomFailed = false;
20427 var panFailed = false;
20428
20429 if (!opts) {
20430 return this;
20431 }
20432
20433 if (!number$1(opts.zoom)) {
20434 zoomDefd = false;
20435 }
20436
20437 if (!plainObject(opts.pan)) {
20438 panDefd = false;
20439 }
20440
20441 if (!zoomDefd && !panDefd) {
20442 return this;
20443 }
20444
20445 if (zoomDefd) {
20446 var z = opts.zoom;
20447
20448 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
20449 zoomFailed = true;
20450 } else {
20451 _p.zoom = z;
20452 events.push('zoom');
20453 }
20454 }
20455
20456 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
20457 var p = opts.pan;
20458
20459 if (number$1(p.x)) {
20460 _p.pan.x = p.x;
20461 panFailed = false;
20462 }
20463
20464 if (number$1(p.y)) {
20465 _p.pan.y = p.y;
20466 panFailed = false;
20467 }
20468
20469 if (!panFailed) {
20470 events.push('pan');
20471 }
20472 }
20473
20474 if (events.length > 0) {
20475 events.push('viewport');
20476 this.emit(events.join(' '));
20477 this.notify('viewport');
20478 }
20479
20480 return this; // chaining
20481 },
20482 center: function center(elements) {
20483 var pan = this.getCenterPan(elements);
20484
20485 if (pan) {
20486 this._private.pan = pan;
20487 this.emit('pan viewport');
20488 this.notify('viewport');
20489 }
20490
20491 return this; // chaining
20492 },
20493 getCenterPan: function getCenterPan(elements, zoom) {
20494 if (!this._private.panningEnabled) {
20495 return;
20496 }
20497
20498 if (string(elements)) {
20499 var selector = elements;
20500 elements = this.mutableElements().filter(selector);
20501 } else if (!elementOrCollection(elements)) {
20502 elements = this.mutableElements();
20503 }
20504
20505 if (elements.length === 0) {
20506 return;
20507 } // can't centre pan to nothing
20508
20509
20510 var bb = elements.boundingBox();
20511 var w = this.width();
20512 var h = this.height();
20513 zoom = zoom === undefined ? this._private.zoom : zoom;
20514 var pan = {
20515 // middle
20516 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
20517 y: (h - zoom * (bb.y1 + bb.y2)) / 2
20518 };
20519 return pan;
20520 },
20521 reset: function reset() {
20522 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
20523 return this;
20524 }
20525
20526 this.viewport({
20527 pan: {
20528 x: 0,
20529 y: 0
20530 },
20531 zoom: 1
20532 });
20533 return this; // chaining
20534 },
20535 invalidateSize: function invalidateSize() {
20536 this._private.sizeCache = null;
20537 },
20538 size: function size() {
20539 var _p = this._private;
20540 var container = _p.container;
20541 return _p.sizeCache = _p.sizeCache || (container ? function () {
20542 var style = window$1.getComputedStyle(container);
20543
20544 var val = function val(name) {
20545 return parseFloat(style.getPropertyValue(name));
20546 };
20547
20548 return {
20549 width: container.clientWidth - val('padding-left') - val('padding-right'),
20550 height: container.clientHeight - val('padding-top') - val('padding-bottom')
20551 };
20552 }() : {
20553 // fallback if no container (not 0 b/c can be used for dividing etc)
20554 width: 1,
20555 height: 1
20556 });
20557 },
20558 width: function width() {
20559 return this.size().width;
20560 },
20561 height: function height() {
20562 return this.size().height;
20563 },
20564 extent: function extent() {
20565 var pan = this._private.pan;
20566 var zoom = this._private.zoom;
20567 var rb = this.renderedExtent();
20568 var b = {
20569 x1: (rb.x1 - pan.x) / zoom,
20570 x2: (rb.x2 - pan.x) / zoom,
20571 y1: (rb.y1 - pan.y) / zoom,
20572 y2: (rb.y2 - pan.y) / zoom
20573 };
20574 b.w = b.x2 - b.x1;
20575 b.h = b.y2 - b.y1;
20576 return b;
20577 },
20578 renderedExtent: function renderedExtent() {
20579 var width = this.width();
20580 var height = this.height();
20581 return {
20582 x1: 0,
20583 y1: 0,
20584 x2: width,
20585 y2: height,
20586 w: width,
20587 h: height
20588 };
20589 },
20590 multiClickDebounceTime: function multiClickDebounceTime(_int) {
20591 if (_int) this._private.multiClickDebounceTime = _int;else return this._private.multiClickDebounceTime;
20592 return this; // chaining
20593 }
20594 }; // aliases
20595
20596 corefn$1.centre = corefn$1.center; // backwards compatibility
20597
20598 corefn$1.autolockNodes = corefn$1.autolock;
20599 corefn$1.autoungrabifyNodes = corefn$1.autoungrabify;
20600
20601 var fn = {
20602 data: define.data({
20603 field: 'data',
20604 bindingEvent: 'data',
20605 allowBinding: true,
20606 allowSetting: true,
20607 settingEvent: 'data',
20608 settingTriggersEvent: true,
20609 triggerFnName: 'trigger',
20610 allowGetting: true,
20611 updateStyle: true
20612 }),
20613 removeData: define.removeData({
20614 field: 'data',
20615 event: 'data',
20616 triggerFnName: 'trigger',
20617 triggerEvent: true,
20618 updateStyle: true
20619 }),
20620 scratch: define.data({
20621 field: 'scratch',
20622 bindingEvent: 'scratch',
20623 allowBinding: true,
20624 allowSetting: true,
20625 settingEvent: 'scratch',
20626 settingTriggersEvent: true,
20627 triggerFnName: 'trigger',
20628 allowGetting: true,
20629 updateStyle: true
20630 }),
20631 removeScratch: define.removeData({
20632 field: 'scratch',
20633 event: 'scratch',
20634 triggerFnName: 'trigger',
20635 triggerEvent: true,
20636 updateStyle: true
20637 })
20638 }; // aliases
20639
20640 fn.attr = fn.data;
20641 fn.removeAttr = fn.removeData;
20642
20643 var Core = function Core(opts) {
20644 var cy = this;
20645 opts = extend({}, opts);
20646 var container = opts.container; // allow for passing a wrapped jquery object
20647 // e.g. cytoscape({ container: $('#cy') })
20648
20649 if (container && !htmlElement(container) && htmlElement(container[0])) {
20650 container = container[0];
20651 }
20652
20653 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
20654
20655 reg = reg || {};
20656
20657 if (reg && reg.cy) {
20658 reg.cy.destroy();
20659 reg = {}; // old instance => replace reg completely
20660 }
20661
20662 var readies = reg.readies = reg.readies || [];
20663
20664 if (container) {
20665 container._cyreg = reg;
20666 } // make sure container assoc'd reg points to this cy
20667
20668
20669 reg.cy = cy;
20670 var head = window$1 !== undefined && container !== undefined && !opts.headless;
20671 var options = opts;
20672 options.layout = extend({
20673 name: head ? 'grid' : 'null'
20674 }, options.layout);
20675 options.renderer = extend({
20676 name: head ? 'canvas' : 'null'
20677 }, options.renderer);
20678
20679 var defVal = function defVal(def, val, altVal) {
20680 if (val !== undefined) {
20681 return val;
20682 } else if (altVal !== undefined) {
20683 return altVal;
20684 } else {
20685 return def;
20686 }
20687 };
20688
20689 var _p = this._private = {
20690 container: container,
20691 // html dom ele container
20692 ready: false,
20693 // whether ready has been triggered
20694 options: options,
20695 // cached options
20696 elements: new Collection(this),
20697 // elements in the graph
20698 listeners: [],
20699 // list of listeners
20700 aniEles: new Collection(this),
20701 // elements being animated
20702 data: options.data || {},
20703 // data for the core
20704 scratch: {},
20705 // scratch object for core
20706 layout: null,
20707 renderer: null,
20708 destroyed: false,
20709 // whether destroy was called
20710 notificationsEnabled: true,
20711 // whether notifications are sent to the renderer
20712 minZoom: 1e-50,
20713 maxZoom: 1e50,
20714 zoomingEnabled: defVal(true, options.zoomingEnabled),
20715 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
20716 panningEnabled: defVal(true, options.panningEnabled),
20717 userPanningEnabled: defVal(true, options.userPanningEnabled),
20718 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
20719 autolock: defVal(false, options.autolock, options.autolockNodes),
20720 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
20721 autounselectify: defVal(false, options.autounselectify),
20722 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
20723 zoom: number$1(options.zoom) ? options.zoom : 1,
20724 pan: {
20725 x: plainObject(options.pan) && number$1(options.pan.x) ? options.pan.x : 0,
20726 y: plainObject(options.pan) && number$1(options.pan.y) ? options.pan.y : 0
20727 },
20728 animation: {
20729 // object for currently-running animations
20730 current: [],
20731 queue: []
20732 },
20733 hasCompoundNodes: false,
20734 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
20735 };
20736
20737 this.createEmitter(); // set selection type
20738
20739 this.selectionType(options.selectionType); // init zoom bounds
20740
20741 this.zoomRange({
20742 min: options.minZoom,
20743 max: options.maxZoom
20744 });
20745
20746 var loadExtData = function loadExtData(extData, next) {
20747 var anyIsPromise = extData.some(promise);
20748
20749 if (anyIsPromise) {
20750 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
20751 } else {
20752 next(extData); // exec synchronously for convenience
20753 }
20754 }; // start with the default stylesheet so we have something before loading an external stylesheet
20755
20756
20757 if (_p.styleEnabled) {
20758 cy.setStyle([]);
20759 } // create the renderer
20760
20761
20762 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
20763
20764 cy.initRenderer(rendererOptions);
20765
20766 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
20767 cy.notifications(false); // remove old elements
20768
20769 var oldEles = cy.mutableElements();
20770
20771 if (oldEles.length > 0) {
20772 oldEles.remove();
20773 }
20774
20775 if (elements != null) {
20776 if (plainObject(elements) || array(elements)) {
20777 cy.add(elements);
20778 }
20779 }
20780
20781 cy.one('layoutready', function (e) {
20782 cy.notifications(true);
20783 cy.emit(e); // we missed this event by turning notifications off, so pass it on
20784
20785 cy.one('load', onload);
20786 cy.emitAndNotify('load');
20787 }).one('layoutstop', function () {
20788 cy.one('done', ondone);
20789 cy.emit('done');
20790 });
20791 var layoutOpts = extend({}, cy._private.options.layout);
20792 layoutOpts.eles = cy.elements();
20793 cy.layout(layoutOpts).run();
20794 };
20795
20796 loadExtData([options.style, options.elements], function (thens) {
20797 var initStyle = thens[0];
20798 var initEles = thens[1]; // init style
20799
20800 if (_p.styleEnabled) {
20801 cy.style().append(initStyle);
20802 } // initial load
20803
20804
20805 setElesAndLayout(initEles, function () {
20806 // onready
20807 cy.startAnimationLoop();
20808 _p.ready = true; // if a ready callback is specified as an option, the bind it
20809
20810 if (fn$6(options.ready)) {
20811 cy.on('ready', options.ready);
20812 } // bind all the ready handlers registered before creating this instance
20813
20814
20815 for (var i = 0; i < readies.length; i++) {
20816 var fn = readies[i];
20817 cy.on('ready', fn);
20818 }
20819
20820 if (reg) {
20821 reg.readies = [];
20822 } // 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
20823
20824
20825 cy.emit('ready');
20826 }, options.done);
20827 });
20828 };
20829
20830 var corefn = Core.prototype; // short alias
20831
20832 extend(corefn, {
20833 instanceString: function instanceString() {
20834 return 'core';
20835 },
20836 isReady: function isReady() {
20837 return this._private.ready;
20838 },
20839 destroyed: function destroyed() {
20840 return this._private.destroyed;
20841 },
20842 ready: function ready(fn) {
20843 if (this.isReady()) {
20844 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
20845 } else {
20846 this.on('ready', fn);
20847 }
20848
20849 return this;
20850 },
20851 destroy: function destroy() {
20852 var cy = this;
20853 if (cy.destroyed()) return;
20854 cy.stopAnimationLoop();
20855 cy.destroyRenderer();
20856 this.emit('destroy');
20857 cy._private.destroyed = true;
20858 return cy;
20859 },
20860 hasElementWithId: function hasElementWithId(id) {
20861 return this._private.elements.hasElementWithId(id);
20862 },
20863 getElementById: function getElementById(id) {
20864 return this._private.elements.getElementById(id);
20865 },
20866 hasCompoundNodes: function hasCompoundNodes() {
20867 return this._private.hasCompoundNodes;
20868 },
20869 headless: function headless() {
20870 return this._private.renderer.isHeadless();
20871 },
20872 styleEnabled: function styleEnabled() {
20873 return this._private.styleEnabled;
20874 },
20875 addToPool: function addToPool(eles) {
20876 this._private.elements.merge(eles);
20877
20878 return this; // chaining
20879 },
20880 removeFromPool: function removeFromPool(eles) {
20881 this._private.elements.unmerge(eles);
20882
20883 return this;
20884 },
20885 container: function container() {
20886 return this._private.container || null;
20887 },
20888 mount: function mount(container) {
20889 if (container == null) {
20890 return;
20891 }
20892
20893 var cy = this;
20894 var _p = cy._private;
20895 var options = _p.options;
20896
20897 if (!htmlElement(container) && htmlElement(container[0])) {
20898 container = container[0];
20899 }
20900
20901 cy.stopAnimationLoop();
20902 cy.destroyRenderer();
20903 _p.container = container;
20904 _p.styleEnabled = true;
20905 cy.invalidateSize();
20906 cy.initRenderer(extend({}, options, options.renderer, {
20907 // allow custom renderer name to be re-used, otherwise use canvas
20908 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
20909 }));
20910 cy.startAnimationLoop();
20911 cy.style(options.style);
20912 cy.emit('mount');
20913 return cy;
20914 },
20915 unmount: function unmount() {
20916 var cy = this;
20917 cy.stopAnimationLoop();
20918 cy.destroyRenderer();
20919 cy.initRenderer({
20920 name: 'null'
20921 });
20922 cy.emit('unmount');
20923 return cy;
20924 },
20925 options: function options() {
20926 return copy(this._private.options);
20927 },
20928 json: function json(obj) {
20929 var cy = this;
20930 var _p = cy._private;
20931 var eles = cy.mutableElements();
20932
20933 var getFreshRef = function getFreshRef(ele) {
20934 return cy.getElementById(ele.id());
20935 };
20936
20937 if (plainObject(obj)) {
20938 // set
20939 cy.startBatch();
20940
20941 if (obj.elements) {
20942 var idInJson = {};
20943
20944 var updateEles = function updateEles(jsons, gr) {
20945 var toAdd = [];
20946 var toMod = [];
20947
20948 for (var i = 0; i < jsons.length; i++) {
20949 var json = jsons[i];
20950
20951 if (!json.data.id) {
20952 warn('cy.json() cannot handle elements without an ID attribute');
20953 continue;
20954 }
20955
20956 var id = '' + json.data.id; // id must be string
20957
20958 var ele = cy.getElementById(id);
20959 idInJson[id] = true;
20960
20961 if (ele.length !== 0) {
20962 // existing element should be updated
20963 toMod.push({
20964 ele: ele,
20965 json: json
20966 });
20967 } else {
20968 // otherwise should be added
20969 if (gr) {
20970 json.group = gr;
20971 toAdd.push(json);
20972 } else {
20973 toAdd.push(json);
20974 }
20975 }
20976 }
20977
20978 cy.add(toAdd);
20979
20980 for (var _i = 0; _i < toMod.length; _i++) {
20981 var _toMod$_i = toMod[_i],
20982 _ele = _toMod$_i.ele,
20983 _json = _toMod$_i.json;
20984
20985 _ele.json(_json);
20986 }
20987 };
20988
20989 if (array(obj.elements)) {
20990 // elements: []
20991 updateEles(obj.elements);
20992 } else {
20993 // elements: { nodes: [], edges: [] }
20994 var grs = ['nodes', 'edges'];
20995
20996 for (var i = 0; i < grs.length; i++) {
20997 var gr = grs[i];
20998 var elements = obj.elements[gr];
20999
21000 if (array(elements)) {
21001 updateEles(elements, gr);
21002 }
21003 }
21004 }
21005
21006 var parentsToRemove = cy.collection();
21007 eles.filter(function (ele) {
21008 return !idInJson[ele.id()];
21009 }).forEach(function (ele) {
21010 if (ele.isParent()) {
21011 parentsToRemove.merge(ele);
21012 } else {
21013 ele.remove();
21014 }
21015 }); // so that children are not removed w/parent
21016
21017 parentsToRemove.forEach(function (ele) {
21018 return ele.children().move({
21019 parent: null
21020 });
21021 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
21022
21023 parentsToRemove.forEach(function (ele) {
21024 return getFreshRef(ele).remove();
21025 });
21026 }
21027
21028 if (obj.style) {
21029 cy.style(obj.style);
21030 }
21031
21032 if (obj.zoom != null && obj.zoom !== _p.zoom) {
21033 cy.zoom(obj.zoom);
21034 }
21035
21036 if (obj.pan) {
21037 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
21038 cy.pan(obj.pan);
21039 }
21040 }
21041
21042 if (obj.data) {
21043 cy.data(obj.data);
21044 }
21045
21046 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify', 'multiClickDebounceTime'];
21047
21048 for (var _i2 = 0; _i2 < fields.length; _i2++) {
21049 var f = fields[_i2];
21050
21051 if (obj[f] != null) {
21052 cy[f](obj[f]);
21053 }
21054 }
21055
21056 cy.endBatch();
21057 return this; // chaining
21058 } else {
21059 // get
21060 var flat = !!obj;
21061 var json = {};
21062
21063 if (flat) {
21064 json.elements = this.elements().map(function (ele) {
21065 return ele.json();
21066 });
21067 } else {
21068 json.elements = {};
21069 eles.forEach(function (ele) {
21070 var group = ele.group();
21071
21072 if (!json.elements[group]) {
21073 json.elements[group] = [];
21074 }
21075
21076 json.elements[group].push(ele.json());
21077 });
21078 }
21079
21080 if (this._private.styleEnabled) {
21081 json.style = cy.style().json();
21082 }
21083
21084 json.data = copy(cy.data());
21085 var options = _p.options;
21086 json.zoomingEnabled = _p.zoomingEnabled;
21087 json.userZoomingEnabled = _p.userZoomingEnabled;
21088 json.zoom = _p.zoom;
21089 json.minZoom = _p.minZoom;
21090 json.maxZoom = _p.maxZoom;
21091 json.panningEnabled = _p.panningEnabled;
21092 json.userPanningEnabled = _p.userPanningEnabled;
21093 json.pan = copy(_p.pan);
21094 json.boxSelectionEnabled = _p.boxSelectionEnabled;
21095 json.renderer = copy(options.renderer);
21096 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
21097 json.textureOnViewport = options.textureOnViewport;
21098 json.wheelSensitivity = options.wheelSensitivity;
21099 json.motionBlur = options.motionBlur;
21100 json.multiClickDebounceTime = options.multiClickDebounceTime;
21101 return json;
21102 }
21103 }
21104 });
21105 corefn.$id = corefn.getElementById;
21106 [corefn$9, corefn$8, elesfn, corefn$7, corefn$6, corefn$5, corefn$4, corefn$3, corefn$2, corefn$1, fn].forEach(function (props) {
21107 extend(corefn, props);
21108 });
21109
21110 /* eslint-disable no-unused-vars */
21111
21112 var defaults$7 = {
21113 fit: true,
21114 // whether to fit the viewport to the graph
21115 directed: false,
21116 // whether the tree is directed downwards (or edges can point in any direction if false)
21117 padding: 30,
21118 // padding on fit
21119 circle: false,
21120 // put depths in concentric circles if true, put depths top down if false
21121 grid: false,
21122 // whether to create an even grid into which the DAG is placed (circle:false only)
21123 spacingFactor: 1.75,
21124 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
21125 boundingBox: undefined,
21126 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21127 avoidOverlap: true,
21128 // prevents node overlap, may overflow boundingBox if not enough space
21129 nodeDimensionsIncludeLabels: false,
21130 // Excludes the label when calculating node bounding boxes for the layout algorithm
21131 roots: undefined,
21132 // the roots of the trees
21133 maximal: false,
21134 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
21135 depthSort: undefined,
21136 // a sorting function to order nodes at equal depth. e.g. function(a, b){ return a.data('weight') - b.data('weight') }
21137 animate: false,
21138 // whether to transition the node positions
21139 animationDuration: 500,
21140 // duration of animation in ms if enabled
21141 animationEasing: undefined,
21142 // easing of animation if enabled,
21143 animateFilter: function animateFilter(node, i) {
21144 return true;
21145 },
21146 // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
21147 ready: undefined,
21148 // callback on layoutready
21149 stop: undefined,
21150 // callback on layoutstop
21151 transform: function transform(node, position) {
21152 return position;
21153 } // transform a given node position. Useful for changing flow direction in discrete layouts
21154
21155 };
21156 /* eslint-enable */
21157
21158 var getInfo = function getInfo(ele) {
21159 return ele.scratch('breadthfirst');
21160 };
21161
21162 var setInfo = function setInfo(ele, obj) {
21163 return ele.scratch('breadthfirst', obj);
21164 };
21165
21166 function BreadthFirstLayout(options) {
21167 this.options = extend({}, defaults$7, options);
21168 }
21169
21170 BreadthFirstLayout.prototype.run = function () {
21171 var params = this.options;
21172 var options = params;
21173 var cy = params.cy;
21174 var eles = options.eles;
21175 var nodes = eles.nodes().filter(function (n) {
21176 return !n.isParent();
21177 });
21178 var graph = eles;
21179 var directed = options.directed;
21180 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
21181
21182 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21183 x1: 0,
21184 y1: 0,
21185 w: cy.width(),
21186 h: cy.height()
21187 });
21188 var roots;
21189
21190 if (elementOrCollection(options.roots)) {
21191 roots = options.roots;
21192 } else if (array(options.roots)) {
21193 var rootsArray = [];
21194
21195 for (var i = 0; i < options.roots.length; i++) {
21196 var id = options.roots[i];
21197 var ele = cy.getElementById(id);
21198 rootsArray.push(ele);
21199 }
21200
21201 roots = cy.collection(rootsArray);
21202 } else if (string(options.roots)) {
21203 roots = cy.$(options.roots);
21204 } else {
21205 if (directed) {
21206 roots = nodes.roots();
21207 } else {
21208 var components = eles.components();
21209 roots = cy.collection();
21210
21211 var _loop = function _loop(_i) {
21212 var comp = components[_i];
21213 var maxDegree = comp.maxDegree(false);
21214 var compRoots = comp.filter(function (ele) {
21215 return ele.degree(false) === maxDegree;
21216 });
21217 roots = roots.add(compRoots);
21218 };
21219
21220 for (var _i = 0; _i < components.length; _i++) {
21221 _loop(_i);
21222 }
21223 }
21224 }
21225
21226 var depths = [];
21227 var foundByBfs = {};
21228
21229 var addToDepth = function addToDepth(ele, d) {
21230 if (depths[d] == null) {
21231 depths[d] = [];
21232 }
21233
21234 var i = depths[d].length;
21235 depths[d].push(ele);
21236 setInfo(ele, {
21237 index: i,
21238 depth: d
21239 });
21240 };
21241
21242 var changeDepth = function changeDepth(ele, newDepth) {
21243 var _getInfo = getInfo(ele),
21244 depth = _getInfo.depth,
21245 index = _getInfo.index;
21246
21247 depths[depth][index] = null;
21248 addToDepth(ele, newDepth);
21249 }; // find the depths of the nodes
21250
21251
21252 graph.bfs({
21253 roots: roots,
21254 directed: options.directed,
21255 visit: function visit(node, edge, pNode, i, depth) {
21256 var ele = node[0];
21257 var id = ele.id();
21258 addToDepth(ele, depth);
21259 foundByBfs[id] = true;
21260 }
21261 }); // check for nodes not found by bfs
21262
21263 var orphanNodes = [];
21264
21265 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
21266 var _ele = nodes[_i2];
21267
21268 if (foundByBfs[_ele.id()]) {
21269 continue;
21270 } else {
21271 orphanNodes.push(_ele);
21272 }
21273 } // assign the nodes a depth and index
21274
21275
21276 var assignDepthsAt = function assignDepthsAt(i) {
21277 var eles = depths[i];
21278
21279 for (var j = 0; j < eles.length; j++) {
21280 var _ele2 = eles[j];
21281
21282 if (_ele2 == null) {
21283 eles.splice(j, 1);
21284 j--;
21285 continue;
21286 }
21287
21288 setInfo(_ele2, {
21289 depth: i,
21290 index: j
21291 });
21292 }
21293 };
21294
21295 var assignDepths = function assignDepths() {
21296 for (var _i3 = 0; _i3 < depths.length; _i3++) {
21297 assignDepthsAt(_i3);
21298 }
21299 };
21300
21301 var adjustMaximally = function adjustMaximally(ele, shifted) {
21302 var eInfo = getInfo(ele);
21303 var incomers = ele.incomers().filter(function (el) {
21304 return el.isNode() && eles.has(el);
21305 });
21306 var maxDepth = -1;
21307 var id = ele.id();
21308
21309 for (var k = 0; k < incomers.length; k++) {
21310 var incmr = incomers[k];
21311 var iInfo = getInfo(incmr);
21312 maxDepth = Math.max(maxDepth, iInfo.depth);
21313 }
21314
21315 if (eInfo.depth <= maxDepth) {
21316 if (shifted[id]) {
21317 return null;
21318 }
21319
21320 changeDepth(ele, maxDepth + 1);
21321 shifted[id] = true;
21322 return true;
21323 }
21324
21325 return false;
21326 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
21327
21328
21329 if (directed && maximal) {
21330 var Q = [];
21331 var shifted = {};
21332
21333 var enqueue = function enqueue(n) {
21334 return Q.push(n);
21335 };
21336
21337 var dequeue = function dequeue() {
21338 return Q.shift();
21339 };
21340
21341 nodes.forEach(function (n) {
21342 return Q.push(n);
21343 });
21344
21345 while (Q.length > 0) {
21346 var _ele3 = dequeue();
21347
21348 var didShift = adjustMaximally(_ele3, shifted);
21349
21350 if (didShift) {
21351 _ele3.outgoers().filter(function (el) {
21352 return el.isNode() && eles.has(el);
21353 }).forEach(enqueue);
21354 } else if (didShift === null) {
21355 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
21356 break; // exit on failure
21357 }
21358 }
21359 }
21360
21361 assignDepths(); // clear holes
21362 // find min distance we need to leave between nodes
21363
21364 var minDistance = 0;
21365
21366 if (options.avoidOverlap) {
21367 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
21368 var n = nodes[_i4];
21369 var nbb = n.layoutDimensions(options);
21370 var w = nbb.w;
21371 var h = nbb.h;
21372 minDistance = Math.max(minDistance, w, h);
21373 }
21374 } // get the weighted percent for an element based on its connectivity to other levels
21375
21376
21377 var cachedWeightedPercent = {};
21378
21379 var getWeightedPercent = function getWeightedPercent(ele) {
21380 if (cachedWeightedPercent[ele.id()]) {
21381 return cachedWeightedPercent[ele.id()];
21382 }
21383
21384 var eleDepth = getInfo(ele).depth;
21385 var neighbors = ele.neighborhood();
21386 var percent = 0;
21387 var samples = 0;
21388
21389 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
21390 var neighbor = neighbors[_i5];
21391
21392 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
21393 continue;
21394 }
21395
21396 var bf = getInfo(neighbor);
21397
21398 if (bf == null) {
21399 continue;
21400 }
21401
21402 var index = bf.index;
21403 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
21404
21405 if (index == null || depth == null) {
21406 continue;
21407 }
21408
21409 var nDepth = depths[depth].length;
21410
21411 if (depth < eleDepth) {
21412 // only get influenced by elements above
21413 percent += index / nDepth;
21414 samples++;
21415 }
21416 }
21417
21418 samples = Math.max(1, samples);
21419 percent = percent / samples;
21420
21421 if (samples === 0) {
21422 // put lone nodes at the start
21423 percent = 0;
21424 }
21425
21426 cachedWeightedPercent[ele.id()] = percent;
21427 return percent;
21428 }; // rearrange the indices in each depth level based on connectivity
21429
21430
21431 var sortFn = function sortFn(a, b) {
21432 var apct = getWeightedPercent(a);
21433 var bpct = getWeightedPercent(b);
21434 var diff = apct - bpct;
21435
21436 if (diff === 0) {
21437 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
21438 } else {
21439 return diff;
21440 }
21441 };
21442
21443 if (options.depthSort !== undefined) {
21444 sortFn = options.depthSort;
21445 } // sort each level to make connected nodes closer
21446
21447
21448 for (var _i6 = 0; _i6 < depths.length; _i6++) {
21449 depths[_i6].sort(sortFn);
21450
21451 assignDepthsAt(_i6);
21452 } // assign orphan nodes to a new top-level depth
21453
21454
21455 var orphanDepth = [];
21456
21457 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
21458 orphanDepth.push(orphanNodes[_i7]);
21459 }
21460
21461 depths.unshift(orphanDepth);
21462 assignDepths();
21463 var biggestDepthSize = 0;
21464
21465 for (var _i8 = 0; _i8 < depths.length; _i8++) {
21466 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
21467 }
21468
21469 var center = {
21470 x: bb.x1 + bb.w / 2,
21471 y: bb.x1 + bb.h / 2
21472 };
21473 var maxDepthSize = depths.reduce(function (max, eles) {
21474 return Math.max(max, eles.length);
21475 }, 0);
21476
21477 var getPosition = function getPosition(ele) {
21478 var _getInfo2 = getInfo(ele),
21479 depth = _getInfo2.depth,
21480 index = _getInfo2.index;
21481
21482 var depthSize = depths[depth].length;
21483 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
21484 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
21485 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
21486 radiusStepSize = Math.max(radiusStepSize, minDistance);
21487
21488 if (!options.circle) {
21489 var epos = {
21490 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
21491 y: (depth + 1) * distanceY
21492 };
21493 return epos;
21494 } else {
21495 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
21496 var theta = 2 * Math.PI / depths[depth].length * index;
21497
21498 if (depth === 0 && depths[0].length === 1) {
21499 radius = 1;
21500 }
21501
21502 return {
21503 x: center.x + radius * Math.cos(theta),
21504 y: center.y + radius * Math.sin(theta)
21505 };
21506 }
21507 };
21508
21509 eles.nodes().layoutPositions(this, options, getPosition);
21510 return this; // chaining
21511 };
21512
21513 var defaults$6 = {
21514 fit: true,
21515 // whether to fit the viewport to the graph
21516 padding: 30,
21517 // the padding on fit
21518 boundingBox: undefined,
21519 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21520 avoidOverlap: true,
21521 // prevents node overlap, may overflow boundingBox and radius if not enough space
21522 nodeDimensionsIncludeLabels: false,
21523 // Excludes the label when calculating node bounding boxes for the layout algorithm
21524 spacingFactor: undefined,
21525 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21526 radius: undefined,
21527 // the radius of the circle
21528 startAngle: 3 / 2 * Math.PI,
21529 // where nodes start in radians
21530 sweep: undefined,
21531 // how many radians should be between the first and last node (defaults to full circle)
21532 clockwise: true,
21533 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
21534 sort: undefined,
21535 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
21536 animate: false,
21537 // whether to transition the node positions
21538 animationDuration: 500,
21539 // duration of animation in ms if enabled
21540 animationEasing: undefined,
21541 // easing of animation if enabled
21542 animateFilter: function animateFilter(node, i) {
21543 return true;
21544 },
21545 // 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
21546 ready: undefined,
21547 // callback on layoutready
21548 stop: undefined,
21549 // callback on layoutstop
21550 transform: function transform(node, position) {
21551 return position;
21552 } // transform a given node position. Useful for changing flow direction in discrete layouts
21553
21554 };
21555
21556 function CircleLayout(options) {
21557 this.options = extend({}, defaults$6, options);
21558 }
21559
21560 CircleLayout.prototype.run = function () {
21561 var params = this.options;
21562 var options = params;
21563 var cy = params.cy;
21564 var eles = options.eles;
21565 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
21566 var nodes = eles.nodes().not(':parent');
21567
21568 if (options.sort) {
21569 nodes = nodes.sort(options.sort);
21570 }
21571
21572 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21573 x1: 0,
21574 y1: 0,
21575 w: cy.width(),
21576 h: cy.height()
21577 });
21578 var center = {
21579 x: bb.x1 + bb.w / 2,
21580 y: bb.y1 + bb.h / 2
21581 };
21582 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
21583 var dTheta = sweep / Math.max(1, nodes.length - 1);
21584 var r;
21585 var minDistance = 0;
21586
21587 for (var i = 0; i < nodes.length; i++) {
21588 var n = nodes[i];
21589 var nbb = n.layoutDimensions(options);
21590 var w = nbb.w;
21591 var h = nbb.h;
21592 minDistance = Math.max(minDistance, w, h);
21593 }
21594
21595 if (number$1(options.radius)) {
21596 r = options.radius;
21597 } else if (nodes.length <= 1) {
21598 r = 0;
21599 } else {
21600 r = Math.min(bb.h, bb.w) / 2 - minDistance;
21601 } // calculate the radius
21602
21603
21604 if (nodes.length > 1 && options.avoidOverlap) {
21605 // but only if more than one node (can't overlap)
21606 minDistance *= 1.75; // just to have some nice spacing
21607
21608 var dcos = Math.cos(dTheta) - Math.cos(0);
21609 var dsin = Math.sin(dTheta) - Math.sin(0);
21610 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
21611
21612 r = Math.max(rMin, r);
21613 }
21614
21615 var getPos = function getPos(ele, i) {
21616 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
21617 var rx = r * Math.cos(theta);
21618 var ry = r * Math.sin(theta);
21619 var pos = {
21620 x: center.x + rx,
21621 y: center.y + ry
21622 };
21623 return pos;
21624 };
21625
21626 eles.nodes().layoutPositions(this, options, getPos);
21627 return this; // chaining
21628 };
21629
21630 var defaults$5 = {
21631 fit: true,
21632 // whether to fit the viewport to the graph
21633 padding: 30,
21634 // the padding on fit
21635 startAngle: 3 / 2 * Math.PI,
21636 // where nodes start in radians
21637 sweep: undefined,
21638 // how many radians should be between the first and last node (defaults to full circle)
21639 clockwise: true,
21640 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
21641 equidistant: false,
21642 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
21643 minNodeSpacing: 10,
21644 // min spacing between outside of nodes (used for radius adjustment)
21645 boundingBox: undefined,
21646 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21647 avoidOverlap: true,
21648 // prevents node overlap, may overflow boundingBox if not enough space
21649 nodeDimensionsIncludeLabels: false,
21650 // Excludes the label when calculating node bounding boxes for the layout algorithm
21651 height: undefined,
21652 // height of layout area (overrides container height)
21653 width: undefined,
21654 // width of layout area (overrides container width)
21655 spacingFactor: undefined,
21656 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21657 concentric: function concentric(node) {
21658 // returns numeric value for each node, placing higher nodes in levels towards the centre
21659 return node.degree();
21660 },
21661 levelWidth: function levelWidth(nodes) {
21662 // the variation of concentric values in each level
21663 return nodes.maxDegree() / 4;
21664 },
21665 animate: false,
21666 // whether to transition the node positions
21667 animationDuration: 500,
21668 // duration of animation in ms if enabled
21669 animationEasing: undefined,
21670 // easing of animation if enabled
21671 animateFilter: function animateFilter(node, i) {
21672 return true;
21673 },
21674 // 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
21675 ready: undefined,
21676 // callback on layoutready
21677 stop: undefined,
21678 // callback on layoutstop
21679 transform: function transform(node, position) {
21680 return position;
21681 } // transform a given node position. Useful for changing flow direction in discrete layouts
21682
21683 };
21684
21685 function ConcentricLayout(options) {
21686 this.options = extend({}, defaults$5, options);
21687 }
21688
21689 ConcentricLayout.prototype.run = function () {
21690 var params = this.options;
21691 var options = params;
21692 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
21693 var cy = params.cy;
21694 var eles = options.eles;
21695 var nodes = eles.nodes().not(':parent');
21696 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21697 x1: 0,
21698 y1: 0,
21699 w: cy.width(),
21700 h: cy.height()
21701 });
21702 var center = {
21703 x: bb.x1 + bb.w / 2,
21704 y: bb.y1 + bb.h / 2
21705 };
21706 var nodeValues = []; // { node, value }
21707
21708 var maxNodeSize = 0;
21709
21710 for (var i = 0; i < nodes.length; i++) {
21711 var node = nodes[i];
21712 var value = void 0; // calculate the node value
21713
21714 value = options.concentric(node);
21715 nodeValues.push({
21716 value: value,
21717 node: node
21718 }); // for style mapping
21719
21720 node._private.scratch.concentric = value;
21721 } // in case we used the `concentric` in style
21722
21723
21724 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
21725
21726 for (var _i = 0; _i < nodes.length; _i++) {
21727 var _node = nodes[_i];
21728
21729 var nbb = _node.layoutDimensions(options);
21730
21731 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
21732 } // sort node values in descreasing order
21733
21734
21735 nodeValues.sort(function (a, b) {
21736 return b.value - a.value;
21737 });
21738 var levelWidth = options.levelWidth(nodes); // put the values into levels
21739
21740 var levels = [[]];
21741 var currentLevel = levels[0];
21742
21743 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
21744 var val = nodeValues[_i2];
21745
21746 if (currentLevel.length > 0) {
21747 var diff = Math.abs(currentLevel[0].value - val.value);
21748
21749 if (diff >= levelWidth) {
21750 currentLevel = [];
21751 levels.push(currentLevel);
21752 }
21753 }
21754
21755 currentLevel.push(val);
21756 } // create positions from levels
21757
21758
21759 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
21760
21761 if (!options.avoidOverlap) {
21762 // then strictly constrain to bb
21763 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
21764 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
21765 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
21766 minDist = Math.min(minDist, rStep);
21767 } // find the metrics for each level
21768
21769
21770 var r = 0;
21771
21772 for (var _i3 = 0; _i3 < levels.length; _i3++) {
21773 var level = levels[_i3];
21774 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
21775 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
21776
21777 if (level.length > 1 && options.avoidOverlap) {
21778 // but only if more than one node (can't overlap)
21779 var dcos = Math.cos(dTheta) - Math.cos(0);
21780 var dsin = Math.sin(dTheta) - Math.sin(0);
21781 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
21782
21783 r = Math.max(rMin, r);
21784 }
21785
21786 level.r = r;
21787 r += minDist;
21788 }
21789
21790 if (options.equidistant) {
21791 var rDeltaMax = 0;
21792 var _r = 0;
21793
21794 for (var _i4 = 0; _i4 < levels.length; _i4++) {
21795 var _level = levels[_i4];
21796 var rDelta = _level.r - _r;
21797 rDeltaMax = Math.max(rDeltaMax, rDelta);
21798 }
21799
21800 _r = 0;
21801
21802 for (var _i5 = 0; _i5 < levels.length; _i5++) {
21803 var _level2 = levels[_i5];
21804
21805 if (_i5 === 0) {
21806 _r = _level2.r;
21807 }
21808
21809 _level2.r = _r;
21810 _r += rDeltaMax;
21811 }
21812 } // calculate the node positions
21813
21814
21815 var pos = {}; // id => position
21816
21817 for (var _i6 = 0; _i6 < levels.length; _i6++) {
21818 var _level3 = levels[_i6];
21819 var _dTheta = _level3.dTheta;
21820 var _r2 = _level3.r;
21821
21822 for (var j = 0; j < _level3.length; j++) {
21823 var _val = _level3[j];
21824 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
21825 var p = {
21826 x: center.x + _r2 * Math.cos(theta),
21827 y: center.y + _r2 * Math.sin(theta)
21828 };
21829 pos[_val.node.id()] = p;
21830 }
21831 } // position the nodes
21832
21833
21834 eles.nodes().layoutPositions(this, options, function (ele) {
21835 var id = ele.id();
21836 return pos[id];
21837 });
21838 return this; // chaining
21839 };
21840
21841 /*
21842 The CoSE layout was written by Gerardo Huck.
21843 https://www.linkedin.com/in/gerardohuck/
21844
21845 Based on the following article:
21846 http://dl.acm.org/citation.cfm?id=1498047
21847
21848 Modifications tracked on Github.
21849 */
21850 var DEBUG;
21851 /**
21852 * @brief : default layout options
21853 */
21854
21855 var defaults$4 = {
21856 // Called on `layoutready`
21857 ready: function ready() {},
21858 // Called on `layoutstop`
21859 stop: function stop() {},
21860 // Whether to animate while running the layout
21861 // true : Animate continuously as the layout is running
21862 // false : Just show the end result
21863 // 'end' : Animate with the end result, from the initial positions to the end positions
21864 animate: true,
21865 // Easing of the animation for animate:'end'
21866 animationEasing: undefined,
21867 // The duration of the animation for animate:'end'
21868 animationDuration: undefined,
21869 // A function that determines whether the node should be animated
21870 // All nodes animated by default on animate enabled
21871 // Non-animated nodes are positioned immediately when the layout starts
21872 animateFilter: function animateFilter(node, i) {
21873 return true;
21874 },
21875 // The layout animates only after this many milliseconds for animate:true
21876 // (prevents flashing on fast runs)
21877 animationThreshold: 250,
21878 // Number of iterations between consecutive screen positions update
21879 refresh: 20,
21880 // Whether to fit the network view after when done
21881 fit: true,
21882 // Padding on fit
21883 padding: 30,
21884 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21885 boundingBox: undefined,
21886 // Excludes the label when calculating node bounding boxes for the layout algorithm
21887 nodeDimensionsIncludeLabels: false,
21888 // Randomize the initial positions of the nodes (true) or use existing positions (false)
21889 randomize: false,
21890 // Extra spacing between components in non-compound graphs
21891 componentSpacing: 40,
21892 // Node repulsion (non overlapping) multiplier
21893 nodeRepulsion: function nodeRepulsion(node) {
21894 return 2048;
21895 },
21896 // Node repulsion (overlapping) multiplier
21897 nodeOverlap: 4,
21898 // Ideal edge (non nested) length
21899 idealEdgeLength: function idealEdgeLength(edge) {
21900 return 32;
21901 },
21902 // Divisor to compute edge forces
21903 edgeElasticity: function edgeElasticity(edge) {
21904 return 32;
21905 },
21906 // Nesting factor (multiplier) to compute ideal edge length for nested edges
21907 nestingFactor: 1.2,
21908 // Gravity force (constant)
21909 gravity: 1,
21910 // Maximum number of iterations to perform
21911 numIter: 1000,
21912 // Initial temperature (maximum node displacement)
21913 initialTemp: 1000,
21914 // Cooling factor (how the temperature is reduced between consecutive iterations
21915 coolingFactor: 0.99,
21916 // Lower temperature threshold (below this point the layout will end)
21917 minTemp: 1.0
21918 };
21919 /**
21920 * @brief : constructor
21921 * @arg options : object containing layout options
21922 */
21923
21924 function CoseLayout(options) {
21925 this.options = extend({}, defaults$4, options);
21926 this.options.layout = this;
21927 }
21928 /**
21929 * @brief : runs the layout
21930 */
21931
21932
21933 CoseLayout.prototype.run = function () {
21934 var options = this.options;
21935 var cy = options.cy;
21936 var layout = this;
21937 layout.stopped = false;
21938
21939 if (options.animate === true || options.animate === false) {
21940 layout.emit({
21941 type: 'layoutstart',
21942 layout: layout
21943 });
21944 } // Set DEBUG - Global variable
21945
21946
21947 if (true === options.debug) {
21948 DEBUG = true;
21949 } else {
21950 DEBUG = false;
21951 } // Initialize layout info
21952
21953
21954 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
21955
21956 if (DEBUG) {
21957 printLayoutInfo(layoutInfo);
21958 } // If required, randomize node positions
21959
21960
21961 if (options.randomize) {
21962 randomizePositions(layoutInfo);
21963 }
21964
21965 var startTime = performanceNow();
21966
21967 var refresh = function refresh() {
21968 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
21969
21970 if (true === options.fit) {
21971 cy.fit(options.padding);
21972 }
21973 };
21974
21975 var mainLoop = function mainLoop(i) {
21976 if (layout.stopped || i >= options.numIter) {
21977 // logDebug("Layout manually stopped. Stopping computation in step " + i);
21978 return false;
21979 } // Do one step in the phisical simulation
21980
21981
21982 step(layoutInfo, options); // Update temperature
21983
21984 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
21985
21986 if (layoutInfo.temperature < options.minTemp) {
21987 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
21988 return false;
21989 }
21990
21991 return true;
21992 };
21993
21994 var done = function done() {
21995 if (options.animate === true || options.animate === false) {
21996 refresh(); // Layout has finished
21997
21998 layout.one('layoutstop', options.stop);
21999 layout.emit({
22000 type: 'layoutstop',
22001 layout: layout
22002 });
22003 } else {
22004 var nodes = options.eles.nodes();
22005 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
22006 nodes.layoutPositions(layout, options, getScaledPos);
22007 }
22008 };
22009
22010 var i = 0;
22011 var loopRet = true;
22012
22013 if (options.animate === true) {
22014 var frame = function frame() {
22015 var f = 0;
22016
22017 while (loopRet && f < options.refresh) {
22018 loopRet = mainLoop(i);
22019 i++;
22020 f++;
22021 }
22022
22023 if (!loopRet) {
22024 // it's done
22025 separateComponents(layoutInfo, options);
22026 done();
22027 } else {
22028 var now = performanceNow();
22029
22030 if (now - startTime >= options.animationThreshold) {
22031 refresh();
22032 }
22033
22034 requestAnimationFrame(frame);
22035 }
22036 };
22037
22038 frame();
22039 } else {
22040 while (loopRet) {
22041 loopRet = mainLoop(i);
22042 i++;
22043 }
22044
22045 separateComponents(layoutInfo, options);
22046 done();
22047 }
22048
22049 return this; // chaining
22050 };
22051 /**
22052 * @brief : called on continuous layouts to stop them before they finish
22053 */
22054
22055
22056 CoseLayout.prototype.stop = function () {
22057 this.stopped = true;
22058
22059 if (this.thread) {
22060 this.thread.stop();
22061 }
22062
22063 this.emit('layoutstop');
22064 return this; // chaining
22065 };
22066
22067 CoseLayout.prototype.destroy = function () {
22068 if (this.thread) {
22069 this.thread.stop();
22070 }
22071
22072 return this; // chaining
22073 };
22074 /**
22075 * @brief : Creates an object which is contains all the data
22076 * used in the layout process
22077 * @arg cy : cytoscape.js object
22078 * @return : layoutInfo object initialized
22079 */
22080
22081
22082 var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
22083 // Shortcut
22084 var edges = options.eles.edges();
22085 var nodes = options.eles.nodes();
22086 var layoutInfo = {
22087 isCompound: cy.hasCompoundNodes(),
22088 layoutNodes: [],
22089 idToIndex: {},
22090 nodeSize: nodes.size(),
22091 graphSet: [],
22092 indexToGraph: [],
22093 layoutEdges: [],
22094 edgeSize: edges.size(),
22095 temperature: options.initialTemp,
22096 clientWidth: cy.width(),
22097 clientHeight: cy.width(),
22098 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
22099 x1: 0,
22100 y1: 0,
22101 w: cy.width(),
22102 h: cy.height()
22103 })
22104 };
22105 var components = options.eles.components();
22106 var id2cmptId = {};
22107
22108 for (var i = 0; i < components.length; i++) {
22109 var component = components[i];
22110
22111 for (var j = 0; j < component.length; j++) {
22112 var node = component[j];
22113 id2cmptId[node.id()] = i;
22114 }
22115 } // Iterate over all nodes, creating layout nodes
22116
22117
22118 for (var i = 0; i < layoutInfo.nodeSize; i++) {
22119 var n = nodes[i];
22120 var nbb = n.layoutDimensions(options);
22121 var tempNode = {};
22122 tempNode.isLocked = n.locked();
22123 tempNode.id = n.data('id');
22124 tempNode.parentId = n.data('parent');
22125 tempNode.cmptId = id2cmptId[n.id()];
22126 tempNode.children = [];
22127 tempNode.positionX = n.position('x');
22128 tempNode.positionY = n.position('y');
22129 tempNode.offsetX = 0;
22130 tempNode.offsetY = 0;
22131 tempNode.height = nbb.w;
22132 tempNode.width = nbb.h;
22133 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
22134 tempNode.minX = tempNode.positionX - tempNode.width / 2;
22135 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
22136 tempNode.minY = tempNode.positionY - tempNode.height / 2;
22137 tempNode.padLeft = parseFloat(n.style('padding'));
22138 tempNode.padRight = parseFloat(n.style('padding'));
22139 tempNode.padTop = parseFloat(n.style('padding'));
22140 tempNode.padBottom = parseFloat(n.style('padding')); // forces
22141
22142 tempNode.nodeRepulsion = fn$6(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
22143
22144 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
22145
22146 layoutInfo.idToIndex[tempNode.id] = i;
22147 } // Inline implementation of a queue, used for traversing the graph in BFS order
22148
22149
22150 var queue = [];
22151 var start = 0; // Points to the start the queue
22152
22153 var end = -1; // Points to the end of the queue
22154
22155 var tempGraph = []; // Second pass to add child information and
22156 // initialize queue for hierarchical traversal
22157
22158 for (var i = 0; i < layoutInfo.nodeSize; i++) {
22159 var n = layoutInfo.layoutNodes[i];
22160 var p_id = n.parentId; // Check if node n has a parent node
22161
22162 if (null != p_id) {
22163 // Add node Id to parent's list of children
22164 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
22165 } else {
22166 // If a node doesn't have a parent, then it's in the root graph
22167 queue[++end] = n.id;
22168 tempGraph.push(n.id);
22169 }
22170 } // Add root graph to graphSet
22171
22172
22173 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
22174
22175 while (start <= end) {
22176 // Get the node to visit and remove it from queue
22177 var node_id = queue[start++];
22178 var node_ix = layoutInfo.idToIndex[node_id];
22179 var node = layoutInfo.layoutNodes[node_ix];
22180 var children = node.children;
22181
22182 if (children.length > 0) {
22183 // Add children nodes as a new graph to graph set
22184 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
22185
22186 for (var i = 0; i < children.length; i++) {
22187 queue[++end] = children[i];
22188 }
22189 }
22190 } // Create indexToGraph map
22191
22192
22193 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
22194 var graph = layoutInfo.graphSet[i];
22195
22196 for (var j = 0; j < graph.length; j++) {
22197 var index = layoutInfo.idToIndex[graph[j]];
22198 layoutInfo.indexToGraph[index] = i;
22199 }
22200 } // Iterate over all edges, creating Layout Edges
22201
22202
22203 for (var i = 0; i < layoutInfo.edgeSize; i++) {
22204 var e = edges[i];
22205 var tempEdge = {};
22206 tempEdge.id = e.data('id');
22207 tempEdge.sourceId = e.data('source');
22208 tempEdge.targetId = e.data('target'); // Compute ideal length
22209
22210 var idealLength = fn$6(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
22211 var elasticity = fn$6(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
22212
22213 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
22214 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
22215 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
22216 var targetGraph = layoutInfo.indexToGraph[targetIx];
22217
22218 if (sourceGraph != targetGraph) {
22219 // Find lowest common graph ancestor
22220 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
22221
22222 var lcaGraph = layoutInfo.graphSet[lca];
22223 var depth = 0; // Source depth
22224
22225 var tempNode = layoutInfo.layoutNodes[sourceIx];
22226
22227 while (-1 === lcaGraph.indexOf(tempNode.id)) {
22228 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
22229 depth++;
22230 } // Target depth
22231
22232
22233 tempNode = layoutInfo.layoutNodes[targetIx];
22234
22235 while (-1 === lcaGraph.indexOf(tempNode.id)) {
22236 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
22237 depth++;
22238 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
22239 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
22240 // ". Depth: " + depth);
22241 // Update idealLength
22242
22243
22244 idealLength *= depth * options.nestingFactor;
22245 }
22246
22247 tempEdge.idealLength = idealLength;
22248 tempEdge.elasticity = elasticity;
22249 layoutInfo.layoutEdges.push(tempEdge);
22250 } // Finally, return layoutInfo object
22251
22252
22253 return layoutInfo;
22254 };
22255 /**
22256 * @brief : This function finds the index of the lowest common
22257 * graph ancestor between 2 nodes in the subtree
22258 * (from the graph hierarchy induced tree) whose
22259 * root is graphIx
22260 *
22261 * @arg node1: node1's ID
22262 * @arg node2: node2's ID
22263 * @arg layoutInfo: layoutInfo object
22264 *
22265 */
22266
22267
22268 var findLCA = function findLCA(node1, node2, layoutInfo) {
22269 // Find their common ancester, starting from the root graph
22270 var res = findLCA_aux(node1, node2, 0, layoutInfo);
22271
22272 if (2 > res.count) {
22273 // If aux function couldn't find the common ancester,
22274 // then it is the root graph
22275 return 0;
22276 } else {
22277 return res.graph;
22278 }
22279 };
22280 /**
22281 * @brief : Auxiliary function used for LCA computation
22282 *
22283 * @arg node1 : node1's ID
22284 * @arg node2 : node2's ID
22285 * @arg graphIx : subgraph index
22286 * @arg layoutInfo : layoutInfo object
22287 *
22288 * @return : object of the form {count: X, graph: Y}, where:
22289 * X is the number of ancesters (max: 2) found in
22290 * graphIx (and it's subgraphs),
22291 * Y is the graph index of the lowest graph containing
22292 * all X nodes
22293 */
22294
22295
22296 var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
22297 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
22298
22299 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
22300 return {
22301 count: 2,
22302 graph: graphIx
22303 };
22304 } // Make recursive calls for all subgraphs
22305
22306
22307 var c = 0;
22308
22309 for (var i = 0; i < graph.length; i++) {
22310 var nodeId = graph[i];
22311 var nodeIx = layoutInfo.idToIndex[nodeId];
22312 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
22313
22314 if (0 === children.length) {
22315 continue;
22316 }
22317
22318 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
22319 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
22320
22321 if (0 === result.count) {
22322 // Neither node1 nor node2 are present in this subgraph
22323 continue;
22324 } else if (1 === result.count) {
22325 // One of (node1, node2) is present in this subgraph
22326 c++;
22327
22328 if (2 === c) {
22329 // We've already found both nodes, no need to keep searching
22330 break;
22331 }
22332 } else {
22333 // Both nodes are present in this subgraph
22334 return result;
22335 }
22336 }
22337
22338 return {
22339 count: c,
22340 graph: graphIx
22341 };
22342 };
22343 /**
22344 * @brief: printsLayoutInfo into js console
22345 * Only used for debbuging
22346 */
22347
22348
22349var printLayoutInfo;
22350 /**
22351 * @brief : Randomizes the position of all nodes
22352 */
22353
22354
22355 var randomizePositions = function randomizePositions(layoutInfo, cy) {
22356 var width = layoutInfo.clientWidth;
22357 var height = layoutInfo.clientHeight;
22358
22359 for (var i = 0; i < layoutInfo.nodeSize; i++) {
22360 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
22361
22362 if (0 === n.children.length && !n.isLocked) {
22363 n.positionX = Math.random() * width;
22364 n.positionY = Math.random() * height;
22365 }
22366 }
22367 };
22368
22369 var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
22370 var bb = layoutInfo.boundingBox;
22371 var coseBB = {
22372 x1: Infinity,
22373 x2: -Infinity,
22374 y1: Infinity,
22375 y2: -Infinity
22376 };
22377
22378 if (options.boundingBox) {
22379 nodes.forEach(function (node) {
22380 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
22381 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
22382 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
22383 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
22384 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
22385 });
22386 coseBB.w = coseBB.x2 - coseBB.x1;
22387 coseBB.h = coseBB.y2 - coseBB.y1;
22388 }
22389
22390 return function (ele, i) {
22391 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
22392
22393 if (options.boundingBox) {
22394 // then add extra bounding box constraint
22395 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
22396 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
22397 return {
22398 x: bb.x1 + pctX * bb.w,
22399 y: bb.y1 + pctY * bb.h
22400 };
22401 } else {
22402 return {
22403 x: lnode.positionX,
22404 y: lnode.positionY
22405 };
22406 }
22407 };
22408 };
22409 /**
22410 * @brief : Updates the positions of nodes in the network
22411 * @arg layoutInfo : LayoutInfo object
22412 * @arg cy : Cytoscape object
22413 * @arg options : Layout options
22414 */
22415
22416
22417 var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
22418 // var s = 'Refreshing positions';
22419 // logDebug(s);
22420 var layout = options.layout;
22421 var nodes = options.eles.nodes();
22422 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
22423 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
22424
22425 if (true !== layoutInfo.ready) {
22426 // s = 'Triggering layoutready';
22427 // logDebug(s);
22428 layoutInfo.ready = true;
22429 layout.one('layoutready', options.ready);
22430 layout.emit({
22431 type: 'layoutready',
22432 layout: this
22433 });
22434 }
22435 };
22436 /**
22437 * @brief : Logs a debug message in JS console, if DEBUG is ON
22438 */
22439 // var logDebug = function(text) {
22440 // if (DEBUG) {
22441 // console.debug(text);
22442 // }
22443 // };
22444
22445 /**
22446 * @brief : Performs one iteration of the physical simulation
22447 * @arg layoutInfo : LayoutInfo object already initialized
22448 * @arg cy : Cytoscape object
22449 * @arg options : Layout options
22450 */
22451
22452
22453 var step = function step(layoutInfo, options, _step) {
22454 // var s = "\n\n###############################";
22455 // s += "\nSTEP: " + step;
22456 // s += "\n###############################\n";
22457 // logDebug(s);
22458 // Calculate node repulsions
22459 calculateNodeForces(layoutInfo, options); // Calculate edge forces
22460
22461 calculateEdgeForces(layoutInfo); // Calculate gravity forces
22462
22463 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
22464
22465 propagateForces(layoutInfo); // Update positions based on calculated forces
22466
22467 updatePositions(layoutInfo);
22468 };
22469 /**
22470 * @brief : Computes the node repulsion forces
22471 */
22472
22473
22474 var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
22475 // Go through each of the graphs in graphSet
22476 // Nodes only repel each other if they belong to the same graph
22477 // var s = 'calculateNodeForces';
22478 // logDebug(s);
22479 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
22480 var graph = layoutInfo.graphSet[i];
22481 var numNodes = graph.length; // s = "Set: " + graph.toString();
22482 // logDebug(s);
22483 // Now get all the pairs of nodes
22484 // Only get each pair once, (A, B) = (B, A)
22485
22486 for (var j = 0; j < numNodes; j++) {
22487 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
22488
22489 for (var k = j + 1; k < numNodes; k++) {
22490 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
22491 nodeRepulsion(node1, node2, layoutInfo, options);
22492 }
22493 }
22494 }
22495 };
22496
22497 var randomDistance = function randomDistance(max) {
22498 return -max + 2 * max * Math.random();
22499 };
22500 /**
22501 * @brief : Compute the node repulsion forces between a pair of nodes
22502 */
22503
22504
22505 var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
22506 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
22507 var cmptId1 = node1.cmptId;
22508 var cmptId2 = node2.cmptId;
22509
22510 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
22511 return;
22512 } // Get direction of line connecting both node centers
22513
22514
22515 var directionX = node2.positionX - node1.positionX;
22516 var directionY = node2.positionY - node1.positionY;
22517 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
22518 // If both centers are the same, apply a random force
22519
22520 if (0 === directionX && 0 === directionY) {
22521 directionX = randomDistance(maxRandDist);
22522 directionY = randomDistance(maxRandDist);
22523 }
22524
22525 var overlap = nodesOverlap(node1, node2, directionX, directionY);
22526
22527 if (overlap > 0) {
22528 // s += "\nNodes DO overlap.";
22529 // s += "\nOverlap: " + overlap;
22530 // If nodes overlap, repulsion force is proportional
22531 // to the overlap
22532 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
22533
22534 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
22535
22536 var forceX = force * directionX / distance;
22537 var forceY = force * directionY / distance;
22538 } else {
22539 // s += "\nNodes do NOT overlap.";
22540 // If there's no overlap, force is inversely proportional
22541 // to squared distance
22542 // Get clipping points for both nodes
22543 var point1 = findClippingPoint(node1, directionX, directionY);
22544 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
22545
22546 var distanceX = point2.x - point1.x;
22547 var distanceY = point2.y - point1.y;
22548 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
22549 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
22550 // Compute the module and components of the force vector
22551
22552 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
22553 var forceX = force * distanceX / distance;
22554 var forceY = force * distanceY / distance;
22555 } // Apply force
22556
22557
22558 if (!node1.isLocked) {
22559 node1.offsetX -= forceX;
22560 node1.offsetY -= forceY;
22561 }
22562
22563 if (!node2.isLocked) {
22564 node2.offsetX += forceX;
22565 node2.offsetY += forceY;
22566 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
22567 // logDebug(s);
22568
22569
22570 return;
22571 };
22572 /**
22573 * @brief : Determines whether two nodes overlap or not
22574 * @return : Amount of overlapping (0 => no overlap)
22575 */
22576
22577
22578 var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
22579 if (dX > 0) {
22580 var overlapX = node1.maxX - node2.minX;
22581 } else {
22582 var overlapX = node2.maxX - node1.minX;
22583 }
22584
22585 if (dY > 0) {
22586 var overlapY = node1.maxY - node2.minY;
22587 } else {
22588 var overlapY = node2.maxY - node1.minY;
22589 }
22590
22591 if (overlapX >= 0 && overlapY >= 0) {
22592 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
22593 } else {
22594 return 0;
22595 }
22596 };
22597 /**
22598 * @brief : Finds the point in which an edge (direction dX, dY) intersects
22599 * the rectangular bounding box of it's source/target node
22600 */
22601
22602
22603 var findClippingPoint = function findClippingPoint(node, dX, dY) {
22604 // Shorcuts
22605 var X = node.positionX;
22606 var Y = node.positionY;
22607 var H = node.height || 1;
22608 var W = node.width || 1;
22609 var dirSlope = dY / dX;
22610 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
22611 // " . Height: " + H + ", Width: " + W +
22612 // "\nDirection " + dX + ", " + dY;
22613 //
22614 // Compute intersection
22615
22616 var res = {}; // Case: Vertical direction (up)
22617
22618 if (0 === dX && 0 < dY) {
22619 res.x = X; // s += "\nUp direction";
22620
22621 res.y = Y + H / 2;
22622 return res;
22623 } // Case: Vertical direction (down)
22624
22625
22626 if (0 === dX && 0 > dY) {
22627 res.x = X;
22628 res.y = Y + H / 2; // s += "\nDown direction";
22629
22630 return res;
22631 } // Case: Intersects the right border
22632
22633
22634 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
22635 res.x = X + W / 2;
22636 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
22637
22638 return res;
22639 } // Case: Intersects the left border
22640
22641
22642 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
22643 res.x = X - W / 2;
22644 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
22645
22646 return res;
22647 } // Case: Intersects the top border
22648
22649
22650 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
22651 res.x = X + H * dX / 2 / dY;
22652 res.y = Y + H / 2; // s += "\nTop border";
22653
22654 return res;
22655 } // Case: Intersects the bottom border
22656
22657
22658 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
22659 res.x = X - H * dX / 2 / dY;
22660 res.y = Y - H / 2; // s += "\nBottom border";
22661
22662 return res;
22663 } // s += "\nClipping point found at " + res.x + ", " + res.y;
22664 // logDebug(s);
22665
22666
22667 return res;
22668 };
22669 /**
22670 * @brief : Calculates all edge forces
22671 */
22672
22673
22674 var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
22675 // Iterate over all edges
22676 for (var i = 0; i < layoutInfo.edgeSize; i++) {
22677 // Get edge, source & target nodes
22678 var edge = layoutInfo.layoutEdges[i];
22679 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
22680 var source = layoutInfo.layoutNodes[sourceIx];
22681 var targetIx = layoutInfo.idToIndex[edge.targetId];
22682 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
22683
22684 var directionX = target.positionX - source.positionX;
22685 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
22686 // A random force has already been applied as node repulsion
22687
22688 if (0 === directionX && 0 === directionY) {
22689 continue;
22690 } // Get clipping points for both nodes
22691
22692
22693 var point1 = findClippingPoint(source, directionX, directionY);
22694 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
22695 var lx = point2.x - point1.x;
22696 var ly = point2.y - point1.y;
22697 var l = Math.sqrt(lx * lx + ly * ly);
22698 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
22699
22700 if (0 !== l) {
22701 var forceX = force * lx / l;
22702 var forceY = force * ly / l;
22703 } else {
22704 var forceX = 0;
22705 var forceY = 0;
22706 } // Add this force to target and source nodes
22707
22708
22709 if (!source.isLocked) {
22710 source.offsetX += forceX;
22711 source.offsetY += forceY;
22712 }
22713
22714 if (!target.isLocked) {
22715 target.offsetX -= forceX;
22716 target.offsetY -= forceY;
22717 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
22718 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
22719 // logDebug(s);
22720
22721 }
22722 };
22723 /**
22724 * @brief : Computes gravity forces for all nodes
22725 */
22726
22727
22728 var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
22729 if (options.gravity === 0) {
22730 return;
22731 }
22732
22733 var distThreshold = 1; // var s = 'calculateGravityForces';
22734 // logDebug(s);
22735
22736 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
22737 var graph = layoutInfo.graphSet[i];
22738 var numNodes = graph.length; // s = "Set: " + graph.toString();
22739 // logDebug(s);
22740 // Compute graph center
22741
22742 if (0 === i) {
22743 var centerX = layoutInfo.clientHeight / 2;
22744 var centerY = layoutInfo.clientWidth / 2;
22745 } else {
22746 // Get Parent node for this graph, and use its position as center
22747 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
22748 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
22749 var centerX = parent.positionX;
22750 var centerY = parent.positionY;
22751 } // s = "Center found at: " + centerX + ", " + centerY;
22752 // logDebug(s);
22753 // Apply force to all nodes in graph
22754
22755
22756 for (var j = 0; j < numNodes; j++) {
22757 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
22758
22759 if (node.isLocked) {
22760 continue;
22761 }
22762
22763 var dx = centerX - node.positionX;
22764 var dy = centerY - node.positionY;
22765 var d = Math.sqrt(dx * dx + dy * dy);
22766
22767 if (d > distThreshold) {
22768 var fx = options.gravity * dx / d;
22769 var fy = options.gravity * dy / d;
22770 node.offsetX += fx;
22771 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
22772 } // logDebug(s);
22773
22774 }
22775 }
22776 };
22777 /**
22778 * @brief : This function propagates the existing offsets from
22779 * parent nodes to its descendents.
22780 * @arg layoutInfo : layoutInfo Object
22781 * @arg cy : cytoscape Object
22782 * @arg options : Layout options
22783 */
22784
22785
22786 var propagateForces = function propagateForces(layoutInfo, options) {
22787 // Inline implementation of a queue, used for traversing the graph in BFS order
22788 var queue = [];
22789 var start = 0; // Points to the start the queue
22790
22791 var end = -1; // Points to the end of the queue
22792 // logDebug('propagateForces');
22793 // Start by visiting the nodes in the root graph
22794
22795 queue.push.apply(queue, layoutInfo.graphSet[0]);
22796 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
22797
22798 while (start <= end) {
22799 // Get the node to visit and remove it from queue
22800 var nodeId = queue[start++];
22801 var nodeIndex = layoutInfo.idToIndex[nodeId];
22802 var node = layoutInfo.layoutNodes[nodeIndex];
22803 var children = node.children; // We only need to process the node if it's compound
22804
22805 if (0 < children.length && !node.isLocked) {
22806 var offX = node.offsetX;
22807 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
22808 // ". OffsetX: " + offX + ". OffsetY: " + offY;
22809 // s += "\n Children: " + children.toString();
22810 // logDebug(s);
22811
22812 for (var i = 0; i < children.length; i++) {
22813 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
22814
22815 childNode.offsetX += offX;
22816 childNode.offsetY += offY; // Add children to queue to be visited
22817
22818 queue[++end] = children[i];
22819 } // Reset parent offsets
22820
22821
22822 node.offsetX = 0;
22823 node.offsetY = 0;
22824 }
22825 }
22826 };
22827 /**
22828 * @brief : Updates the layout model positions, based on
22829 * the accumulated forces
22830 */
22831
22832
22833 var updatePositions = function updatePositions(layoutInfo, options) {
22834 // var s = 'Updating positions';
22835 // logDebug(s);
22836 // Reset boundaries for compound nodes
22837 for (var i = 0; i < layoutInfo.nodeSize; i++) {
22838 var n = layoutInfo.layoutNodes[i];
22839
22840 if (0 < n.children.length) {
22841 // logDebug("Resetting boundaries of compound node: " + n.id);
22842 n.maxX = undefined;
22843 n.minX = undefined;
22844 n.maxY = undefined;
22845 n.minY = undefined;
22846 }
22847 }
22848
22849 for (var i = 0; i < layoutInfo.nodeSize; i++) {
22850 var n = layoutInfo.layoutNodes[i];
22851
22852 if (0 < n.children.length || n.isLocked) {
22853 // No need to set compound or locked node position
22854 // logDebug("Skipping position update of node: " + n.id);
22855 continue;
22856 } // s = "Node: " + n.id + " Previous position: (" +
22857 // n.positionX + ", " + n.positionY + ").";
22858 // Limit displacement in order to improve stability
22859
22860
22861 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
22862 n.positionX += tempForce.x;
22863 n.positionY += tempForce.y;
22864 n.offsetX = 0;
22865 n.offsetY = 0;
22866 n.minX = n.positionX - n.width;
22867 n.maxX = n.positionX + n.width;
22868 n.minY = n.positionY - n.height;
22869 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
22870 // logDebug(s);
22871 // Update ancestry boudaries
22872
22873 updateAncestryBoundaries(n, layoutInfo);
22874 } // Update size, position of compund nodes
22875
22876
22877 for (var i = 0; i < layoutInfo.nodeSize; i++) {
22878 var n = layoutInfo.layoutNodes[i];
22879
22880 if (0 < n.children.length && !n.isLocked) {
22881 n.positionX = (n.maxX + n.minX) / 2;
22882 n.positionY = (n.maxY + n.minY) / 2;
22883 n.width = n.maxX - n.minX;
22884 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
22885 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
22886 // s += "\nWidth: " + n.width + ", Height: " + n.height;
22887 // logDebug(s);
22888 }
22889 }
22890 };
22891 /**
22892 * @brief : Limits a force (forceX, forceY) to be not
22893 * greater (in modulo) than max.
22894 8 Preserves force direction.
22895 */
22896
22897
22898 var limitForce = function limitForce(forceX, forceY, max) {
22899 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
22900 var force = Math.sqrt(forceX * forceX + forceY * forceY);
22901
22902 if (force > max) {
22903 var res = {
22904 x: max * forceX / force,
22905 y: max * forceY / force
22906 };
22907 } else {
22908 var res = {
22909 x: forceX,
22910 y: forceY
22911 };
22912 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
22913 // logDebug(s);
22914
22915
22916 return res;
22917 };
22918 /**
22919 * @brief : Function used for keeping track of compound node
22920 * sizes, since they should bound all their subnodes.
22921 */
22922
22923
22924 var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
22925 // var s = "Propagating new position/size of node " + node.id;
22926 var parentId = node.parentId;
22927
22928 if (null == parentId) {
22929 // If there's no parent, we are done
22930 // s += ". No parent node.";
22931 // logDebug(s);
22932 return;
22933 } // Get Parent Node
22934
22935
22936 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
22937 var flag = false; // MaxX
22938
22939 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
22940 p.maxX = node.maxX + p.padRight;
22941 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
22942 } // MinX
22943
22944
22945 if (null == p.minX || node.minX - p.padLeft < p.minX) {
22946 p.minX = node.minX - p.padLeft;
22947 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
22948 } // MaxY
22949
22950
22951 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
22952 p.maxY = node.maxY + p.padBottom;
22953 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
22954 } // MinY
22955
22956
22957 if (null == p.minY || node.minY - p.padTop < p.minY) {
22958 p.minY = node.minY - p.padTop;
22959 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
22960 } // If updated boundaries, propagate changes upward
22961
22962
22963 if (flag) {
22964 // logDebug(s);
22965 return updateAncestryBoundaries(p, layoutInfo);
22966 } // s += ". No changes in boundaries/position of parent node " + p.id;
22967 // logDebug(s);
22968
22969
22970 return;
22971 };
22972
22973 var separateComponents = function separateComponents(layoutInfo, options) {
22974 var nodes = layoutInfo.layoutNodes;
22975 var components = [];
22976
22977 for (var i = 0; i < nodes.length; i++) {
22978 var node = nodes[i];
22979 var cid = node.cmptId;
22980 var component = components[cid] = components[cid] || [];
22981 component.push(node);
22982 }
22983
22984 var totalA = 0;
22985
22986 for (var i = 0; i < components.length; i++) {
22987 var c = components[i];
22988
22989 if (!c) {
22990 continue;
22991 }
22992
22993 c.x1 = Infinity;
22994 c.x2 = -Infinity;
22995 c.y1 = Infinity;
22996 c.y2 = -Infinity;
22997
22998 for (var j = 0; j < c.length; j++) {
22999 var n = c[j];
23000 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
23001 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
23002 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
23003 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
23004 }
23005
23006 c.w = c.x2 - c.x1;
23007 c.h = c.y2 - c.y1;
23008 totalA += c.w * c.h;
23009 }
23010
23011 components.sort(function (c1, c2) {
23012 return c2.w * c2.h - c1.w * c1.h;
23013 });
23014 var x = 0;
23015 var y = 0;
23016 var usedW = 0;
23017 var rowH = 0;
23018 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
23019
23020 for (var i = 0; i < components.length; i++) {
23021 var c = components[i];
23022
23023 if (!c) {
23024 continue;
23025 }
23026
23027 for (var j = 0; j < c.length; j++) {
23028 var n = c[j];
23029
23030 if (!n.isLocked) {
23031 n.positionX += x - c.x1;
23032 n.positionY += y - c.y1;
23033 }
23034 }
23035
23036 x += c.w + options.componentSpacing;
23037 usedW += c.w + options.componentSpacing;
23038 rowH = Math.max(rowH, c.h);
23039
23040 if (usedW > maxRowW) {
23041 y += rowH + options.componentSpacing;
23042 x = 0;
23043 usedW = 0;
23044 rowH = 0;
23045 }
23046 }
23047 };
23048
23049 var defaults$3 = {
23050 fit: true,
23051 // whether to fit the viewport to the graph
23052 padding: 30,
23053 // padding used on fit
23054 boundingBox: undefined,
23055 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
23056 avoidOverlap: true,
23057 // prevents node overlap, may overflow boundingBox if not enough space
23058 avoidOverlapPadding: 10,
23059 // extra spacing around nodes when avoidOverlap: true
23060 nodeDimensionsIncludeLabels: false,
23061 // Excludes the label when calculating node bounding boxes for the layout algorithm
23062 spacingFactor: undefined,
23063 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
23064 condense: false,
23065 // uses all available space on false, uses minimal space on true
23066 rows: undefined,
23067 // force num of rows in the grid
23068 cols: undefined,
23069 // force num of columns in the grid
23070 position: function position(node) {},
23071 // returns { row, col } for element
23072 sort: undefined,
23073 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
23074 animate: false,
23075 // whether to transition the node positions
23076 animationDuration: 500,
23077 // duration of animation in ms if enabled
23078 animationEasing: undefined,
23079 // easing of animation if enabled
23080 animateFilter: function animateFilter(node, i) {
23081 return true;
23082 },
23083 // 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
23084 ready: undefined,
23085 // callback on layoutready
23086 stop: undefined,
23087 // callback on layoutstop
23088 transform: function transform(node, position) {
23089 return position;
23090 } // transform a given node position. Useful for changing flow direction in discrete layouts
23091
23092 };
23093
23094 function GridLayout(options) {
23095 this.options = extend({}, defaults$3, options);
23096 }
23097
23098 GridLayout.prototype.run = function () {
23099 var params = this.options;
23100 var options = params;
23101 var cy = params.cy;
23102 var eles = options.eles;
23103 var nodes = eles.nodes().not(':parent');
23104
23105 if (options.sort) {
23106 nodes = nodes.sort(options.sort);
23107 }
23108
23109 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
23110 x1: 0,
23111 y1: 0,
23112 w: cy.width(),
23113 h: cy.height()
23114 });
23115
23116 if (bb.h === 0 || bb.w === 0) {
23117 eles.nodes().layoutPositions(this, options, function (ele) {
23118 return {
23119 x: bb.x1,
23120 y: bb.y1
23121 };
23122 });
23123 } else {
23124 // width/height * splits^2 = cells where splits is number of times to split width
23125 var cells = nodes.size();
23126 var splits = Math.sqrt(cells * bb.h / bb.w);
23127 var rows = Math.round(splits);
23128 var cols = Math.round(bb.w / bb.h * splits);
23129
23130 var small = function small(val) {
23131 if (val == null) {
23132 return Math.min(rows, cols);
23133 } else {
23134 var min = Math.min(rows, cols);
23135
23136 if (min == rows) {
23137 rows = val;
23138 } else {
23139 cols = val;
23140 }
23141 }
23142 };
23143
23144 var large = function large(val) {
23145 if (val == null) {
23146 return Math.max(rows, cols);
23147 } else {
23148 var max = Math.max(rows, cols);
23149
23150 if (max == rows) {
23151 rows = val;
23152 } else {
23153 cols = val;
23154 }
23155 }
23156 };
23157
23158 var oRows = options.rows;
23159 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
23160
23161 if (oRows != null && oCols != null) {
23162 rows = oRows;
23163 cols = oCols;
23164 } else if (oRows != null && oCols == null) {
23165 rows = oRows;
23166 cols = Math.ceil(cells / rows);
23167 } else if (oRows == null && oCols != null) {
23168 cols = oCols;
23169 rows = Math.ceil(cells / cols);
23170 } // otherwise use the automatic values and adjust accordingly
23171 // if rounding was up, see if we can reduce rows or columns
23172 else if (cols * rows > cells) {
23173 var sm = small();
23174 var lg = large(); // reducing the small side takes away the most cells, so try it first
23175
23176 if ((sm - 1) * lg >= cells) {
23177 small(sm - 1);
23178 } else if ((lg - 1) * sm >= cells) {
23179 large(lg - 1);
23180 }
23181 } else {
23182 // if rounding was too low, add rows or columns
23183 while (cols * rows < cells) {
23184 var _sm = small();
23185
23186 var _lg = large(); // try to add to larger side first (adds less in multiplication)
23187
23188
23189 if ((_lg + 1) * _sm >= cells) {
23190 large(_lg + 1);
23191 } else {
23192 small(_sm + 1);
23193 }
23194 }
23195 }
23196
23197 var cellWidth = bb.w / cols;
23198 var cellHeight = bb.h / rows;
23199
23200 if (options.condense) {
23201 cellWidth = 0;
23202 cellHeight = 0;
23203 }
23204
23205 if (options.avoidOverlap) {
23206 for (var i = 0; i < nodes.length; i++) {
23207 var node = nodes[i];
23208 var pos = node._private.position;
23209
23210 if (pos.x == null || pos.y == null) {
23211 // for bb
23212 pos.x = 0;
23213 pos.y = 0;
23214 }
23215
23216 var nbb = node.layoutDimensions(options);
23217 var p = options.avoidOverlapPadding;
23218 var w = nbb.w + p;
23219 var h = nbb.h + p;
23220 cellWidth = Math.max(cellWidth, w);
23221 cellHeight = Math.max(cellHeight, h);
23222 }
23223 }
23224
23225 var cellUsed = {}; // e.g. 'c-0-2' => true
23226
23227 var used = function used(row, col) {
23228 return cellUsed['c-' + row + '-' + col] ? true : false;
23229 };
23230
23231 var use = function use(row, col) {
23232 cellUsed['c-' + row + '-' + col] = true;
23233 }; // to keep track of current cell position
23234
23235
23236 var row = 0;
23237 var col = 0;
23238
23239 var moveToNextCell = function moveToNextCell() {
23240 col++;
23241
23242 if (col >= cols) {
23243 col = 0;
23244 row++;
23245 }
23246 }; // get a cache of all the manual positions
23247
23248
23249 var id2manPos = {};
23250
23251 for (var _i = 0; _i < nodes.length; _i++) {
23252 var _node = nodes[_i];
23253 var rcPos = options.position(_node);
23254
23255 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
23256 // must have at least row or col def'd
23257 var _pos = {
23258 row: rcPos.row,
23259 col: rcPos.col
23260 };
23261
23262 if (_pos.col === undefined) {
23263 // find unused col
23264 _pos.col = 0;
23265
23266 while (used(_pos.row, _pos.col)) {
23267 _pos.col++;
23268 }
23269 } else if (_pos.row === undefined) {
23270 // find unused row
23271 _pos.row = 0;
23272
23273 while (used(_pos.row, _pos.col)) {
23274 _pos.row++;
23275 }
23276 }
23277
23278 id2manPos[_node.id()] = _pos;
23279 use(_pos.row, _pos.col);
23280 }
23281 }
23282
23283 var getPos = function getPos(element, i) {
23284 var x, y;
23285
23286 if (element.locked() || element.isParent()) {
23287 return false;
23288 } // see if we have a manual position set
23289
23290
23291 var rcPos = id2manPos[element.id()];
23292
23293 if (rcPos) {
23294 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
23295 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
23296 } else {
23297 // otherwise set automatically
23298 while (used(row, col)) {
23299 moveToNextCell();
23300 }
23301
23302 x = col * cellWidth + cellWidth / 2 + bb.x1;
23303 y = row * cellHeight + cellHeight / 2 + bb.y1;
23304 use(row, col);
23305 moveToNextCell();
23306 }
23307
23308 return {
23309 x: x,
23310 y: y
23311 };
23312 };
23313
23314 nodes.layoutPositions(this, options, getPos);
23315 }
23316
23317 return this; // chaining
23318 };
23319
23320 var defaults$2 = {
23321 ready: function ready() {},
23322 // on layoutready
23323 stop: function stop() {} // on layoutstop
23324
23325 }; // constructor
23326 // options : object containing layout options
23327
23328 function NullLayout(options) {
23329 this.options = extend({}, defaults$2, options);
23330 } // runs the layout
23331
23332
23333 NullLayout.prototype.run = function () {
23334 var options = this.options;
23335 var eles = options.eles; // elements to consider in the layout
23336
23337 var layout = this; // cy is automatically populated for us in the constructor
23338 // (disable eslint for next line as this serves as example layout code to external developers)
23339 // eslint-disable-next-line no-unused-vars
23340
23341 options.cy;
23342 layout.emit('layoutstart'); // puts all nodes at (0, 0)
23343 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
23344
23345 eles.nodes().positions(function () {
23346 return {
23347 x: 0,
23348 y: 0
23349 };
23350 }); // trigger layoutready when each node has had its position set at least once
23351
23352 layout.one('layoutready', options.ready);
23353 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
23354
23355 layout.one('layoutstop', options.stop);
23356 layout.emit('layoutstop');
23357 return this; // chaining
23358 }; // called on continuous layouts to stop them before they finish
23359
23360
23361 NullLayout.prototype.stop = function () {
23362 return this; // chaining
23363 };
23364
23365 var defaults$1 = {
23366 positions: undefined,
23367 // map of (node id) => (position obj); or function(node){ return somPos; }
23368 zoom: undefined,
23369 // the zoom level to set (prob want fit = false if set)
23370 pan: undefined,
23371 // the pan level to set (prob want fit = false if set)
23372 fit: true,
23373 // whether to fit to viewport
23374 padding: 30,
23375 // padding on fit
23376 animate: false,
23377 // whether to transition the node positions
23378 animationDuration: 500,
23379 // duration of animation in ms if enabled
23380 animationEasing: undefined,
23381 // easing of animation if enabled
23382 animateFilter: function animateFilter(node, i) {
23383 return true;
23384 },
23385 // 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
23386 ready: undefined,
23387 // callback on layoutready
23388 stop: undefined,
23389 // callback on layoutstop
23390 transform: function transform(node, position) {
23391 return position;
23392 } // transform a given node position. Useful for changing flow direction in discrete layouts
23393
23394 };
23395
23396 function PresetLayout(options) {
23397 this.options = extend({}, defaults$1, options);
23398 }
23399
23400 PresetLayout.prototype.run = function () {
23401 var options = this.options;
23402 var eles = options.eles;
23403 var nodes = eles.nodes();
23404 var posIsFn = fn$6(options.positions);
23405
23406 function getPosition(node) {
23407 if (options.positions == null) {
23408 return copyPosition(node.position());
23409 }
23410
23411 if (posIsFn) {
23412 return options.positions(node);
23413 }
23414
23415 var pos = options.positions[node._private.data.id];
23416
23417 if (pos == null) {
23418 return null;
23419 }
23420
23421 return pos;
23422 }
23423
23424 nodes.layoutPositions(this, options, function (node, i) {
23425 var position = getPosition(node);
23426
23427 if (node.locked() || position == null) {
23428 return false;
23429 }
23430
23431 return position;
23432 });
23433 return this; // chaining
23434 };
23435
23436 var defaults = {
23437 fit: true,
23438 // whether to fit to viewport
23439 padding: 30,
23440 // fit padding
23441 boundingBox: undefined,
23442 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
23443 animate: false,
23444 // whether to transition the node positions
23445 animationDuration: 500,
23446 // duration of animation in ms if enabled
23447 animationEasing: undefined,
23448 // easing of animation if enabled
23449 animateFilter: function animateFilter(node, i) {
23450 return true;
23451 },
23452 // 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
23453 ready: undefined,
23454 // callback on layoutready
23455 stop: undefined,
23456 // callback on layoutstop
23457 transform: function transform(node, position) {
23458 return position;
23459 } // transform a given node position. Useful for changing flow direction in discrete layouts
23460
23461 };
23462
23463 function RandomLayout(options) {
23464 this.options = extend({}, defaults, options);
23465 }
23466
23467 RandomLayout.prototype.run = function () {
23468 var options = this.options;
23469 var cy = options.cy;
23470 var eles = options.eles;
23471 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
23472 x1: 0,
23473 y1: 0,
23474 w: cy.width(),
23475 h: cy.height()
23476 });
23477
23478 var getPos = function getPos(node, i) {
23479 return {
23480 x: bb.x1 + Math.round(Math.random() * bb.w),
23481 y: bb.y1 + Math.round(Math.random() * bb.h)
23482 };
23483 };
23484
23485 eles.nodes().layoutPositions(this, options, getPos);
23486 return this; // chaining
23487 };
23488
23489 var layout = [{
23490 name: 'breadthfirst',
23491 impl: BreadthFirstLayout
23492 }, {
23493 name: 'circle',
23494 impl: CircleLayout
23495 }, {
23496 name: 'concentric',
23497 impl: ConcentricLayout
23498 }, {
23499 name: 'cose',
23500 impl: CoseLayout
23501 }, {
23502 name: 'grid',
23503 impl: GridLayout
23504 }, {
23505 name: 'null',
23506 impl: NullLayout
23507 }, {
23508 name: 'preset',
23509 impl: PresetLayout
23510 }, {
23511 name: 'random',
23512 impl: RandomLayout
23513 }];
23514
23515 function NullRenderer(options) {
23516 this.options = options;
23517 this.notifications = 0; // for testing
23518 }
23519
23520 var noop = function noop() {};
23521
23522 var throwImgErr = function throwImgErr() {
23523 throw new Error('A headless instance can not render images');
23524 };
23525
23526 NullRenderer.prototype = {
23527 recalculateRenderedStyle: noop,
23528 notify: function notify() {
23529 this.notifications++;
23530 },
23531 init: noop,
23532 isHeadless: function isHeadless() {
23533 return true;
23534 },
23535 png: throwImgErr,
23536 jpg: throwImgErr
23537 };
23538
23539 var BRp$f = {};
23540 BRp$f.arrowShapeWidth = 0.3;
23541
23542 BRp$f.registerArrowShapes = function () {
23543 var arrowShapes = this.arrowShapes = {};
23544 var renderer = this; // Contract for arrow shapes:
23545 // 0, 0 is arrow tip
23546 // (0, 1) is direction towards node
23547 // (1, 0) is right
23548 //
23549 // functional api:
23550 // collide: check x, y in shape
23551 // roughCollide: called before collide, no false negatives
23552 // draw: draw
23553 // spacing: dist(arrowTip, nodeBoundary)
23554 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
23555
23556 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
23557 var x1 = translation.x - size / 2 - padding;
23558 var x2 = translation.x + size / 2 + padding;
23559 var y1 = translation.y - size / 2 - padding;
23560 var y2 = translation.y + size / 2 + padding;
23561 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
23562 return inside;
23563 };
23564
23565 var transform = function transform(x, y, size, angle, translation) {
23566 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
23567 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
23568 var xScaled = xRotated * size;
23569 var yScaled = yRotated * size;
23570 var xTranslated = xScaled + translation.x;
23571 var yTranslated = yScaled + translation.y;
23572 return {
23573 x: xTranslated,
23574 y: yTranslated
23575 };
23576 };
23577
23578 var transformPoints = function transformPoints(pts, size, angle, translation) {
23579 var retPts = [];
23580
23581 for (var i = 0; i < pts.length; i += 2) {
23582 var x = pts[i];
23583 var y = pts[i + 1];
23584 retPts.push(transform(x, y, size, angle, translation));
23585 }
23586
23587 return retPts;
23588 };
23589
23590 var pointsToArr = function pointsToArr(pts) {
23591 var ret = [];
23592
23593 for (var i = 0; i < pts.length; i++) {
23594 var p = pts[i];
23595 ret.push(p.x, p.y);
23596 }
23597
23598 return ret;
23599 };
23600
23601 var standardGap = function standardGap(edge) {
23602 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
23603 };
23604
23605 var defineArrowShape = function defineArrowShape(name, defn) {
23606 if (string(defn)) {
23607 defn = arrowShapes[defn];
23608 }
23609
23610 arrowShapes[name] = extend({
23611 name: name,
23612 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
23613 collide: function collide(x, y, size, angle, translation, padding) {
23614 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
23615 var inside = pointInsidePolygonPoints(x, y, points);
23616 return inside;
23617 },
23618 roughCollide: bbCollide,
23619 draw: function draw(context, size, angle, translation) {
23620 var points = transformPoints(this.points, size, angle, translation);
23621 renderer.arrowShapeImpl('polygon')(context, points);
23622 },
23623 spacing: function spacing(edge) {
23624 return 0;
23625 },
23626 gap: standardGap
23627 }, defn);
23628 };
23629
23630 defineArrowShape('none', {
23631 collide: falsify,
23632 roughCollide: falsify,
23633 draw: noop$1,
23634 spacing: zeroify,
23635 gap: zeroify
23636 });
23637 defineArrowShape('triangle', {
23638 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
23639 });
23640 defineArrowShape('arrow', 'triangle');
23641 defineArrowShape('triangle-backcurve', {
23642 points: arrowShapes['triangle'].points,
23643 controlPoint: [0, -0.15],
23644 roughCollide: bbCollide,
23645 draw: function draw(context, size, angle, translation, edgeWidth) {
23646 var ptsTrans = transformPoints(this.points, size, angle, translation);
23647 var ctrlPt = this.controlPoint;
23648 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
23649 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
23650 },
23651 gap: function gap(edge) {
23652 return standardGap(edge) * 0.8;
23653 }
23654 });
23655 defineArrowShape('triangle-tee', {
23656 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
23657 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
23658 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
23659 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
23660 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
23661 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
23662 return inside;
23663 },
23664 draw: function draw(context, size, angle, translation, edgeWidth) {
23665 var triPts = transformPoints(this.points, size, angle, translation);
23666 var teePts = transformPoints(this.pointsTee, size, angle, translation);
23667 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
23668 }
23669 });
23670 defineArrowShape('circle-triangle', {
23671 radius: 0.15,
23672 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
23673 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
23674 var t = translation;
23675 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
23676 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
23677 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
23678 },
23679 draw: function draw(context, size, angle, translation, edgeWidth) {
23680 var triPts = transformPoints(this.pointsTr, size, angle, translation);
23681 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
23682 },
23683 spacing: function spacing(edge) {
23684 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
23685 }
23686 });
23687 defineArrowShape('triangle-cross', {
23688 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
23689 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
23690 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
23691 0.15, -0.4],
23692 crossLinePts: function crossLinePts(size, edgeWidth) {
23693 // shift points so that the distance between the cross points matches edge width
23694 var p = this.baseCrossLinePts.slice();
23695 var shiftFactor = edgeWidth / size;
23696 var y0 = 3;
23697 var y1 = 5;
23698 p[y0] = p[y0] - shiftFactor;
23699 p[y1] = p[y1] - shiftFactor;
23700 return p;
23701 },
23702 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
23703 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
23704 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
23705 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
23706 return inside;
23707 },
23708 draw: function draw(context, size, angle, translation, edgeWidth) {
23709 var triPts = transformPoints(this.points, size, angle, translation);
23710 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
23711 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
23712 }
23713 });
23714 defineArrowShape('vee', {
23715 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
23716 gap: function gap(edge) {
23717 return standardGap(edge) * 0.525;
23718 }
23719 });
23720 defineArrowShape('circle', {
23721 radius: 0.15,
23722 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
23723 var t = translation;
23724 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
23725 return inside;
23726 },
23727 draw: function draw(context, size, angle, translation, edgeWidth) {
23728 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
23729 },
23730 spacing: function spacing(edge) {
23731 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
23732 }
23733 });
23734 defineArrowShape('tee', {
23735 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
23736 spacing: function spacing(edge) {
23737 return 1;
23738 },
23739 gap: function gap(edge) {
23740 return 1;
23741 }
23742 });
23743 defineArrowShape('square', {
23744 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
23745 });
23746 defineArrowShape('diamond', {
23747 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
23748 gap: function gap(edge) {
23749 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
23750 }
23751 });
23752 defineArrowShape('chevron', {
23753 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
23754 gap: function gap(edge) {
23755 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
23756 }
23757 });
23758 };
23759
23760 var BRp$e = {}; // Project mouse
23761
23762 BRp$e.projectIntoViewport = function (clientX, clientY) {
23763 var cy = this.cy;
23764 var offsets = this.findContainerClientCoords();
23765 var offsetLeft = offsets[0];
23766 var offsetTop = offsets[1];
23767 var scale = offsets[4];
23768 var pan = cy.pan();
23769 var zoom = cy.zoom();
23770 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
23771 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
23772 return [x, y];
23773 };
23774
23775 BRp$e.findContainerClientCoords = function () {
23776 if (this.containerBB) {
23777 return this.containerBB;
23778 }
23779
23780 var container = this.container;
23781 var rect = container.getBoundingClientRect();
23782 var style = window$1.getComputedStyle(container);
23783
23784 var styleValue = function styleValue(name) {
23785 return parseFloat(style.getPropertyValue(name));
23786 };
23787
23788 var padding = {
23789 left: styleValue('padding-left'),
23790 right: styleValue('padding-right'),
23791 top: styleValue('padding-top'),
23792 bottom: styleValue('padding-bottom')
23793 };
23794 var border = {
23795 left: styleValue('border-left-width'),
23796 right: styleValue('border-right-width'),
23797 top: styleValue('border-top-width'),
23798 bottom: styleValue('border-bottom-width')
23799 };
23800 var clientWidth = container.clientWidth;
23801 var clientHeight = container.clientHeight;
23802 var paddingHor = padding.left + padding.right;
23803 var paddingVer = padding.top + padding.bottom;
23804 var borderHor = border.left + border.right;
23805 var scale = rect.width / (clientWidth + borderHor);
23806 var unscaledW = clientWidth - paddingHor;
23807 var unscaledH = clientHeight - paddingVer;
23808 var left = rect.left + padding.left + border.left;
23809 var top = rect.top + padding.top + border.top;
23810 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
23811 };
23812
23813 BRp$e.invalidateContainerClientCoordsCache = function () {
23814 this.containerBB = null;
23815 };
23816
23817 BRp$e.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
23818 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
23819 };
23820
23821 BRp$e.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
23822 var self = this;
23823 var r = this;
23824 var eles = r.getCachedZSortedEles();
23825 var near = []; // 1 node max, 1 edge max
23826
23827 var zoom = r.cy.zoom();
23828 var hasCompounds = r.cy.hasCompoundNodes();
23829 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
23830 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
23831 var labelThreshold = (isTouch ? 8 : 2) / zoom;
23832 var minSqDist = Infinity;
23833 var nearEdge;
23834 var nearNode;
23835
23836 if (interactiveElementsOnly) {
23837 eles = eles.interactive;
23838 }
23839
23840 function addEle(ele, sqDist) {
23841 if (ele.isNode()) {
23842 if (nearNode) {
23843 return; // can't replace node
23844 } else {
23845 nearNode = ele;
23846 near.push(ele);
23847 }
23848 }
23849
23850 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
23851 if (nearEdge) {
23852 // then replace existing edge
23853 // can replace only if same z-index
23854 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) {
23855 for (var i = 0; i < near.length; i++) {
23856 if (near[i].isEdge()) {
23857 near[i] = ele;
23858 nearEdge = ele;
23859 minSqDist = sqDist != null ? sqDist : minSqDist;
23860 break;
23861 }
23862 }
23863 }
23864 } else {
23865 near.push(ele);
23866 nearEdge = ele;
23867 minSqDist = sqDist != null ? sqDist : minSqDist;
23868 }
23869 }
23870 }
23871
23872 function checkNode(node) {
23873 var width = node.outerWidth() + 2 * nodeThreshold;
23874 var height = node.outerHeight() + 2 * nodeThreshold;
23875 var hw = width / 2;
23876 var hh = height / 2;
23877 var pos = node.position();
23878
23879 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
23880 && pos.y - hh <= y && y <= pos.y + hh // bb check y
23881 ) {
23882 var shape = r.nodeShapes[self.getNodeShape(node)];
23883
23884 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
23885 addEle(node, 0);
23886 return true;
23887 }
23888 }
23889 }
23890
23891 function checkEdge(edge) {
23892 var _p = edge._private;
23893 var rs = _p.rscratch;
23894 var styleWidth = edge.pstyle('width').pfValue;
23895 var scale = edge.pstyle('arrow-scale').value;
23896 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
23897
23898 var widthSq = width * width;
23899 var width2 = width * 2;
23900 var src = _p.source;
23901 var tgt = _p.target;
23902 var sqDist;
23903
23904 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
23905 var pts = rs.allpts;
23906
23907 for (var i = 0; i + 3 < pts.length; i += 2) {
23908 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]))) {
23909 addEle(edge, sqDist);
23910 return true;
23911 }
23912 }
23913 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
23914 var pts = rs.allpts;
23915
23916 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23917 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]))) {
23918 addEle(edge, sqDist);
23919 return true;
23920 }
23921 }
23922 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
23923
23924
23925 var src = src || _p.source;
23926 var tgt = tgt || _p.target;
23927 var arSize = self.getArrowWidth(styleWidth, scale);
23928 var arrows = [{
23929 name: 'source',
23930 x: rs.arrowStartX,
23931 y: rs.arrowStartY,
23932 angle: rs.srcArrowAngle
23933 }, {
23934 name: 'target',
23935 x: rs.arrowEndX,
23936 y: rs.arrowEndY,
23937 angle: rs.tgtArrowAngle
23938 }, {
23939 name: 'mid-source',
23940 x: rs.midX,
23941 y: rs.midY,
23942 angle: rs.midsrcArrowAngle
23943 }, {
23944 name: 'mid-target',
23945 x: rs.midX,
23946 y: rs.midY,
23947 angle: rs.midtgtArrowAngle
23948 }];
23949
23950 for (var i = 0; i < arrows.length; i++) {
23951 var ar = arrows[i];
23952 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
23953 var edgeWidth = edge.pstyle('width').pfValue;
23954
23955 if (shape.roughCollide(x, y, arSize, ar.angle, {
23956 x: ar.x,
23957 y: ar.y
23958 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
23959 x: ar.x,
23960 y: ar.y
23961 }, edgeWidth, edgeThreshold)) {
23962 addEle(edge);
23963 return true;
23964 }
23965 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
23966
23967
23968 if (hasCompounds && near.length > 0) {
23969 checkNode(src);
23970 checkNode(tgt);
23971 }
23972 }
23973
23974 function preprop(obj, name, pre) {
23975 return getPrefixedProperty(obj, name, pre);
23976 }
23977
23978 function checkLabel(ele, prefix) {
23979 var _p = ele._private;
23980 var th = labelThreshold;
23981 var prefixDash;
23982
23983 if (prefix) {
23984 prefixDash = prefix + '-';
23985 } else {
23986 prefixDash = '';
23987 }
23988
23989 ele.boundingBox();
23990 var bb = _p.labelBounds[prefix || 'main'];
23991 var text = ele.pstyle(prefixDash + 'label').value;
23992 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
23993
23994 if (!eventsEnabled || !text) {
23995 return;
23996 }
23997
23998 var lx = preprop(_p.rscratch, 'labelX', prefix);
23999 var ly = preprop(_p.rscratch, 'labelY', prefix);
24000 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
24001 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
24002 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
24003 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
24004
24005 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
24006
24007 var ly1 = bb.y1 - th - oy;
24008 var ly2 = bb.y2 + th - oy;
24009
24010 if (theta) {
24011 var cos = Math.cos(theta);
24012 var sin = Math.sin(theta);
24013
24014 var rotate = function rotate(x, y) {
24015 x = x - lx;
24016 y = y - ly;
24017 return {
24018 x: x * cos - y * sin + lx,
24019 y: x * sin + y * cos + ly
24020 };
24021 };
24022
24023 var px1y1 = rotate(lx1, ly1);
24024 var px1y2 = rotate(lx1, ly2);
24025 var px2y1 = rotate(lx2, ly1);
24026 var px2y2 = rotate(lx2, ly2);
24027 var points = [// with the margin added after the rotation is applied
24028 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
24029
24030 if (pointInsidePolygonPoints(x, y, points)) {
24031 addEle(ele);
24032 return true;
24033 }
24034 } else {
24035 // do a cheaper bb check
24036 if (inBoundingBox(bb, x, y)) {
24037 addEle(ele);
24038 return true;
24039 }
24040 }
24041 }
24042
24043 for (var i = eles.length - 1; i >= 0; i--) {
24044 // reverse order for precedence
24045 var ele = eles[i];
24046
24047 if (ele.isNode()) {
24048 checkNode(ele) || checkLabel(ele);
24049 } else {
24050 // then edge
24051 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
24052 }
24053 }
24054
24055 return near;
24056 }; // 'Give me everything from this box'
24057
24058
24059 BRp$e.getAllInBox = function (x1, y1, x2, y2) {
24060 var eles = this.getCachedZSortedEles().interactive;
24061 var box = [];
24062 var x1c = Math.min(x1, x2);
24063 var x2c = Math.max(x1, x2);
24064 var y1c = Math.min(y1, y2);
24065 var y2c = Math.max(y1, y2);
24066 x1 = x1c;
24067 x2 = x2c;
24068 y1 = y1c;
24069 y2 = y2c;
24070 var boxBb = makeBoundingBox({
24071 x1: x1,
24072 y1: y1,
24073 x2: x2,
24074 y2: y2
24075 });
24076
24077 for (var e = 0; e < eles.length; e++) {
24078 var ele = eles[e];
24079
24080 if (ele.isNode()) {
24081 var node = ele;
24082 var nodeBb = node.boundingBox({
24083 includeNodes: true,
24084 includeEdges: false,
24085 includeLabels: false
24086 });
24087
24088 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
24089 box.push(node);
24090 }
24091 } else {
24092 var edge = ele;
24093 var _p = edge._private;
24094 var rs = _p.rscratch;
24095
24096 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
24097 continue;
24098 }
24099
24100 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
24101 continue;
24102 }
24103
24104 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
24105 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
24106 var allInside = true;
24107
24108 for (var i = 0; i < pts.length; i++) {
24109 if (!pointInBoundingBox(boxBb, pts[i])) {
24110 allInside = false;
24111 break;
24112 }
24113 }
24114
24115 if (allInside) {
24116 box.push(edge);
24117 }
24118 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
24119 box.push(edge);
24120 }
24121 }
24122 }
24123
24124 return box;
24125 };
24126
24127 var BRp$d = {};
24128
24129 BRp$d.calculateArrowAngles = function (edge) {
24130 var rs = edge._private.rscratch;
24131 var isHaystack = rs.edgeType === 'haystack';
24132 var isBezier = rs.edgeType === 'bezier';
24133 var isMultibezier = rs.edgeType === 'multibezier';
24134 var isSegments = rs.edgeType === 'segments';
24135 var isCompound = rs.edgeType === 'compound';
24136 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
24137
24138 var dispX, dispY;
24139 var startX, startY, endX, endY, midX, midY;
24140
24141 if (isHaystack) {
24142 startX = rs.haystackPts[0];
24143 startY = rs.haystackPts[1];
24144 endX = rs.haystackPts[2];
24145 endY = rs.haystackPts[3];
24146 } else {
24147 startX = rs.arrowStartX;
24148 startY = rs.arrowStartY;
24149 endX = rs.arrowEndX;
24150 endY = rs.arrowEndY;
24151 }
24152
24153 midX = rs.midX;
24154 midY = rs.midY; // source
24155 //
24156
24157 if (isSegments) {
24158 dispX = startX - rs.segpts[0];
24159 dispY = startY - rs.segpts[1];
24160 } else if (isMultibezier || isCompound || isSelf || isBezier) {
24161 var pts = rs.allpts;
24162 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
24163 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
24164 dispX = startX - bX;
24165 dispY = startY - bY;
24166 } else {
24167 dispX = startX - midX;
24168 dispY = startY - midY;
24169 }
24170
24171 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
24172 //
24173
24174 var midX = rs.midX;
24175 var midY = rs.midY;
24176
24177 if (isHaystack) {
24178 midX = (startX + endX) / 2;
24179 midY = (startY + endY) / 2;
24180 }
24181
24182 dispX = endX - startX;
24183 dispY = endY - startY;
24184
24185 if (isSegments) {
24186 var pts = rs.allpts;
24187
24188 if (pts.length / 2 % 2 === 0) {
24189 var i2 = pts.length / 2;
24190 var i1 = i2 - 2;
24191 dispX = pts[i2] - pts[i1];
24192 dispY = pts[i2 + 1] - pts[i1 + 1];
24193 } else {
24194 var i2 = pts.length / 2 - 1;
24195 var i1 = i2 - 2;
24196 var i3 = i2 + 2;
24197 dispX = pts[i2] - pts[i1];
24198 dispY = pts[i2 + 1] - pts[i1 + 1];
24199 }
24200 } else if (isMultibezier || isCompound || isSelf) {
24201 var pts = rs.allpts;
24202 var cpts = rs.ctrlpts;
24203 var bp0x, bp0y;
24204 var bp1x, bp1y;
24205
24206 if (cpts.length / 2 % 2 === 0) {
24207 var p0 = pts.length / 2 - 1; // startpt
24208
24209 var ic = p0 + 2;
24210 var p1 = ic + 2;
24211 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
24212 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
24213 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
24214 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
24215 } else {
24216 var ic = pts.length / 2 - 1; // ctrpt
24217
24218 var p0 = ic - 2; // startpt
24219
24220 var p1 = ic + 2; // endpt
24221
24222 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
24223 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
24224 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
24225 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
24226 }
24227
24228 dispX = bp1x - bp0x;
24229 dispY = bp1y - bp0y;
24230 }
24231
24232 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
24233 rs.midDispX = dispX;
24234 rs.midDispY = dispY; // mid source
24235 //
24236
24237 dispX *= -1;
24238 dispY *= -1;
24239
24240 if (isSegments) {
24241 var pts = rs.allpts;
24242
24243 if (pts.length / 2 % 2 === 0) ; else {
24244 var i2 = pts.length / 2 - 1;
24245 var i3 = i2 + 2;
24246 dispX = -(pts[i3] - pts[i2]);
24247 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
24248 }
24249 }
24250
24251 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
24252 //
24253
24254 if (isSegments) {
24255 dispX = endX - rs.segpts[rs.segpts.length - 2];
24256 dispY = endY - rs.segpts[rs.segpts.length - 1];
24257 } else if (isMultibezier || isCompound || isSelf || isBezier) {
24258 var pts = rs.allpts;
24259 var l = pts.length;
24260 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
24261 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
24262 dispX = endX - bX;
24263 dispY = endY - bY;
24264 } else {
24265 dispX = endX - midX;
24266 dispY = endY - midY;
24267 }
24268
24269 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
24270 };
24271
24272 BRp$d.getArrowWidth = BRp$d.getArrowHeight = function (edgeWidth, scale) {
24273 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
24274 var cachedVal = cache[edgeWidth + ', ' + scale];
24275
24276 if (cachedVal) {
24277 return cachedVal;
24278 }
24279
24280 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
24281 cache[edgeWidth + ', ' + scale] = cachedVal;
24282 return cachedVal;
24283 };
24284
24285 var BRp$c = {};
24286
24287 BRp$c.findHaystackPoints = function (edges) {
24288 for (var i = 0; i < edges.length; i++) {
24289 var edge = edges[i];
24290 var _p = edge._private;
24291 var rs = _p.rscratch;
24292
24293 if (!rs.haystack) {
24294 var angle = Math.random() * 2 * Math.PI;
24295 rs.source = {
24296 x: Math.cos(angle),
24297 y: Math.sin(angle)
24298 };
24299 angle = Math.random() * 2 * Math.PI;
24300 rs.target = {
24301 x: Math.cos(angle),
24302 y: Math.sin(angle)
24303 };
24304 }
24305
24306 var src = _p.source;
24307 var tgt = _p.target;
24308 var srcPos = src.position();
24309 var tgtPos = tgt.position();
24310 var srcW = src.width();
24311 var tgtW = tgt.width();
24312 var srcH = src.height();
24313 var tgtH = tgt.height();
24314 var radius = edge.pstyle('haystack-radius').value;
24315 var halfRadius = radius / 2; // b/c have to half width/height
24316
24317 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];
24318 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
24319 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
24320
24321 rs.edgeType = 'haystack';
24322 rs.haystack = true;
24323 this.storeEdgeProjections(edge);
24324 this.calculateArrowAngles(edge);
24325 this.recalculateEdgeLabelProjections(edge);
24326 this.calculateLabelAngles(edge);
24327 }
24328 };
24329
24330 BRp$c.findSegmentsPoints = function (edge, pairInfo) {
24331 // Segments (multiple straight lines)
24332 var rs = edge._private.rscratch;
24333 var posPts = pairInfo.posPts,
24334 intersectionPts = pairInfo.intersectionPts,
24335 vectorNormInverse = pairInfo.vectorNormInverse;
24336 var edgeDistances = edge.pstyle('edge-distances').value;
24337 var segmentWs = edge.pstyle('segment-weights');
24338 var segmentDs = edge.pstyle('segment-distances');
24339 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
24340 rs.edgeType = 'segments';
24341 rs.segpts = [];
24342
24343 for (var s = 0; s < segmentsN; s++) {
24344 var w = segmentWs.pfValue[s];
24345 var d = segmentDs.pfValue[s];
24346 var w1 = 1 - w;
24347 var w2 = w;
24348 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
24349 var adjustedMidpt = {
24350 x: midptPts.x1 * w1 + midptPts.x2 * w2,
24351 y: midptPts.y1 * w1 + midptPts.y2 * w2
24352 };
24353 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
24354 }
24355 };
24356
24357 BRp$c.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
24358 // Self-edge
24359 var rs = edge._private.rscratch;
24360 var dirCounts = pairInfo.dirCounts,
24361 srcPos = pairInfo.srcPos;
24362 var ctrlptDists = edge.pstyle('control-point-distances');
24363 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
24364 var loopDir = edge.pstyle('loop-direction').pfValue;
24365 var loopSwp = edge.pstyle('loop-sweep').pfValue;
24366 var stepSize = edge.pstyle('control-point-step-size').pfValue;
24367 rs.edgeType = 'self';
24368 var j = i;
24369 var loopDist = stepSize;
24370
24371 if (edgeIsUnbundled) {
24372 j = 0;
24373 loopDist = ctrlptDist;
24374 }
24375
24376 var loopAngle = loopDir - Math.PI / 2;
24377 var outAngle = loopAngle - loopSwp / 2;
24378 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
24379
24380 var dc = String(loopDir + '_' + loopSwp);
24381 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
24382 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)];
24383 };
24384
24385 BRp$c.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
24386 // Compound edge
24387 var rs = edge._private.rscratch;
24388 rs.edgeType = 'compound';
24389 var srcPos = pairInfo.srcPos,
24390 tgtPos = pairInfo.tgtPos,
24391 srcW = pairInfo.srcW,
24392 srcH = pairInfo.srcH,
24393 tgtW = pairInfo.tgtW,
24394 tgtH = pairInfo.tgtH;
24395 var stepSize = edge.pstyle('control-point-step-size').pfValue;
24396 var ctrlptDists = edge.pstyle('control-point-distances');
24397 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
24398 var j = i;
24399 var loopDist = stepSize;
24400
24401 if (edgeIsUnbundled) {
24402 j = 0;
24403 loopDist = ctrlptDist;
24404 }
24405
24406 var loopW = 50;
24407 var loopaPos = {
24408 x: srcPos.x - srcW / 2,
24409 y: srcPos.y - srcH / 2
24410 };
24411 var loopbPos = {
24412 x: tgtPos.x - tgtW / 2,
24413 y: tgtPos.y - tgtH / 2
24414 };
24415 var loopPos = {
24416 x: Math.min(loopaPos.x, loopbPos.x),
24417 y: Math.min(loopaPos.y, loopbPos.y)
24418 }; // avoids cases with impossible beziers
24419
24420 var minCompoundStretch = 0.5;
24421 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
24422 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
24423 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];
24424 };
24425
24426 BRp$c.findStraightEdgePoints = function (edge) {
24427 // Straight edge within bundle
24428 edge._private.rscratch.edgeType = 'straight';
24429 };
24430
24431 BRp$c.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
24432 var rs = edge._private.rscratch;
24433 var vectorNormInverse = pairInfo.vectorNormInverse,
24434 posPts = pairInfo.posPts,
24435 intersectionPts = pairInfo.intersectionPts;
24436 var edgeDistances = edge.pstyle('edge-distances').value;
24437 var stepSize = edge.pstyle('control-point-step-size').pfValue;
24438 var ctrlptDists = edge.pstyle('control-point-distances');
24439 var ctrlptWs = edge.pstyle('control-point-weights');
24440 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
24441 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
24442 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
24443
24444 var multi = edgeIsUnbundled;
24445 rs.edgeType = multi ? 'multibezier' : 'bezier';
24446 rs.ctrlpts = [];
24447
24448 for (var b = 0; b < bezierN; b++) {
24449 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
24450 var manctrlptDist = void 0;
24451 var sign = signum(normctrlptDist);
24452
24453 if (multi) {
24454 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
24455
24456 ctrlptWeight = ctrlptWs.value[b];
24457 }
24458
24459 if (edgeIsUnbundled) {
24460 // multi or single unbundled
24461 manctrlptDist = ctrlptDist;
24462 } else {
24463 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
24464 }
24465
24466 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
24467 var w1 = 1 - ctrlptWeight;
24468 var w2 = ctrlptWeight;
24469 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
24470 var adjustedMidpt = {
24471 x: midptPts.x1 * w1 + midptPts.x2 * w2,
24472 y: midptPts.y1 * w1 + midptPts.y2 * w2
24473 };
24474 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
24475 }
24476 };
24477
24478 BRp$c.findTaxiPoints = function (edge, pairInfo) {
24479 // Taxicab geometry with two turns maximum
24480 var rs = edge._private.rscratch;
24481 rs.edgeType = 'segments';
24482 var VERTICAL = 'vertical';
24483 var HORIZONTAL = 'horizontal';
24484 var LEFTWARD = 'leftward';
24485 var RIGHTWARD = 'rightward';
24486 var DOWNWARD = 'downward';
24487 var UPWARD = 'upward';
24488 var AUTO = 'auto';
24489 var posPts = pairInfo.posPts,
24490 srcW = pairInfo.srcW,
24491 srcH = pairInfo.srcH,
24492 tgtW = pairInfo.tgtW,
24493 tgtH = pairInfo.tgtH;
24494 var edgeDistances = edge.pstyle('edge-distances').value;
24495 var dIncludesNodeBody = edgeDistances !== 'node-position';
24496 var taxiDir = edge.pstyle('taxi-direction').value;
24497 var rawTaxiDir = taxiDir; // unprocessed value
24498
24499 var taxiTurn = edge.pstyle('taxi-turn');
24500 var turnIsPercent = taxiTurn.units === '%';
24501 var taxiTurnPfVal = taxiTurn.pfValue;
24502 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
24503
24504 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
24505 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
24506 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
24507 var pdx = posPts.x2 - posPts.x1;
24508 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
24509
24510 var subDWH = function subDWH(dxy, dwh) {
24511 if (dxy > 0) {
24512 return Math.max(dxy - dwh, 0);
24513 } else {
24514 return Math.min(dxy + dwh, 0);
24515 }
24516 };
24517
24518 var dx = subDWH(pdx, dw);
24519 var dy = subDWH(pdy, dh);
24520 var isExplicitDir = false;
24521
24522 if (rawTaxiDir === AUTO) {
24523 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
24524 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
24525 taxiDir = VERTICAL;
24526 isExplicitDir = true;
24527 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
24528 taxiDir = HORIZONTAL;
24529 isExplicitDir = true;
24530 }
24531
24532 var isVert = taxiDir === VERTICAL;
24533 var l = isVert ? dy : dx;
24534 var pl = isVert ? pdy : pdx;
24535 var sgnL = signum(pl);
24536 var forcedDir = false;
24537
24538 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
24539 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
24540 sgnL *= -1;
24541 l = sgnL * Math.abs(l);
24542 forcedDir = true;
24543 }
24544
24545 var d;
24546
24547 if (turnIsPercent) {
24548 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
24549 d = p * l;
24550 } else {
24551 var k = taxiTurnPfVal < 0 ? l : 0;
24552 d = k + taxiTurnPfVal * sgnL;
24553 }
24554
24555 var getIsTooClose = function getIsTooClose(d) {
24556 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
24557 };
24558
24559 var isTooCloseSrc = getIsTooClose(d);
24560 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
24561 var isTooClose = isTooCloseSrc || isTooCloseTgt;
24562
24563 if (isTooClose && !forcedDir) {
24564 // non-ideal routing
24565 if (isVert) {
24566 // vertical fallbacks
24567 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
24568 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
24569
24570 if (lShapeInsideSrc) {
24571 // horizontal Z-shape (direction not respected)
24572 var x = (posPts.x1 + posPts.x2) / 2;
24573 var y1 = posPts.y1,
24574 y2 = posPts.y2;
24575 rs.segpts = [x, y1, x, y2];
24576 } else if (lShapeInsideTgt) {
24577 // vertical Z-shape (distance not respected)
24578 var y = (posPts.y1 + posPts.y2) / 2;
24579 var x1 = posPts.x1,
24580 x2 = posPts.x2;
24581 rs.segpts = [x1, y, x2, y];
24582 } else {
24583 // L-shape fallback (turn distance not respected, but works well with tree siblings)
24584 rs.segpts = [posPts.x1, posPts.y2];
24585 }
24586 } else {
24587 // horizontal fallbacks
24588 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
24589
24590 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
24591
24592 if (_lShapeInsideSrc) {
24593 // vertical Z-shape (direction not respected)
24594 var _y = (posPts.y1 + posPts.y2) / 2;
24595
24596 var _x = posPts.x1,
24597 _x2 = posPts.x2;
24598 rs.segpts = [_x, _y, _x2, _y];
24599 } else if (_lShapeInsideTgt) {
24600 // horizontal Z-shape (turn distance not respected)
24601 var _x3 = (posPts.x1 + posPts.x2) / 2;
24602
24603 var _y2 = posPts.y1,
24604 _y3 = posPts.y2;
24605 rs.segpts = [_x3, _y2, _x3, _y3];
24606 } else {
24607 // L-shape (turn distance not respected, but works well for tree siblings)
24608 rs.segpts = [posPts.x2, posPts.y1];
24609 }
24610 }
24611 } else {
24612 // ideal routing
24613 if (isVert) {
24614 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
24615
24616 var _x4 = posPts.x1,
24617 _x5 = posPts.x2;
24618 rs.segpts = [_x4, _y4, _x5, _y4];
24619 } else {
24620 // horizontal
24621 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
24622
24623 var _y5 = posPts.y1,
24624 _y6 = posPts.y2;
24625 rs.segpts = [_x6, _y5, _x6, _y6];
24626 }
24627 }
24628 };
24629
24630 BRp$c.tryToCorrectInvalidPoints = function (edge, pairInfo) {
24631 var rs = edge._private.rscratch; // can only correct beziers for now...
24632
24633 if (rs.edgeType === 'bezier') {
24634 var srcPos = pairInfo.srcPos,
24635 tgtPos = pairInfo.tgtPos,
24636 srcW = pairInfo.srcW,
24637 srcH = pairInfo.srcH,
24638 tgtW = pairInfo.tgtW,
24639 tgtH = pairInfo.tgtH,
24640 srcShape = pairInfo.srcShape,
24641 tgtShape = pairInfo.tgtShape;
24642 var badStart = !number$1(rs.startX) || !number$1(rs.startY);
24643 var badAStart = !number$1(rs.arrowStartX) || !number$1(rs.arrowStartY);
24644 var badEnd = !number$1(rs.endX) || !number$1(rs.endY);
24645 var badAEnd = !number$1(rs.arrowEndX) || !number$1(rs.arrowEndY);
24646 var minCpADistFactor = 3;
24647 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
24648 var minCpADist = minCpADistFactor * arrowW;
24649 var startACpDist = dist({
24650 x: rs.ctrlpts[0],
24651 y: rs.ctrlpts[1]
24652 }, {
24653 x: rs.startX,
24654 y: rs.startY
24655 });
24656 var closeStartACp = startACpDist < minCpADist;
24657 var endACpDist = dist({
24658 x: rs.ctrlpts[0],
24659 y: rs.ctrlpts[1]
24660 }, {
24661 x: rs.endX,
24662 y: rs.endY
24663 });
24664 var closeEndACp = endACpDist < minCpADist;
24665 var overlapping = false;
24666
24667 if (badStart || badAStart || closeStartACp) {
24668 overlapping = true; // project control point along line from src centre to outside the src shape
24669 // (otherwise intersection will yield nothing)
24670
24671 var cpD = {
24672 // delta
24673 x: rs.ctrlpts[0] - srcPos.x,
24674 y: rs.ctrlpts[1] - srcPos.y
24675 };
24676 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
24677
24678 var cpM = {
24679 // normalised delta
24680 x: cpD.x / cpL,
24681 y: cpD.y / cpL
24682 };
24683 var radius = Math.max(srcW, srcH);
24684 var cpProj = {
24685 // *2 radius guarantees outside shape
24686 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
24687 y: rs.ctrlpts[1] + cpM.y * 2 * radius
24688 };
24689 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
24690
24691 if (closeStartACp) {
24692 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
24693 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
24694 } else {
24695 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
24696 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
24697 }
24698 }
24699
24700 if (badEnd || badAEnd || closeEndACp) {
24701 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
24702 // (otherwise intersection will yield nothing)
24703
24704 var _cpD = {
24705 // delta
24706 x: rs.ctrlpts[0] - tgtPos.x,
24707 y: rs.ctrlpts[1] - tgtPos.y
24708 };
24709
24710 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
24711
24712
24713 var _cpM = {
24714 // normalised delta
24715 x: _cpD.x / _cpL,
24716 y: _cpD.y / _cpL
24717 };
24718
24719 var _radius = Math.max(srcW, srcH);
24720
24721 var _cpProj = {
24722 // *2 radius guarantees outside shape
24723 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
24724 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
24725 };
24726 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
24727
24728 if (closeEndACp) {
24729 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
24730 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
24731 } else {
24732 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
24733 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
24734 }
24735 }
24736
24737 if (overlapping) {
24738 // recalc endpts
24739 this.findEndpoints(edge);
24740 }
24741 }
24742 };
24743
24744 BRp$c.storeAllpts = function (edge) {
24745 var rs = edge._private.rscratch;
24746
24747 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
24748 rs.allpts = [];
24749 rs.allpts.push(rs.startX, rs.startY);
24750
24751 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
24752 // ctrl pt itself
24753 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
24754
24755 if (b + 3 < rs.ctrlpts.length) {
24756 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
24757 }
24758 }
24759
24760 rs.allpts.push(rs.endX, rs.endY);
24761 var m, mt;
24762
24763 if (rs.ctrlpts.length / 2 % 2 === 0) {
24764 m = rs.allpts.length / 2 - 1;
24765 rs.midX = rs.allpts[m];
24766 rs.midY = rs.allpts[m + 1];
24767 } else {
24768 m = rs.allpts.length / 2 - 3;
24769 mt = 0.5;
24770 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
24771 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
24772 }
24773 } else if (rs.edgeType === 'straight') {
24774 // need to calc these after endpts
24775 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
24776
24777 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
24778 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
24779 } else if (rs.edgeType === 'segments') {
24780 rs.allpts = [];
24781 rs.allpts.push(rs.startX, rs.startY);
24782 rs.allpts.push.apply(rs.allpts, rs.segpts);
24783 rs.allpts.push(rs.endX, rs.endY);
24784
24785 if (rs.segpts.length % 4 === 0) {
24786 var i2 = rs.segpts.length / 2;
24787 var i1 = i2 - 2;
24788 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
24789 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
24790 } else {
24791 var _i = rs.segpts.length / 2 - 1;
24792
24793 rs.midX = rs.segpts[_i];
24794 rs.midY = rs.segpts[_i + 1];
24795 }
24796 }
24797 };
24798
24799 BRp$c.checkForInvalidEdgeWarning = function (edge) {
24800 var rs = edge[0]._private.rscratch;
24801
24802 if (rs.nodesOverlap || number$1(rs.startX) && number$1(rs.startY) && number$1(rs.endX) && number$1(rs.endY)) {
24803 rs.loggedErr = false;
24804 } else {
24805 if (!rs.loggedErr) {
24806 rs.loggedErr = true;
24807 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.');
24808 }
24809 }
24810 };
24811
24812 BRp$c.findEdgeControlPoints = function (edges) {
24813 var _this = this;
24814
24815 if (!edges || edges.length === 0) {
24816 return;
24817 }
24818
24819 var r = this;
24820 var cy = r.cy;
24821 var hasCompounds = cy.hasCompoundNodes();
24822 var hashTable = {
24823 map: new Map$2(),
24824 get: function get(pairId) {
24825 var map2 = this.map.get(pairId[0]);
24826
24827 if (map2 != null) {
24828 return map2.get(pairId[1]);
24829 } else {
24830 return null;
24831 }
24832 },
24833 set: function set(pairId, val) {
24834 var map2 = this.map.get(pairId[0]);
24835
24836 if (map2 == null) {
24837 map2 = new Map$2();
24838 this.map.set(pairId[0], map2);
24839 }
24840
24841 map2.set(pairId[1], val);
24842 }
24843 };
24844 var pairIds = [];
24845 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
24846
24847 for (var i = 0; i < edges.length; i++) {
24848 var edge = edges[i];
24849 var _p = edge._private;
24850 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
24851 // they shouldn't take up space
24852
24853 if (edge.removed() || !edge.takesUpSpace()) {
24854 continue;
24855 }
24856
24857 if (curveStyle === 'haystack') {
24858 haystackEdges.push(edge);
24859 continue;
24860 }
24861
24862 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'straight-triangle' || curveStyle === 'taxi';
24863 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
24864 var src = _p.source;
24865 var tgt = _p.target;
24866 var srcIndex = src.poolIndex();
24867 var tgtIndex = tgt.poolIndex();
24868 var pairId = [srcIndex, tgtIndex].sort();
24869 var tableEntry = hashTable.get(pairId);
24870
24871 if (tableEntry == null) {
24872 tableEntry = {
24873 eles: []
24874 };
24875 hashTable.set(pairId, tableEntry);
24876 pairIds.push(pairId);
24877 }
24878
24879 tableEntry.eles.push(edge);
24880
24881 if (edgeIsUnbundled) {
24882 tableEntry.hasUnbundled = true;
24883 }
24884
24885 if (edgeIsBezier) {
24886 tableEntry.hasBezier = true;
24887 }
24888 } // for each pair (src, tgt), create the ctrl pts
24889 // Nested for loop is OK; total number of iterations for both loops = edgeCount
24890
24891
24892 var _loop = function _loop(p) {
24893 var pairId = pairIds[p];
24894 var pairInfo = hashTable.get(pairId);
24895 var swappedpairInfo = void 0;
24896
24897 if (!pairInfo.hasUnbundled) {
24898 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
24899 return e.isBundledBezier();
24900 });
24901 clearArray(pairInfo.eles);
24902 pllEdges.forEach(function (edge) {
24903 return pairInfo.eles.push(edge);
24904 }); // for each pair id, the edges should be sorted by index
24905
24906 pairInfo.eles.sort(function (edge1, edge2) {
24907 return edge1.poolIndex() - edge2.poolIndex();
24908 });
24909 }
24910
24911 var firstEdge = pairInfo.eles[0];
24912 var src = firstEdge.source();
24913 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
24914
24915 if (src.poolIndex() > tgt.poolIndex()) {
24916 var temp = src;
24917 src = tgt;
24918 tgt = temp;
24919 }
24920
24921 var srcPos = pairInfo.srcPos = src.position();
24922 var tgtPos = pairInfo.tgtPos = tgt.position();
24923 var srcW = pairInfo.srcW = src.outerWidth();
24924 var srcH = pairInfo.srcH = src.outerHeight();
24925 var tgtW = pairInfo.tgtW = tgt.outerWidth();
24926 var tgtH = pairInfo.tgtH = tgt.outerHeight();
24927
24928 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
24929
24930 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
24931
24932 pairInfo.dirCounts = {
24933 'north': 0,
24934 'west': 0,
24935 'south': 0,
24936 'east': 0,
24937 'northwest': 0,
24938 'southwest': 0,
24939 'northeast': 0,
24940 'southeast': 0
24941 };
24942
24943 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
24944 var _edge = pairInfo.eles[_i2];
24945 var rs = _edge[0]._private.rscratch;
24946
24947 var _curveStyle = _edge.pstyle('curve-style').value;
24948
24949 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
24950
24951
24952 var edgeIsSwapped = !src.same(_edge.source());
24953
24954 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
24955 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
24956
24957 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
24958 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
24959
24960 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
24961 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
24962 var intersectionPts = pairInfo.intersectionPts = {
24963 x1: srcOutside[0],
24964 x2: tgtOutside[0],
24965 y1: srcOutside[1],
24966 y2: tgtOutside[1]
24967 };
24968 var posPts = pairInfo.posPts = {
24969 x1: srcPos.x,
24970 x2: tgtPos.x,
24971 y1: srcPos.y,
24972 y2: tgtPos.y
24973 };
24974 var dy = tgtOutside[1] - srcOutside[1];
24975 var dx = tgtOutside[0] - srcOutside[0];
24976 var l = Math.sqrt(dx * dx + dy * dy);
24977 var vector = pairInfo.vector = {
24978 x: dx,
24979 y: dy
24980 };
24981 var vectorNorm = pairInfo.vectorNorm = {
24982 x: vector.x / l,
24983 y: vector.y / l
24984 };
24985 var vectorNormInverse = {
24986 x: -vectorNorm.y,
24987 y: vectorNorm.x
24988 }; // if node shapes overlap, then no ctrl pts to draw
24989
24990 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);
24991 pairInfo.vectorNormInverse = vectorNormInverse;
24992 swappedpairInfo = {
24993 nodesOverlap: pairInfo.nodesOverlap,
24994 dirCounts: pairInfo.dirCounts,
24995 calculatedIntersection: true,
24996 hasBezier: pairInfo.hasBezier,
24997 hasUnbundled: pairInfo.hasUnbundled,
24998 eles: pairInfo.eles,
24999 srcPos: tgtPos,
25000 tgtPos: srcPos,
25001 srcW: tgtW,
25002 srcH: tgtH,
25003 tgtW: srcW,
25004 tgtH: srcH,
25005 srcIntn: tgtIntn,
25006 tgtIntn: srcIntn,
25007 srcShape: tgtShape,
25008 tgtShape: srcShape,
25009 posPts: {
25010 x1: posPts.x2,
25011 y1: posPts.y2,
25012 x2: posPts.x1,
25013 y2: posPts.y1
25014 },
25015 intersectionPts: {
25016 x1: intersectionPts.x2,
25017 y1: intersectionPts.y2,
25018 x2: intersectionPts.x1,
25019 y2: intersectionPts.y1
25020 },
25021 vector: {
25022 x: -vector.x,
25023 y: -vector.y
25024 },
25025 vectorNorm: {
25026 x: -vectorNorm.x,
25027 y: -vectorNorm.y
25028 },
25029 vectorNormInverse: {
25030 x: -vectorNormInverse.x,
25031 y: -vectorNormInverse.y
25032 }
25033 };
25034 }
25035
25036 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
25037 rs.nodesOverlap = passedPairInfo.nodesOverlap;
25038 rs.srcIntn = passedPairInfo.srcIntn;
25039 rs.tgtIntn = passedPairInfo.tgtIntn;
25040
25041 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
25042 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
25043 } else if (src === tgt) {
25044 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
25045 } else if (_curveStyle === 'segments') {
25046 _this.findSegmentsPoints(_edge, passedPairInfo);
25047 } else if (_curveStyle === 'taxi') {
25048 _this.findTaxiPoints(_edge, passedPairInfo);
25049 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
25050 _this.findStraightEdgePoints(_edge);
25051 } else {
25052 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
25053 }
25054
25055 _this.findEndpoints(_edge);
25056
25057 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
25058
25059 _this.checkForInvalidEdgeWarning(_edge);
25060
25061 _this.storeAllpts(_edge);
25062
25063 _this.storeEdgeProjections(_edge);
25064
25065 _this.calculateArrowAngles(_edge);
25066
25067 _this.recalculateEdgeLabelProjections(_edge);
25068
25069 _this.calculateLabelAngles(_edge);
25070 } // for pair edges
25071
25072 };
25073
25074 for (var p = 0; p < pairIds.length; p++) {
25075 _loop(p);
25076 } // for pair ids
25077 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
25078
25079
25080 this.findHaystackPoints(haystackEdges);
25081 };
25082
25083 function getPts(pts) {
25084 var retPts = [];
25085
25086 if (pts == null) {
25087 return;
25088 }
25089
25090 for (var i = 0; i < pts.length; i += 2) {
25091 var x = pts[i];
25092 var y = pts[i + 1];
25093 retPts.push({
25094 x: x,
25095 y: y
25096 });
25097 }
25098
25099 return retPts;
25100 }
25101
25102 BRp$c.getSegmentPoints = function (edge) {
25103 var rs = edge[0]._private.rscratch;
25104 var type = rs.edgeType;
25105
25106 if (type === 'segments') {
25107 this.recalculateRenderedStyle(edge);
25108 return getPts(rs.segpts);
25109 }
25110 };
25111
25112 BRp$c.getControlPoints = function (edge) {
25113 var rs = edge[0]._private.rscratch;
25114 var type = rs.edgeType;
25115
25116 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
25117 this.recalculateRenderedStyle(edge);
25118 return getPts(rs.ctrlpts);
25119 }
25120 };
25121
25122 BRp$c.getEdgeMidpoint = function (edge) {
25123 var rs = edge[0]._private.rscratch;
25124 this.recalculateRenderedStyle(edge);
25125 return {
25126 x: rs.midX,
25127 y: rs.midY
25128 };
25129 };
25130
25131 var BRp$b = {};
25132
25133 BRp$b.manualEndptToPx = function (node, prop) {
25134 var r = this;
25135 var npos = node.position();
25136 var w = node.outerWidth();
25137 var h = node.outerHeight();
25138
25139 if (prop.value.length === 2) {
25140 var p = [prop.pfValue[0], prop.pfValue[1]];
25141
25142 if (prop.units[0] === '%') {
25143 p[0] = p[0] * w;
25144 }
25145
25146 if (prop.units[1] === '%') {
25147 p[1] = p[1] * h;
25148 }
25149
25150 p[0] += npos.x;
25151 p[1] += npos.y;
25152 return p;
25153 } else {
25154 var angle = prop.pfValue[0];
25155 angle = -Math.PI / 2 + angle; // start at 12 o'clock
25156
25157 var l = 2 * Math.max(w, h);
25158 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
25159 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
25160 }
25161 };
25162
25163 BRp$b.findEndpoints = function (edge) {
25164 var r = this;
25165 var intersect;
25166 var source = edge.source()[0];
25167 var target = edge.target()[0];
25168 var srcPos = source.position();
25169 var tgtPos = target.position();
25170 var tgtArShape = edge.pstyle('target-arrow-shape').value;
25171 var srcArShape = edge.pstyle('source-arrow-shape').value;
25172 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
25173 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
25174 var curveStyle = edge.pstyle('curve-style').value;
25175 var rs = edge._private.rscratch;
25176 var et = rs.edgeType;
25177 var taxi = curveStyle === 'taxi';
25178 var self = et === 'self' || et === 'compound';
25179 var bezier = et === 'bezier' || et === 'multibezier' || self;
25180 var multi = et !== 'bezier';
25181 var lines = et === 'straight' || et === 'segments';
25182 var segments = et === 'segments';
25183 var hasEndpts = bezier || multi || lines;
25184 var overrideEndpts = self || taxi;
25185 var srcManEndpt = edge.pstyle('source-endpoint');
25186 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
25187 var tgtManEndpt = edge.pstyle('target-endpoint');
25188 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
25189 rs.srcManEndpt = srcManEndpt;
25190 rs.tgtManEndpt = tgtManEndpt;
25191 var p1; // last known point of edge on target side
25192
25193 var p2; // last known point of edge on source side
25194
25195 var p1_i; // point to intersect with target shape
25196
25197 var p2_i; // point to intersect with source shape
25198
25199 if (bezier) {
25200 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
25201 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
25202 p1 = cpEnd;
25203 p2 = cpStart;
25204 } else if (lines) {
25205 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
25206 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
25207 p1 = tgtArrowFromPt;
25208 p2 = srcArrowFromPt;
25209 }
25210
25211 if (tgtManEndptVal === 'inside-to-node') {
25212 intersect = [tgtPos.x, tgtPos.y];
25213 } else if (tgtManEndpt.units) {
25214 intersect = this.manualEndptToPx(target, tgtManEndpt);
25215 } else if (tgtManEndptVal === 'outside-to-line') {
25216 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
25217 } else {
25218 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
25219 p1_i = p1;
25220 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
25221 p1_i = [srcPos.x, srcPos.y];
25222 }
25223
25224 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
25225
25226 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
25227 var trs = target._private.rscratch;
25228 var lw = trs.labelWidth;
25229 var lh = trs.labelHeight;
25230 var lx = trs.labelX;
25231 var ly = trs.labelY;
25232 var lw2 = lw / 2;
25233 var lh2 = lh / 2;
25234 var va = target.pstyle('text-valign').value;
25235
25236 if (va === 'top') {
25237 ly -= lh2;
25238 } else if (va === 'bottom') {
25239 ly += lh2;
25240 }
25241
25242 var ha = target.pstyle('text-halign').value;
25243
25244 if (ha === 'left') {
25245 lx -= lw2;
25246 } else if (ha === 'right') {
25247 lx += lw2;
25248 }
25249
25250 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);
25251
25252 if (labelIntersect.length > 0) {
25253 var refPt = srcPos;
25254 var intSqdist = sqdist(refPt, array2point(intersect));
25255 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
25256 var minSqDist = intSqdist;
25257
25258 if (labIntSqdist < intSqdist) {
25259 intersect = labelIntersect;
25260 minSqDist = labIntSqdist;
25261 }
25262
25263 if (labelIntersect.length > 2) {
25264 var labInt2SqDist = sqdist(refPt, {
25265 x: labelIntersect[2],
25266 y: labelIntersect[3]
25267 });
25268
25269 if (labInt2SqDist < minSqDist) {
25270 intersect = [labelIntersect[2], labelIntersect[3]];
25271 }
25272 }
25273 }
25274 }
25275 }
25276
25277 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
25278 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
25279 rs.endX = edgeEnd[0];
25280 rs.endY = edgeEnd[1];
25281 rs.arrowEndX = arrowEnd[0];
25282 rs.arrowEndY = arrowEnd[1];
25283
25284 if (srcManEndptVal === 'inside-to-node') {
25285 intersect = [srcPos.x, srcPos.y];
25286 } else if (srcManEndpt.units) {
25287 intersect = this.manualEndptToPx(source, srcManEndpt);
25288 } else if (srcManEndptVal === 'outside-to-line') {
25289 intersect = rs.srcIntn; // use cached value from ctrlpt calc
25290 } else {
25291 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
25292 p2_i = p2;
25293 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
25294 p2_i = [tgtPos.x, tgtPos.y];
25295 }
25296
25297 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
25298
25299 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
25300 var srs = source._private.rscratch;
25301 var _lw = srs.labelWidth;
25302 var _lh = srs.labelHeight;
25303 var _lx = srs.labelX;
25304 var _ly = srs.labelY;
25305
25306 var _lw2 = _lw / 2;
25307
25308 var _lh2 = _lh / 2;
25309
25310 var _va = source.pstyle('text-valign').value;
25311
25312 if (_va === 'top') {
25313 _ly -= _lh2;
25314 } else if (_va === 'bottom') {
25315 _ly += _lh2;
25316 }
25317
25318 var _ha = source.pstyle('text-halign').value;
25319
25320 if (_ha === 'left') {
25321 _lx -= _lw2;
25322 } else if (_ha === 'right') {
25323 _lx += _lw2;
25324 }
25325
25326 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);
25327
25328 if (_labelIntersect.length > 0) {
25329 var _refPt = tgtPos;
25330
25331 var _intSqdist = sqdist(_refPt, array2point(intersect));
25332
25333 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
25334
25335 var _minSqDist = _intSqdist;
25336
25337 if (_labIntSqdist < _intSqdist) {
25338 intersect = [_labelIntersect[0], _labelIntersect[1]];
25339 _minSqDist = _labIntSqdist;
25340 }
25341
25342 if (_labelIntersect.length > 2) {
25343 var _labInt2SqDist = sqdist(_refPt, {
25344 x: _labelIntersect[2],
25345 y: _labelIntersect[3]
25346 });
25347
25348 if (_labInt2SqDist < _minSqDist) {
25349 intersect = [_labelIntersect[2], _labelIntersect[3]];
25350 }
25351 }
25352 }
25353 }
25354 }
25355
25356 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
25357 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
25358 rs.startX = edgeStart[0];
25359 rs.startY = edgeStart[1];
25360 rs.arrowStartX = arrowStart[0];
25361 rs.arrowStartY = arrowStart[1];
25362
25363 if (hasEndpts) {
25364 if (!number$1(rs.startX) || !number$1(rs.startY) || !number$1(rs.endX) || !number$1(rs.endY)) {
25365 rs.badLine = true;
25366 } else {
25367 rs.badLine = false;
25368 }
25369 }
25370 };
25371
25372 BRp$b.getSourceEndpoint = function (edge) {
25373 var rs = edge[0]._private.rscratch;
25374 this.recalculateRenderedStyle(edge);
25375
25376 switch (rs.edgeType) {
25377 case 'haystack':
25378 return {
25379 x: rs.haystackPts[0],
25380 y: rs.haystackPts[1]
25381 };
25382
25383 default:
25384 return {
25385 x: rs.arrowStartX,
25386 y: rs.arrowStartY
25387 };
25388 }
25389 };
25390
25391 BRp$b.getTargetEndpoint = function (edge) {
25392 var rs = edge[0]._private.rscratch;
25393 this.recalculateRenderedStyle(edge);
25394
25395 switch (rs.edgeType) {
25396 case 'haystack':
25397 return {
25398 x: rs.haystackPts[2],
25399 y: rs.haystackPts[3]
25400 };
25401
25402 default:
25403 return {
25404 x: rs.arrowEndX,
25405 y: rs.arrowEndY
25406 };
25407 }
25408 };
25409
25410 var BRp$a = {};
25411
25412 function pushBezierPts(r, edge, pts) {
25413 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
25414 return qbezierAt(p1, p2, p3, t);
25415 };
25416
25417 var _p = edge._private;
25418 var bpts = _p.rstyle.bezierPts;
25419
25420 for (var i = 0; i < r.bezierProjPcts.length; i++) {
25421 var p = r.bezierProjPcts[i];
25422 bpts.push({
25423 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
25424 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
25425 });
25426 }
25427 }
25428
25429 BRp$a.storeEdgeProjections = function (edge) {
25430 var _p = edge._private;
25431 var rs = _p.rscratch;
25432 var et = rs.edgeType; // clear the cached points state
25433
25434 _p.rstyle.bezierPts = null;
25435 _p.rstyle.linePts = null;
25436 _p.rstyle.haystackPts = null;
25437
25438 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
25439 _p.rstyle.bezierPts = [];
25440
25441 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
25442 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
25443 }
25444 } else if (et === 'segments') {
25445 var lpts = _p.rstyle.linePts = [];
25446
25447 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
25448 lpts.push({
25449 x: rs.allpts[i],
25450 y: rs.allpts[i + 1]
25451 });
25452 }
25453 } else if (et === 'haystack') {
25454 var hpts = rs.haystackPts;
25455 _p.rstyle.haystackPts = [{
25456 x: hpts[0],
25457 y: hpts[1]
25458 }, {
25459 x: hpts[2],
25460 y: hpts[3]
25461 }];
25462 }
25463
25464 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
25465 };
25466
25467 BRp$a.recalculateEdgeProjections = function (edges) {
25468 this.findEdgeControlPoints(edges);
25469 };
25470
25471 /* global document */
25472
25473 var BRp$9 = {};
25474
25475 BRp$9.recalculateNodeLabelProjection = function (node) {
25476 var content = node.pstyle('label').strValue;
25477
25478 if (emptyString(content)) {
25479 return;
25480 }
25481
25482 var textX, textY;
25483 var _p = node._private;
25484 var nodeWidth = node.width();
25485 var nodeHeight = node.height();
25486 var padding = node.padding();
25487 var nodePos = node.position();
25488 var textHalign = node.pstyle('text-halign').strValue;
25489 var textValign = node.pstyle('text-valign').strValue;
25490 var rs = _p.rscratch;
25491 var rstyle = _p.rstyle;
25492
25493 switch (textHalign) {
25494 case 'left':
25495 textX = nodePos.x - nodeWidth / 2 - padding;
25496 break;
25497
25498 case 'right':
25499 textX = nodePos.x + nodeWidth / 2 + padding;
25500 break;
25501
25502 default:
25503 // e.g. center
25504 textX = nodePos.x;
25505 }
25506
25507 switch (textValign) {
25508 case 'top':
25509 textY = nodePos.y - nodeHeight / 2 - padding;
25510 break;
25511
25512 case 'bottom':
25513 textY = nodePos.y + nodeHeight / 2 + padding;
25514 break;
25515
25516 default:
25517 // e.g. middle
25518 textY = nodePos.y;
25519 }
25520
25521 rs.labelX = textX;
25522 rs.labelY = textY;
25523 rstyle.labelX = textX;
25524 rstyle.labelY = textY;
25525 this.calculateLabelAngles(node);
25526 this.applyLabelDimensions(node);
25527 };
25528
25529 var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
25530 var angle = Math.atan(dy / dx);
25531
25532 if (dx === 0 && angle < 0) {
25533 angle = angle * -1;
25534 }
25535
25536 return angle;
25537 };
25538
25539 var lineAngle = function lineAngle(p0, p1) {
25540 var dx = p1.x - p0.x;
25541 var dy = p1.y - p0.y;
25542 return lineAngleFromDelta(dx, dy);
25543 };
25544
25545 var bezierAngle = function bezierAngle(p0, p1, p2, t) {
25546 var t0 = bound(0, t - 0.001, 1);
25547 var t1 = bound(0, t + 0.001, 1);
25548 var lp0 = qbezierPtAt(p0, p1, p2, t0);
25549 var lp1 = qbezierPtAt(p0, p1, p2, t1);
25550 return lineAngle(lp0, lp1);
25551 };
25552
25553 BRp$9.recalculateEdgeLabelProjections = function (edge) {
25554 var p;
25555 var _p = edge._private;
25556 var rs = _p.rscratch;
25557 var r = this;
25558 var content = {
25559 mid: edge.pstyle('label').strValue,
25560 source: edge.pstyle('source-label').strValue,
25561 target: edge.pstyle('target-label').strValue
25562 };
25563
25564 if (content.mid || content.source || content.target) ; else {
25565 return; // no labels => no calcs
25566 } // add center point to style so bounding box calculations can use it
25567 //
25568
25569
25570 p = {
25571 x: rs.midX,
25572 y: rs.midY
25573 };
25574
25575 var setRs = function setRs(propName, prefix, value) {
25576 setPrefixedProperty(_p.rscratch, propName, prefix, value);
25577 setPrefixedProperty(_p.rstyle, propName, prefix, value);
25578 };
25579
25580 setRs('labelX', null, p.x);
25581 setRs('labelY', null, p.y);
25582 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
25583 setRs('labelAutoAngle', null, midAngle);
25584
25585 var createControlPointInfo = function createControlPointInfo() {
25586 if (createControlPointInfo.cache) {
25587 return createControlPointInfo.cache;
25588 } // use cache so only 1x per edge
25589
25590
25591 var ctrlpts = []; // store each ctrlpt info init
25592
25593 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
25594 var p0 = {
25595 x: rs.allpts[i],
25596 y: rs.allpts[i + 1]
25597 };
25598 var p1 = {
25599 x: rs.allpts[i + 2],
25600 y: rs.allpts[i + 3]
25601 }; // ctrlpt
25602
25603 var p2 = {
25604 x: rs.allpts[i + 4],
25605 y: rs.allpts[i + 5]
25606 };
25607 ctrlpts.push({
25608 p0: p0,
25609 p1: p1,
25610 p2: p2,
25611 startDist: 0,
25612 length: 0,
25613 segments: []
25614 });
25615 }
25616
25617 var bpts = _p.rstyle.bezierPts;
25618 var nProjs = r.bezierProjPcts.length;
25619
25620 function addSegment(cp, p0, p1, t0, t1) {
25621 var length = dist(p0, p1);
25622 var prevSegment = cp.segments[cp.segments.length - 1];
25623 var segment = {
25624 p0: p0,
25625 p1: p1,
25626 t0: t0,
25627 t1: t1,
25628 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
25629 length: length
25630 };
25631 cp.segments.push(segment);
25632 cp.length += length;
25633 } // update each ctrlpt with segment info
25634
25635
25636 for (var _i = 0; _i < ctrlpts.length; _i++) {
25637 var cp = ctrlpts[_i];
25638 var prevCp = ctrlpts[_i - 1];
25639
25640 if (prevCp) {
25641 cp.startDist = prevCp.startDist + prevCp.length;
25642 }
25643
25644 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
25645
25646 for (var j = 0; j < nProjs - 1; j++) {
25647 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
25648 }
25649
25650 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
25651 }
25652
25653 return createControlPointInfo.cache = ctrlpts;
25654 };
25655
25656 var calculateEndProjection = function calculateEndProjection(prefix) {
25657 var angle;
25658 var isSrc = prefix === 'source';
25659
25660 if (!content[prefix]) {
25661 return;
25662 }
25663
25664 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
25665
25666 switch (rs.edgeType) {
25667 case 'self':
25668 case 'compound':
25669 case 'bezier':
25670 case 'multibezier':
25671 {
25672 var cps = createControlPointInfo();
25673 var selected;
25674 var startDist = 0;
25675 var totalDist = 0; // find the segment we're on
25676
25677 for (var i = 0; i < cps.length; i++) {
25678 var _cp = cps[isSrc ? i : cps.length - 1 - i];
25679
25680 for (var j = 0; j < _cp.segments.length; j++) {
25681 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
25682 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
25683 startDist = totalDist;
25684 totalDist += _seg.length;
25685
25686 if (totalDist >= offset || lastSeg) {
25687 selected = {
25688 cp: _cp,
25689 segment: _seg
25690 };
25691 break;
25692 }
25693 }
25694
25695 if (selected) {
25696 break;
25697 }
25698 }
25699
25700 var cp = selected.cp;
25701 var seg = selected.segment;
25702 var tSegment = (offset - startDist) / seg.length;
25703 var segDt = seg.t1 - seg.t0;
25704 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
25705 t = bound(0, t, 1);
25706 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
25707 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
25708 break;
25709 }
25710
25711 case 'straight':
25712 case 'segments':
25713 case 'haystack':
25714 {
25715 var d = 0,
25716 di,
25717 d0;
25718 var p0, p1;
25719 var l = rs.allpts.length;
25720
25721 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
25722 if (isSrc) {
25723 p0 = {
25724 x: rs.allpts[_i2],
25725 y: rs.allpts[_i2 + 1]
25726 };
25727 p1 = {
25728 x: rs.allpts[_i2 + 2],
25729 y: rs.allpts[_i2 + 3]
25730 };
25731 } else {
25732 p0 = {
25733 x: rs.allpts[l - 2 - _i2],
25734 y: rs.allpts[l - 1 - _i2]
25735 };
25736 p1 = {
25737 x: rs.allpts[l - 4 - _i2],
25738 y: rs.allpts[l - 3 - _i2]
25739 };
25740 }
25741
25742 di = dist(p0, p1);
25743 d0 = d;
25744 d += di;
25745
25746 if (d >= offset) {
25747 break;
25748 }
25749 }
25750
25751 var pD = offset - d0;
25752
25753 var _t = pD / di;
25754
25755 _t = bound(0, _t, 1);
25756 p = lineAt(p0, p1, _t);
25757 angle = lineAngle(p0, p1);
25758 break;
25759 }
25760 }
25761
25762 setRs('labelX', prefix, p.x);
25763 setRs('labelY', prefix, p.y);
25764 setRs('labelAutoAngle', prefix, angle);
25765 };
25766
25767 calculateEndProjection('source');
25768 calculateEndProjection('target');
25769 this.applyLabelDimensions(edge);
25770 };
25771
25772 BRp$9.applyLabelDimensions = function (ele) {
25773 this.applyPrefixedLabelDimensions(ele);
25774
25775 if (ele.isEdge()) {
25776 this.applyPrefixedLabelDimensions(ele, 'source');
25777 this.applyPrefixedLabelDimensions(ele, 'target');
25778 }
25779 };
25780
25781 BRp$9.applyPrefixedLabelDimensions = function (ele, prefix) {
25782 var _p = ele._private;
25783 var text = this.getLabelText(ele, prefix);
25784 var labelDims = this.calculateLabelDimensions(ele, text);
25785 var lineHeight = ele.pstyle('line-height').pfValue;
25786 var textWrap = ele.pstyle('text-wrap').strValue;
25787 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
25788 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
25789 var normPerLineHeight = labelDims.height / numLines;
25790 var labelLineHeight = normPerLineHeight * lineHeight;
25791 var width = labelDims.width;
25792 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
25793 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
25794 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
25795 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
25796 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
25797 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
25798 };
25799
25800 BRp$9.getLabelText = function (ele, prefix) {
25801 var _p = ele._private;
25802 var pfd = prefix ? prefix + '-' : '';
25803 var text = ele.pstyle(pfd + 'label').strValue;
25804 var textTransform = ele.pstyle('text-transform').value;
25805
25806 var rscratch = function rscratch(propName, value) {
25807 if (value) {
25808 setPrefixedProperty(_p.rscratch, propName, prefix, value);
25809 return value;
25810 } else {
25811 return getPrefixedProperty(_p.rscratch, propName, prefix);
25812 }
25813 }; // for empty text, skip all processing
25814
25815
25816 if (!text) {
25817 return '';
25818 }
25819
25820 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
25821 text = text.toUpperCase();
25822 } else if (textTransform == 'lowercase') {
25823 text = text.toLowerCase();
25824 }
25825
25826 var wrapStyle = ele.pstyle('text-wrap').value;
25827
25828 if (wrapStyle === 'wrap') {
25829 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
25830
25831 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
25832 return rscratch('labelWrapCachedText');
25833 }
25834
25835 var zwsp = "\u200B";
25836 var lines = text.split('\n');
25837 var maxW = ele.pstyle('text-max-width').pfValue;
25838 var overflow = ele.pstyle('text-overflow-wrap').value;
25839 var overflowAny = overflow === 'anywhere';
25840 var wrappedLines = [];
25841 var wordsRegex = /[\s\u200b]+/;
25842 var wordSeparator = overflowAny ? '' : ' ';
25843
25844 for (var l = 0; l < lines.length; l++) {
25845 var line = lines[l];
25846 var lineDims = this.calculateLabelDimensions(ele, line);
25847 var lineW = lineDims.width;
25848
25849 if (overflowAny) {
25850 var processedLine = line.split('').join(zwsp);
25851 line = processedLine;
25852 }
25853
25854 if (lineW > maxW) {
25855 // line is too long
25856 var words = line.split(wordsRegex);
25857 var subline = '';
25858
25859 for (var w = 0; w < words.length; w++) {
25860 var word = words[w];
25861 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
25862 var testDims = this.calculateLabelDimensions(ele, testLine);
25863 var testW = testDims.width;
25864
25865 if (testW <= maxW) {
25866 // word fits on current line
25867 subline += word + wordSeparator;
25868 } else {
25869 // word starts new line
25870 if (subline) {
25871 wrappedLines.push(subline);
25872 }
25873
25874 subline = word + wordSeparator;
25875 }
25876 } // if there's remaining text, put it in a wrapped line
25877
25878
25879 if (!subline.match(/^[\s\u200b]+$/)) {
25880 wrappedLines.push(subline);
25881 }
25882 } else {
25883 // line is already short enough
25884 wrappedLines.push(line);
25885 }
25886 } // for
25887
25888
25889 rscratch('labelWrapCachedLines', wrappedLines);
25890 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
25891 rscratch('labelWrapKey', labelKey);
25892 } else if (wrapStyle === 'ellipsis') {
25893 var _maxW = ele.pstyle('text-max-width').pfValue;
25894 var ellipsized = '';
25895 var ellipsis = "\u2026";
25896 var incLastCh = false;
25897
25898 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
25899 // the label already fits
25900 return text;
25901 }
25902
25903 for (var i = 0; i < text.length; i++) {
25904 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
25905
25906 if (widthWithNextCh > _maxW) {
25907 break;
25908 }
25909
25910 ellipsized += text[i];
25911
25912 if (i === text.length - 1) {
25913 incLastCh = true;
25914 }
25915 }
25916
25917 if (!incLastCh) {
25918 ellipsized += ellipsis;
25919 }
25920
25921 return ellipsized;
25922 } // if ellipsize
25923
25924
25925 return text;
25926 };
25927
25928 BRp$9.getLabelJustification = function (ele) {
25929 var justification = ele.pstyle('text-justification').strValue;
25930 var textHalign = ele.pstyle('text-halign').strValue;
25931
25932 if (justification === 'auto') {
25933 if (ele.isNode()) {
25934 switch (textHalign) {
25935 case 'left':
25936 return 'right';
25937
25938 case 'right':
25939 return 'left';
25940
25941 default:
25942 return 'center';
25943 }
25944 } else {
25945 return 'center';
25946 }
25947 } else {
25948 return justification;
25949 }
25950 };
25951
25952 BRp$9.calculateLabelDimensions = function (ele, text) {
25953 var r = this;
25954 var cacheKey = hashString(text, ele._private.labelDimsKey);
25955 var cache = r.labelDimCache || (r.labelDimCache = []);
25956 var existingVal = cache[cacheKey];
25957
25958 if (existingVal != null) {
25959 return existingVal;
25960 }
25961
25962 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
25963
25964 var fStyle = ele.pstyle('font-style').strValue;
25965 var size = ele.pstyle('font-size').pfValue;
25966 var family = ele.pstyle('font-family').strValue;
25967 var weight = ele.pstyle('font-weight').strValue;
25968 var canvas = this.labelCalcCanvas;
25969 var c2d = this.labelCalcCanvasContext;
25970
25971 if (!canvas) {
25972 canvas = this.labelCalcCanvas = document.createElement('canvas');
25973 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
25974 var ds = canvas.style;
25975 ds.position = 'absolute';
25976 ds.left = '-9999px';
25977 ds.top = '-9999px';
25978 ds.zIndex = '-1';
25979 ds.visibility = 'hidden';
25980 ds.pointerEvents = 'none';
25981 }
25982
25983 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
25984 var width = 0;
25985 var height = 0;
25986 var lines = text.split('\n');
25987
25988 for (var i = 0; i < lines.length; i++) {
25989 var line = lines[i];
25990 var metrics = c2d.measureText(line);
25991 var w = Math.ceil(metrics.width);
25992 var h = size;
25993 width = Math.max(w, width);
25994 height += h;
25995 }
25996
25997 width += padding;
25998 height += padding;
25999 return cache[cacheKey] = {
26000 width: width,
26001 height: height
26002 };
26003 };
26004
26005 BRp$9.calculateLabelAngle = function (ele, prefix) {
26006 var _p = ele._private;
26007 var rs = _p.rscratch;
26008 var isEdge = ele.isEdge();
26009 var prefixDash = prefix ? prefix + '-' : '';
26010 var rot = ele.pstyle(prefixDash + 'text-rotation');
26011 var rotStr = rot.strValue;
26012
26013 if (rotStr === 'none') {
26014 return 0;
26015 } else if (isEdge && rotStr === 'autorotate') {
26016 return rs.labelAutoAngle;
26017 } else if (rotStr === 'autorotate') {
26018 return 0;
26019 } else {
26020 return rot.pfValue;
26021 }
26022 };
26023
26024 BRp$9.calculateLabelAngles = function (ele) {
26025 var r = this;
26026 var isEdge = ele.isEdge();
26027 var _p = ele._private;
26028 var rs = _p.rscratch;
26029 rs.labelAngle = r.calculateLabelAngle(ele);
26030
26031 if (isEdge) {
26032 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
26033 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
26034 }
26035 };
26036
26037 var BRp$8 = {};
26038 var TOO_SMALL_CUT_RECT = 28;
26039 var warnedCutRect = false;
26040
26041 BRp$8.getNodeShape = function (node) {
26042 var r = this;
26043 var shape = node.pstyle('shape').value;
26044
26045 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
26046 if (!warnedCutRect) {
26047 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
26048 warnedCutRect = true;
26049 }
26050
26051 return 'rectangle';
26052 }
26053
26054 if (node.isParent()) {
26055 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
26056 return shape;
26057 } else {
26058 return 'rectangle';
26059 }
26060 }
26061
26062 if (shape === 'polygon') {
26063 var points = node.pstyle('shape-polygon-points').value;
26064 return r.nodeShapes.makePolygon(points).name;
26065 }
26066
26067 return shape;
26068 };
26069
26070 var BRp$7 = {};
26071
26072 BRp$7.registerCalculationListeners = function () {
26073 var cy = this.cy;
26074 var elesToUpdate = cy.collection();
26075 var r = this;
26076
26077 var enqueue = function enqueue(eles) {
26078 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
26079 elesToUpdate.merge(eles);
26080
26081 if (dirtyStyleCaches) {
26082 for (var i = 0; i < eles.length; i++) {
26083 var ele = eles[i];
26084 var _p = ele._private;
26085 var rstyle = _p.rstyle;
26086 rstyle.clean = false;
26087 rstyle.cleanConnected = false;
26088 }
26089 }
26090 };
26091
26092 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
26093 var ele = e.target;
26094 enqueue(ele);
26095 }).on('style.* background.*', function onDirtyStyle(e) {
26096 var ele = e.target;
26097 enqueue(ele, false);
26098 });
26099
26100 var updateEleCalcs = function updateEleCalcs(willDraw) {
26101 if (willDraw) {
26102 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
26103 // before calculating rendered style (and pstyle might not be called yet)
26104
26105 elesToUpdate.cleanStyle();
26106
26107 for (var i = 0; i < elesToUpdate.length; i++) {
26108 var ele = elesToUpdate[i];
26109 var rstyle = ele._private.rstyle;
26110
26111 if (ele.isNode() && !rstyle.cleanConnected) {
26112 enqueue(ele.connectedEdges());
26113 rstyle.cleanConnected = true;
26114 }
26115 }
26116
26117 if (fns) {
26118 for (var _i = 0; _i < fns.length; _i++) {
26119 var fn = fns[_i];
26120 fn(willDraw, elesToUpdate);
26121 }
26122 }
26123
26124 r.recalculateRenderedStyle(elesToUpdate);
26125 elesToUpdate = cy.collection();
26126 }
26127 };
26128
26129 r.flushRenderedStyleQueue = function () {
26130 updateEleCalcs(true);
26131 };
26132
26133 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
26134 };
26135
26136 BRp$7.onUpdateEleCalcs = function (fn) {
26137 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
26138 fns.push(fn);
26139 };
26140
26141 BRp$7.recalculateRenderedStyle = function (eles, useCache) {
26142 var isCleanConnected = function isCleanConnected(ele) {
26143 return ele._private.rstyle.cleanConnected;
26144 };
26145
26146 var edges = [];
26147 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
26148
26149 if (this.destroyed) {
26150 return;
26151 } // use cache by default for perf
26152
26153
26154 if (useCache === undefined) {
26155 useCache = true;
26156 }
26157
26158 for (var i = 0; i < eles.length; i++) {
26159 var ele = eles[i];
26160 var _p = ele._private;
26161 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
26162 // (and a request for recalc may come in between frames)
26163
26164 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
26165 rstyle.clean = false;
26166 } // only update if dirty and in graph
26167
26168
26169 if (useCache && rstyle.clean || ele.removed()) {
26170 continue;
26171 } // only update if not display: none
26172
26173
26174 if (ele.pstyle('display').value === 'none') {
26175 continue;
26176 }
26177
26178 if (_p.group === 'nodes') {
26179 nodes.push(ele);
26180 } else {
26181 // edges
26182 edges.push(ele);
26183 }
26184
26185 rstyle.clean = true;
26186 } // update node data from projections
26187
26188
26189 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
26190 var _ele = nodes[_i2];
26191 var _p2 = _ele._private;
26192 var _rstyle = _p2.rstyle;
26193
26194 var pos = _ele.position();
26195
26196 this.recalculateNodeLabelProjection(_ele);
26197 _rstyle.nodeX = pos.x;
26198 _rstyle.nodeY = pos.y;
26199 _rstyle.nodeW = _ele.pstyle('width').pfValue;
26200 _rstyle.nodeH = _ele.pstyle('height').pfValue;
26201 }
26202
26203 this.recalculateEdgeProjections(edges); // update edge data from projections
26204
26205 for (var _i3 = 0; _i3 < edges.length; _i3++) {
26206 var _ele2 = edges[_i3];
26207 var _p3 = _ele2._private;
26208 var _rstyle2 = _p3.rstyle;
26209 var rs = _p3.rscratch; // update rstyle positions
26210
26211 _rstyle2.srcX = rs.arrowStartX;
26212 _rstyle2.srcY = rs.arrowStartY;
26213 _rstyle2.tgtX = rs.arrowEndX;
26214 _rstyle2.tgtY = rs.arrowEndY;
26215 _rstyle2.midX = rs.midX;
26216 _rstyle2.midY = rs.midY;
26217 _rstyle2.labelAngle = rs.labelAngle;
26218 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
26219 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
26220 }
26221 };
26222
26223 var BRp$6 = {};
26224
26225 BRp$6.updateCachedGrabbedEles = function () {
26226 var eles = this.cachedZSortedEles;
26227
26228 if (!eles) {
26229 // just let this be recalculated on the next z sort tick
26230 return;
26231 }
26232
26233 eles.drag = [];
26234 eles.nondrag = [];
26235 var grabTargets = [];
26236
26237 for (var i = 0; i < eles.length; i++) {
26238 var ele = eles[i];
26239 var rs = ele._private.rscratch;
26240
26241 if (ele.grabbed() && !ele.isParent()) {
26242 grabTargets.push(ele);
26243 } else if (rs.inDragLayer) {
26244 eles.drag.push(ele);
26245 } else {
26246 eles.nondrag.push(ele);
26247 }
26248 } // put the grab target nodes last so it's on top of its neighbourhood
26249
26250
26251 for (var i = 0; i < grabTargets.length; i++) {
26252 var ele = grabTargets[i];
26253 eles.drag.push(ele);
26254 }
26255 };
26256
26257 BRp$6.invalidateCachedZSortedEles = function () {
26258 this.cachedZSortedEles = null;
26259 };
26260
26261 BRp$6.getCachedZSortedEles = function (forceRecalc) {
26262 if (forceRecalc || !this.cachedZSortedEles) {
26263 var eles = this.cy.mutableElements().toArray();
26264 eles.sort(zIndexSort);
26265 eles.interactive = eles.filter(function (ele) {
26266 return ele.interactive();
26267 });
26268 this.cachedZSortedEles = eles;
26269 this.updateCachedGrabbedEles();
26270 } else {
26271 eles = this.cachedZSortedEles;
26272 }
26273
26274 return eles;
26275 };
26276
26277 var BRp$5 = {};
26278 [BRp$e, BRp$d, BRp$c, BRp$b, BRp$a, BRp$9, BRp$8, BRp$7, BRp$6].forEach(function (props) {
26279 extend(BRp$5, props);
26280 });
26281
26282 var BRp$4 = {};
26283
26284 BRp$4.getCachedImage = function (url, crossOrigin, onLoad) {
26285 var r = this;
26286 var imageCache = r.imageCache = r.imageCache || {};
26287 var cache = imageCache[url];
26288
26289 if (cache) {
26290 if (!cache.image.complete) {
26291 cache.image.addEventListener('load', onLoad);
26292 }
26293
26294 return cache.image;
26295 } else {
26296 cache = imageCache[url] = imageCache[url] || {};
26297 var image = cache.image = new Image(); // eslint-disable-line no-undef
26298
26299 image.addEventListener('load', onLoad);
26300 image.addEventListener('error', function () {
26301 image.error = true;
26302 }); // #1582 safari doesn't load data uris with crossOrigin properly
26303 // https://bugs.webkit.org/show_bug.cgi?id=123978
26304
26305 var dataUriPrefix = 'data:';
26306 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
26307
26308 if (!isDataUri) {
26309 image.crossOrigin = crossOrigin; // prevent tainted canvas
26310 }
26311
26312 image.src = url;
26313 return image;
26314 }
26315 };
26316
26317 var BRp$3 = {};
26318 /* global document, window, ResizeObserver, MutationObserver */
26319
26320 BRp$3.registerBinding = function (target, event, handler, useCapture) {
26321 // eslint-disable-line no-unused-vars
26322 var args = Array.prototype.slice.apply(arguments, [1]); // copy
26323
26324 var b = this.binder(target);
26325 return b.on.apply(b, args);
26326 };
26327
26328 BRp$3.binder = function (tgt) {
26329 var r = this;
26330 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
26331
26332 if (r.supportsPassiveEvents == null) {
26333 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
26334 var supportsPassive = false;
26335
26336 try {
26337 var opts = Object.defineProperty({}, 'passive', {
26338 get: function get() {
26339 supportsPassive = true;
26340 return true;
26341 }
26342 });
26343 window.addEventListener('test', null, opts);
26344 } catch (err) {// not supported
26345 }
26346
26347 r.supportsPassiveEvents = supportsPassive;
26348 }
26349
26350 var on = function on(event, handler, useCapture) {
26351 var args = Array.prototype.slice.call(arguments);
26352
26353 if (tgtIsDom && r.supportsPassiveEvents) {
26354 // replace useCapture w/ opts obj
26355 args[2] = {
26356 capture: useCapture != null ? useCapture : false,
26357 passive: false,
26358 once: false
26359 };
26360 }
26361
26362 r.bindings.push({
26363 target: tgt,
26364 args: args
26365 });
26366 (tgt.addEventListener || tgt.on).apply(tgt, args);
26367 return this;
26368 };
26369
26370 return {
26371 on: on,
26372 addEventListener: on,
26373 addListener: on,
26374 bind: on
26375 };
26376 };
26377
26378 BRp$3.nodeIsDraggable = function (node) {
26379 return node && node.isNode() && !node.locked() && node.grabbable();
26380 };
26381
26382 BRp$3.nodeIsGrabbable = function (node) {
26383 return this.nodeIsDraggable(node) && node.interactive();
26384 };
26385
26386 BRp$3.load = function () {
26387 var r = this;
26388
26389 var isSelected = function isSelected(ele) {
26390 return ele.selected();
26391 };
26392
26393 var triggerEvents = function triggerEvents(target, names, e, position) {
26394 if (target == null) {
26395 target = r.cy;
26396 }
26397
26398 for (var i = 0; i < names.length; i++) {
26399 var name = names[i];
26400 target.emit({
26401 originalEvent: e,
26402 type: name,
26403 position: position
26404 });
26405 }
26406 };
26407
26408 var isMultSelKeyDown = function isMultSelKeyDown(e) {
26409 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
26410 };
26411
26412 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
26413 var allowPassthrough = true;
26414
26415 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
26416 // a grabbable compound node below the ele => no passthrough panning
26417 for (var i = 0; downs && i < downs.length; i++) {
26418 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
26419
26420 if (down.isNode() && down.isParent() && !down.pannable()) {
26421 allowPassthrough = false;
26422 break;
26423 }
26424 }
26425 } else {
26426 allowPassthrough = true;
26427 }
26428
26429 return allowPassthrough;
26430 };
26431
26432 var setGrabbed = function setGrabbed(ele) {
26433 ele[0]._private.grabbed = true;
26434 };
26435
26436 var setFreed = function setFreed(ele) {
26437 ele[0]._private.grabbed = false;
26438 };
26439
26440 var setInDragLayer = function setInDragLayer(ele) {
26441 ele[0]._private.rscratch.inDragLayer = true;
26442 };
26443
26444 var setOutDragLayer = function setOutDragLayer(ele) {
26445 ele[0]._private.rscratch.inDragLayer = false;
26446 };
26447
26448 var setGrabTarget = function setGrabTarget(ele) {
26449 ele[0]._private.rscratch.isGrabTarget = true;
26450 };
26451
26452 var removeGrabTarget = function removeGrabTarget(ele) {
26453 ele[0]._private.rscratch.isGrabTarget = false;
26454 };
26455
26456 var addToDragList = function addToDragList(ele, opts) {
26457 var list = opts.addToList;
26458 var listHasEle = list.has(ele);
26459
26460 if (!listHasEle && ele.grabbable() && !ele.locked()) {
26461 list.merge(ele);
26462 setGrabbed(ele);
26463 }
26464 }; // helper function to determine which child nodes and inner edges
26465 // of a compound node to be dragged as well as the grabbed and selected nodes
26466
26467
26468 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
26469 if (!node.cy().hasCompoundNodes()) {
26470 return;
26471 }
26472
26473 if (opts.inDragLayer == null && opts.addToList == null) {
26474 return;
26475 } // nothing to do
26476
26477
26478 var innerNodes = node.descendants();
26479
26480 if (opts.inDragLayer) {
26481 innerNodes.forEach(setInDragLayer);
26482 innerNodes.connectedEdges().forEach(setInDragLayer);
26483 }
26484
26485 if (opts.addToList) {
26486 addToDragList(innerNodes, opts);
26487 }
26488 }; // adds the given nodes and its neighbourhood to the drag layer
26489
26490
26491 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
26492 opts = opts || {};
26493 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
26494
26495 if (opts.inDragLayer) {
26496 nodes.forEach(setInDragLayer);
26497 nodes.neighborhood().stdFilter(function (ele) {
26498 return !hasCompoundNodes || ele.isEdge();
26499 }).forEach(setInDragLayer);
26500 }
26501
26502 if (opts.addToList) {
26503 nodes.forEach(function (ele) {
26504 addToDragList(ele, opts);
26505 });
26506 }
26507
26508 addDescendantsToDrag(nodes, opts); // always add to drag
26509 // also add nodes and edges related to the topmost ancestor
26510
26511 updateAncestorsInDragLayer(nodes, {
26512 inDragLayer: opts.inDragLayer
26513 });
26514 r.updateCachedGrabbedEles();
26515 };
26516
26517 var addNodeToDrag = addNodesToDrag;
26518
26519 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
26520 if (!grabbedEles) {
26521 return;
26522 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
26523
26524
26525 r.getCachedZSortedEles().forEach(function (ele) {
26526 setFreed(ele);
26527 setOutDragLayer(ele);
26528 removeGrabTarget(ele);
26529 });
26530 r.updateCachedGrabbedEles();
26531 }; // helper function to determine which ancestor nodes and edges should go
26532 // to the drag layer (or should be removed from drag layer).
26533
26534
26535 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
26536 if (opts.inDragLayer == null && opts.addToList == null) {
26537 return;
26538 } // nothing to do
26539
26540
26541 if (!node.cy().hasCompoundNodes()) {
26542 return;
26543 } // find top-level parent
26544
26545
26546 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
26547
26548 if (parent.same(node)) {
26549 return;
26550 }
26551
26552 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
26553 var edges = nodes.connectedEdges();
26554
26555 if (opts.inDragLayer) {
26556 edges.forEach(setInDragLayer);
26557 nodes.forEach(setInDragLayer);
26558 }
26559
26560 if (opts.addToList) {
26561 nodes.forEach(function (ele) {
26562 addToDragList(ele, opts);
26563 });
26564 }
26565 };
26566
26567 var blurActiveDomElement = function blurActiveDomElement() {
26568 if (document.activeElement != null && document.activeElement.blur != null) {
26569 document.activeElement.blur();
26570 }
26571 };
26572
26573 var haveMutationsApi = typeof MutationObserver !== 'undefined';
26574 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
26575
26576 if (haveMutationsApi) {
26577 r.removeObserver = new MutationObserver(function (mutns) {
26578 // eslint-disable-line no-undef
26579 for (var i = 0; i < mutns.length; i++) {
26580 var mutn = mutns[i];
26581 var rNodes = mutn.removedNodes;
26582
26583 if (rNodes) {
26584 for (var j = 0; j < rNodes.length; j++) {
26585 var rNode = rNodes[j];
26586
26587 if (rNode === r.container) {
26588 r.destroy();
26589 break;
26590 }
26591 }
26592 }
26593 }
26594 });
26595
26596 if (r.container.parentNode) {
26597 r.removeObserver.observe(r.container.parentNode, {
26598 childList: true
26599 });
26600 }
26601 } else {
26602 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
26603 // eslint-disable-line no-unused-vars
26604 r.destroy();
26605 });
26606 }
26607
26608 var onResize = debounce_1(function () {
26609 r.cy.resize();
26610 }, 100);
26611
26612 if (haveMutationsApi) {
26613 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
26614
26615 r.styleObserver.observe(r.container, {
26616 attributes: true
26617 });
26618 } // auto resize
26619
26620
26621 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
26622
26623 if (haveResizeObserverApi) {
26624 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
26625
26626 r.resizeObserver.observe(r.container);
26627 }
26628
26629 var forEachUp = function forEachUp(domEle, fn) {
26630 while (domEle != null) {
26631 fn(domEle);
26632 domEle = domEle.parentNode;
26633 }
26634 };
26635
26636 var invalidateCoords = function invalidateCoords() {
26637 r.invalidateContainerClientCoordsCache();
26638 };
26639
26640 forEachUp(r.container, function (domEle) {
26641 r.registerBinding(domEle, 'transitionend', invalidateCoords);
26642 r.registerBinding(domEle, 'animationend', invalidateCoords);
26643 r.registerBinding(domEle, 'scroll', invalidateCoords);
26644 }); // stop right click menu from appearing on cy
26645
26646 r.registerBinding(r.container, 'contextmenu', function (e) {
26647 e.preventDefault();
26648 });
26649
26650 var inBoxSelection = function inBoxSelection() {
26651 return r.selection[4] !== 0;
26652 };
26653
26654 var eventInContainer = function eventInContainer(e) {
26655 // save cycles if mouse events aren't to be captured
26656 var containerPageCoords = r.findContainerClientCoords();
26657 var x = containerPageCoords[0];
26658 var y = containerPageCoords[1];
26659 var width = containerPageCoords[2];
26660 var height = containerPageCoords[3];
26661 var positions = e.touches ? e.touches : [e];
26662 var atLeastOnePosInside = false;
26663
26664 for (var i = 0; i < positions.length; i++) {
26665 var p = positions[i];
26666
26667 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
26668 atLeastOnePosInside = true;
26669 break;
26670 }
26671 }
26672
26673 if (!atLeastOnePosInside) {
26674 return false;
26675 }
26676
26677 var container = r.container;
26678 var target = e.target;
26679 var tParent = target.parentNode;
26680 var containerIsTarget = false;
26681
26682 while (tParent) {
26683 if (tParent === container) {
26684 containerIsTarget = true;
26685 break;
26686 }
26687
26688 tParent = tParent.parentNode;
26689 }
26690
26691 if (!containerIsTarget) {
26692 return false;
26693 } // if target is outisde cy container, then this event is not for us
26694
26695
26696 return true;
26697 }; // Primary key
26698
26699
26700 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
26701 if (!eventInContainer(e)) {
26702 return;
26703 }
26704
26705 e.preventDefault();
26706 blurActiveDomElement();
26707 r.hoverData.capture = true;
26708 r.hoverData.which = e.which;
26709 var cy = r.cy;
26710 var gpos = [e.clientX, e.clientY];
26711 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
26712 var select = r.selection;
26713 var nears = r.findNearestElements(pos[0], pos[1], true, false);
26714 var near = nears[0];
26715 var draggedElements = r.dragData.possibleDragElements;
26716 r.hoverData.mdownPos = pos;
26717 r.hoverData.mdownGPos = gpos;
26718
26719 var checkForTaphold = function checkForTaphold() {
26720 r.hoverData.tapholdCancelled = false;
26721 clearTimeout(r.hoverData.tapholdTimeout);
26722 r.hoverData.tapholdTimeout = setTimeout(function () {
26723 if (r.hoverData.tapholdCancelled) {
26724 return;
26725 } else {
26726 var ele = r.hoverData.down;
26727
26728 if (ele) {
26729 ele.emit({
26730 originalEvent: e,
26731 type: 'taphold',
26732 position: {
26733 x: pos[0],
26734 y: pos[1]
26735 }
26736 });
26737 } else {
26738 cy.emit({
26739 originalEvent: e,
26740 type: 'taphold',
26741 position: {
26742 x: pos[0],
26743 y: pos[1]
26744 }
26745 });
26746 }
26747 }
26748 }, r.tapholdDuration);
26749 }; // Right click button
26750
26751
26752 if (e.which == 3) {
26753 r.hoverData.cxtStarted = true;
26754 var cxtEvt = {
26755 originalEvent: e,
26756 type: 'cxttapstart',
26757 position: {
26758 x: pos[0],
26759 y: pos[1]
26760 }
26761 };
26762
26763 if (near) {
26764 near.activate();
26765 near.emit(cxtEvt);
26766 r.hoverData.down = near;
26767 } else {
26768 cy.emit(cxtEvt);
26769 }
26770
26771 r.hoverData.downTime = new Date().getTime();
26772 r.hoverData.cxtDragged = false; // Primary button
26773 } else if (e.which == 1) {
26774 if (near) {
26775 near.activate();
26776 } // Element dragging
26777
26778
26779 {
26780 // If something is under the cursor and it is draggable, prepare to grab it
26781 if (near != null) {
26782 if (r.nodeIsGrabbable(near)) {
26783 var makeEvent = function makeEvent(type) {
26784 return {
26785 originalEvent: e,
26786 type: type,
26787 position: {
26788 x: pos[0],
26789 y: pos[1]
26790 }
26791 };
26792 };
26793
26794 var triggerGrab = function triggerGrab(ele) {
26795 ele.emit(makeEvent('grab'));
26796 };
26797
26798 setGrabTarget(near);
26799
26800 if (!near.selected()) {
26801 draggedElements = r.dragData.possibleDragElements = cy.collection();
26802 addNodeToDrag(near, {
26803 addToList: draggedElements
26804 });
26805 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
26806 } else {
26807 draggedElements = r.dragData.possibleDragElements = cy.collection();
26808 var selectedNodes = cy.$(function (ele) {
26809 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
26810 });
26811 addNodesToDrag(selectedNodes, {
26812 addToList: draggedElements
26813 });
26814 near.emit(makeEvent('grabon'));
26815 selectedNodes.forEach(triggerGrab);
26816 }
26817
26818 r.redrawHint('eles', true);
26819 r.redrawHint('drag', true);
26820 }
26821 }
26822
26823 r.hoverData.down = near;
26824 r.hoverData.downs = nears;
26825 r.hoverData.downTime = new Date().getTime();
26826 }
26827 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
26828 x: pos[0],
26829 y: pos[1]
26830 });
26831
26832 if (near == null) {
26833 select[4] = 1;
26834 r.data.bgActivePosistion = {
26835 x: pos[0],
26836 y: pos[1]
26837 };
26838 r.redrawHint('select', true);
26839 r.redraw();
26840 } else if (near.pannable()) {
26841 select[4] = 1; // for future pan
26842 }
26843
26844 checkForTaphold();
26845 } // Initialize selection box coordinates
26846
26847
26848 select[0] = select[2] = pos[0];
26849 select[1] = select[3] = pos[1];
26850 }, false);
26851 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
26852 // eslint-disable-line no-undef
26853 var capture = r.hoverData.capture;
26854
26855 if (!capture && !eventInContainer(e)) {
26856 return;
26857 }
26858
26859 var preventDefault = false;
26860 var cy = r.cy;
26861 var zoom = cy.zoom();
26862 var gpos = [e.clientX, e.clientY];
26863 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
26864 var mdownPos = r.hoverData.mdownPos;
26865 var mdownGPos = r.hoverData.mdownGPos;
26866 var select = r.selection;
26867 var near = null;
26868
26869 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
26870 near = r.findNearestElement(pos[0], pos[1], true, false);
26871 }
26872
26873 var last = r.hoverData.last;
26874 var down = r.hoverData.down;
26875 var disp = [pos[0] - select[2], pos[1] - select[3]];
26876 var draggedElements = r.dragData.possibleDragElements;
26877 var isOverThresholdDrag;
26878
26879 if (mdownGPos) {
26880 var dx = gpos[0] - mdownGPos[0];
26881 var dx2 = dx * dx;
26882 var dy = gpos[1] - mdownGPos[1];
26883 var dy2 = dy * dy;
26884 var dist2 = dx2 + dy2;
26885 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
26886 }
26887
26888 var multSelKeyDown = isMultSelKeyDown(e);
26889
26890 if (isOverThresholdDrag) {
26891 r.hoverData.tapholdCancelled = true;
26892 }
26893
26894 var updateDragDelta = function updateDragDelta() {
26895 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
26896
26897 if (dragDelta.length === 0) {
26898 dragDelta.push(disp[0]);
26899 dragDelta.push(disp[1]);
26900 } else {
26901 dragDelta[0] += disp[0];
26902 dragDelta[1] += disp[1];
26903 }
26904 };
26905
26906 preventDefault = true;
26907 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
26908 x: pos[0],
26909 y: pos[1]
26910 });
26911
26912 var goIntoBoxMode = function goIntoBoxMode() {
26913 r.data.bgActivePosistion = undefined;
26914
26915 if (!r.hoverData.selecting) {
26916 cy.emit({
26917 originalEvent: e,
26918 type: 'boxstart',
26919 position: {
26920 x: pos[0],
26921 y: pos[1]
26922 }
26923 });
26924 }
26925
26926 select[4] = 1;
26927 r.hoverData.selecting = true;
26928 r.redrawHint('select', true);
26929 r.redraw();
26930 }; // trigger context drag if rmouse down
26931
26932
26933 if (r.hoverData.which === 3) {
26934 // but only if over threshold
26935 if (isOverThresholdDrag) {
26936 var cxtEvt = {
26937 originalEvent: e,
26938 type: 'cxtdrag',
26939 position: {
26940 x: pos[0],
26941 y: pos[1]
26942 }
26943 };
26944
26945 if (down) {
26946 down.emit(cxtEvt);
26947 } else {
26948 cy.emit(cxtEvt);
26949 }
26950
26951 r.hoverData.cxtDragged = true;
26952
26953 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
26954 if (r.hoverData.cxtOver) {
26955 r.hoverData.cxtOver.emit({
26956 originalEvent: e,
26957 type: 'cxtdragout',
26958 position: {
26959 x: pos[0],
26960 y: pos[1]
26961 }
26962 });
26963 }
26964
26965 r.hoverData.cxtOver = near;
26966
26967 if (near) {
26968 near.emit({
26969 originalEvent: e,
26970 type: 'cxtdragover',
26971 position: {
26972 x: pos[0],
26973 y: pos[1]
26974 }
26975 });
26976 }
26977 }
26978 } // Check if we are drag panning the entire graph
26979
26980 } else if (r.hoverData.dragging) {
26981 preventDefault = true;
26982
26983 if (cy.panningEnabled() && cy.userPanningEnabled()) {
26984 var deltaP;
26985
26986 if (r.hoverData.justStartedPan) {
26987 var mdPos = r.hoverData.mdownPos;
26988 deltaP = {
26989 x: (pos[0] - mdPos[0]) * zoom,
26990 y: (pos[1] - mdPos[1]) * zoom
26991 };
26992 r.hoverData.justStartedPan = false;
26993 } else {
26994 deltaP = {
26995 x: disp[0] * zoom,
26996 y: disp[1] * zoom
26997 };
26998 }
26999
27000 cy.panBy(deltaP);
27001 cy.emit('dragpan');
27002 r.hoverData.dragged = true;
27003 } // Needs reproject due to pan changing viewport
27004
27005
27006 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
27007 } else if (select[4] == 1 && (down == null || down.pannable())) {
27008 if (isOverThresholdDrag) {
27009 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
27010 goIntoBoxMode();
27011 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
27012 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
27013
27014 if (allowPassthrough) {
27015 r.hoverData.dragging = true;
27016 r.hoverData.justStartedPan = true;
27017 select[4] = 0;
27018 r.data.bgActivePosistion = array2point(mdownPos);
27019 r.redrawHint('select', true);
27020 r.redraw();
27021 }
27022 }
27023
27024 if (down && down.pannable() && down.active()) {
27025 down.unactivate();
27026 }
27027 }
27028 } else {
27029 if (down && down.pannable() && down.active()) {
27030 down.unactivate();
27031 }
27032
27033 if ((!down || !down.grabbed()) && near != last) {
27034 if (last) {
27035 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
27036 x: pos[0],
27037 y: pos[1]
27038 });
27039 }
27040
27041 if (near) {
27042 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
27043 x: pos[0],
27044 y: pos[1]
27045 });
27046 }
27047
27048 r.hoverData.last = near;
27049 }
27050
27051 if (down) {
27052 if (isOverThresholdDrag) {
27053 // then we can take action
27054 if (cy.boxSelectionEnabled() && multSelKeyDown) {
27055 // then selection overrides
27056 if (down && down.grabbed()) {
27057 freeDraggedElements(draggedElements);
27058 down.emit('freeon');
27059 draggedElements.emit('free');
27060
27061 if (r.dragData.didDrag) {
27062 down.emit('dragfreeon');
27063 draggedElements.emit('dragfree');
27064 }
27065 }
27066
27067 goIntoBoxMode();
27068 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
27069 // drag node
27070 var justStartedDrag = !r.dragData.didDrag;
27071
27072 if (justStartedDrag) {
27073 r.redrawHint('eles', true);
27074 }
27075
27076 r.dragData.didDrag = true; // indicate that we actually did drag the node
27077 // now, add the elements to the drag layer if not done already
27078
27079 if (!r.hoverData.draggingEles) {
27080 addNodesToDrag(draggedElements, {
27081 inDragLayer: true
27082 });
27083 }
27084
27085 var totalShift = {
27086 x: 0,
27087 y: 0
27088 };
27089
27090 if (number$1(disp[0]) && number$1(disp[1])) {
27091 totalShift.x += disp[0];
27092 totalShift.y += disp[1];
27093
27094 if (justStartedDrag) {
27095 var dragDelta = r.hoverData.dragDelta;
27096
27097 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
27098 totalShift.x += dragDelta[0];
27099 totalShift.y += dragDelta[1];
27100 }
27101 }
27102 }
27103
27104 r.hoverData.draggingEles = true;
27105 draggedElements.silentShift(totalShift).emit('position drag');
27106 r.redrawHint('drag', true);
27107 r.redraw();
27108 }
27109 } else {
27110 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
27111 updateDragDelta();
27112 }
27113 } // prevent the dragging from triggering text selection on the page
27114
27115
27116 preventDefault = true;
27117 }
27118
27119 select[2] = pos[0];
27120 select[3] = pos[1];
27121
27122 if (preventDefault) {
27123 if (e.stopPropagation) e.stopPropagation();
27124 if (e.preventDefault) e.preventDefault();
27125 return false;
27126 }
27127 }, false);
27128 var clickTimeout, didDoubleClick, prevClickTimeStamp;
27129 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
27130 // eslint-disable-line no-undef
27131 var capture = r.hoverData.capture;
27132
27133 if (!capture) {
27134 return;
27135 }
27136
27137 r.hoverData.capture = false;
27138 var cy = r.cy;
27139 var pos = r.projectIntoViewport(e.clientX, e.clientY);
27140 var select = r.selection;
27141 var near = r.findNearestElement(pos[0], pos[1], true, false);
27142 var draggedElements = r.dragData.possibleDragElements;
27143 var down = r.hoverData.down;
27144 var multSelKeyDown = isMultSelKeyDown(e);
27145
27146 if (r.data.bgActivePosistion) {
27147 r.redrawHint('select', true);
27148 r.redraw();
27149 }
27150
27151 r.hoverData.tapholdCancelled = true;
27152 r.data.bgActivePosistion = undefined; // not active bg now
27153
27154 if (down) {
27155 down.unactivate();
27156 }
27157
27158 if (r.hoverData.which === 3) {
27159 var cxtEvt = {
27160 originalEvent: e,
27161 type: 'cxttapend',
27162 position: {
27163 x: pos[0],
27164 y: pos[1]
27165 }
27166 };
27167
27168 if (down) {
27169 down.emit(cxtEvt);
27170 } else {
27171 cy.emit(cxtEvt);
27172 }
27173
27174 if (!r.hoverData.cxtDragged) {
27175 var cxtTap = {
27176 originalEvent: e,
27177 type: 'cxttap',
27178 position: {
27179 x: pos[0],
27180 y: pos[1]
27181 }
27182 };
27183
27184 if (down) {
27185 down.emit(cxtTap);
27186 } else {
27187 cy.emit(cxtTap);
27188 }
27189 }
27190
27191 r.hoverData.cxtDragged = false;
27192 r.hoverData.which = null;
27193 } else if (r.hoverData.which === 1) {
27194 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
27195 x: pos[0],
27196 y: pos[1]
27197 });
27198
27199 if (!r.dragData.didDrag && // didn't move a node around
27200 !r.hoverData.dragged && // didn't pan
27201 !r.hoverData.selecting && // not box selection
27202 !r.hoverData.isOverThresholdDrag // didn't move too much
27203 ) {
27204 triggerEvents(down, ["click", "tap", "vclick"], e, {
27205 x: pos[0],
27206 y: pos[1]
27207 });
27208 didDoubleClick = false;
27209
27210 if (e.timeStamp - prevClickTimeStamp <= cy.multiClickDebounceTime()) {
27211 clickTimeout && clearTimeout(clickTimeout);
27212 didDoubleClick = true;
27213 prevClickTimeStamp = null;
27214 triggerEvents(down, ["dblclick", "dbltap", "vdblclick"], e, {
27215 x: pos[0],
27216 y: pos[1]
27217 });
27218 } else {
27219 clickTimeout = setTimeout(function () {
27220 if (didDoubleClick) return;
27221 triggerEvents(down, ["oneclick", "onetap", "voneclick"], e, {
27222 x: pos[0],
27223 y: pos[1]
27224 });
27225 }, cy.multiClickDebounceTime());
27226 prevClickTimeStamp = e.timeStamp;
27227 }
27228 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
27229
27230
27231 if (down == null // not mousedown on node
27232 && !r.dragData.didDrag // didn't move the node around
27233 && !r.hoverData.selecting // not box selection
27234 && !r.hoverData.dragged // didn't pan
27235 && !isMultSelKeyDown(e)) {
27236 cy.$(isSelected).unselect(['tapunselect']);
27237
27238 if (draggedElements.length > 0) {
27239 r.redrawHint('eles', true);
27240 }
27241
27242 r.dragData.possibleDragElements = draggedElements = cy.collection();
27243 } // Single selection
27244
27245
27246 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
27247 if (near != null && near._private.selectable) {
27248 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
27249 if (near.selected()) {
27250 near.unselect(['tapunselect']);
27251 } else {
27252 near.select(['tapselect']);
27253 }
27254 } else {
27255 if (!multSelKeyDown) {
27256 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
27257 near.select(['tapselect']);
27258 }
27259 }
27260
27261 r.redrawHint('eles', true);
27262 }
27263 }
27264
27265 if (r.hoverData.selecting) {
27266 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
27267 r.redrawHint('select', true);
27268
27269 if (box.length > 0) {
27270 r.redrawHint('eles', true);
27271 }
27272
27273 cy.emit({
27274 type: 'boxend',
27275 originalEvent: e,
27276 position: {
27277 x: pos[0],
27278 y: pos[1]
27279 }
27280 });
27281
27282 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
27283 return ele.selectable() && !ele.selected();
27284 };
27285
27286 if (cy.selectionType() === 'additive') {
27287 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
27288 } else {
27289 if (!multSelKeyDown) {
27290 cy.$(isSelected).unmerge(box).unselect();
27291 }
27292
27293 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
27294 } // always need redraw in case eles unselectable
27295
27296
27297 r.redraw();
27298 } // Cancel drag pan
27299
27300
27301 if (r.hoverData.dragging) {
27302 r.hoverData.dragging = false;
27303 r.redrawHint('select', true);
27304 r.redrawHint('eles', true);
27305 r.redraw();
27306 }
27307
27308 if (!select[4]) {
27309 r.redrawHint('drag', true);
27310 r.redrawHint('eles', true);
27311 var downWasGrabbed = down && down.grabbed();
27312 freeDraggedElements(draggedElements);
27313
27314 if (downWasGrabbed) {
27315 down.emit('freeon');
27316 draggedElements.emit('free');
27317
27318 if (r.dragData.didDrag) {
27319 down.emit('dragfreeon');
27320 draggedElements.emit('dragfree');
27321 }
27322 }
27323 }
27324 } // else not right mouse
27325
27326
27327 select[4] = 0;
27328 r.hoverData.down = null;
27329 r.hoverData.cxtStarted = false;
27330 r.hoverData.draggingEles = false;
27331 r.hoverData.selecting = false;
27332 r.hoverData.isOverThresholdDrag = false;
27333 r.dragData.didDrag = false;
27334 r.hoverData.dragged = false;
27335 r.hoverData.dragDelta = [];
27336 r.hoverData.mdownPos = null;
27337 r.hoverData.mdownGPos = null;
27338 }, false);
27339
27340 var wheelHandler = function wheelHandler(e) {
27341 if (r.scrollingPage) {
27342 return;
27343 } // while scrolling, ignore wheel-to-zoom
27344
27345
27346 var cy = r.cy;
27347 var zoom = cy.zoom();
27348 var pan = cy.pan();
27349 var pos = r.projectIntoViewport(e.clientX, e.clientY);
27350 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
27351
27352 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
27353 // if pan dragging or cxt dragging, wheel movements make no zoom
27354 e.preventDefault();
27355 return;
27356 }
27357
27358 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
27359 e.preventDefault();
27360 r.data.wheelZooming = true;
27361 clearTimeout(r.data.wheelTimeout);
27362 r.data.wheelTimeout = setTimeout(function () {
27363 r.data.wheelZooming = false;
27364 r.redrawHint('eles', true);
27365 r.redraw();
27366 }, 150);
27367 var diff;
27368
27369 if (e.deltaY != null) {
27370 diff = e.deltaY / -250;
27371 } else if (e.wheelDeltaY != null) {
27372 diff = e.wheelDeltaY / 1000;
27373 } else {
27374 diff = e.wheelDelta / 1000;
27375 }
27376
27377 diff = diff * r.wheelSensitivity;
27378 var needsWheelFix = e.deltaMode === 1;
27379
27380 if (needsWheelFix) {
27381 // fixes slow wheel events on ff/linux and ff/windows
27382 diff *= 33;
27383 }
27384
27385 var newZoom = cy.zoom() * Math.pow(10, diff);
27386
27387 if (e.type === 'gesturechange') {
27388 newZoom = r.gestureStartZoom * e.scale;
27389 }
27390
27391 cy.zoom({
27392 level: newZoom,
27393 renderedPosition: {
27394 x: rpos[0],
27395 y: rpos[1]
27396 }
27397 });
27398 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
27399 }
27400 }; // Functions to help with whether mouse wheel should trigger zooming
27401 // --
27402
27403
27404 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
27405 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
27406 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
27407 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
27408
27409 r.registerBinding(window, 'scroll', function scrollHandler(e) {
27410 // eslint-disable-line no-unused-vars
27411 r.scrollingPage = true;
27412 clearTimeout(r.scrollingPageTimeout);
27413 r.scrollingPageTimeout = setTimeout(function () {
27414 r.scrollingPage = false;
27415 }, 250);
27416 }, true); // desktop safari pinch to zoom start
27417
27418 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
27419 r.gestureStartZoom = r.cy.zoom();
27420
27421 if (!r.hasTouchStarted) {
27422 // don't affect touch devices like iphone
27423 e.preventDefault();
27424 }
27425 }, true);
27426 r.registerBinding(r.container, 'gesturechange', function (e) {
27427 if (!r.hasTouchStarted) {
27428 // don't affect touch devices like iphone
27429 wheelHandler(e);
27430 }
27431 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
27432 // Handle mouseout on Cytoscape container
27433
27434 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
27435 var pos = r.projectIntoViewport(e.clientX, e.clientY);
27436 r.cy.emit({
27437 originalEvent: e,
27438 type: 'mouseout',
27439 position: {
27440 x: pos[0],
27441 y: pos[1]
27442 }
27443 });
27444 }, false);
27445 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
27446 var pos = r.projectIntoViewport(e.clientX, e.clientY);
27447 r.cy.emit({
27448 originalEvent: e,
27449 type: 'mouseover',
27450 position: {
27451 x: pos[0],
27452 y: pos[1]
27453 }
27454 });
27455 }, false);
27456 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
27457
27458 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
27459
27460 var center1, modelCenter1; // center point on start pinch to zoom
27461
27462 var offsetLeft, offsetTop;
27463 var containerWidth, containerHeight;
27464 var twoFingersStartInside;
27465
27466 var distance = function distance(x1, y1, x2, y2) {
27467 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
27468 };
27469
27470 var distanceSq = function distanceSq(x1, y1, x2, y2) {
27471 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
27472 };
27473
27474 var touchstartHandler;
27475 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
27476 r.hasTouchStarted = true;
27477
27478 if (!eventInContainer(e)) {
27479 return;
27480 }
27481
27482 blurActiveDomElement();
27483 r.touchData.capture = true;
27484 r.data.bgActivePosistion = undefined;
27485 var cy = r.cy;
27486 var now = r.touchData.now;
27487 var earlier = r.touchData.earlier;
27488
27489 if (e.touches[0]) {
27490 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
27491 now[0] = pos[0];
27492 now[1] = pos[1];
27493 }
27494
27495 if (e.touches[1]) {
27496 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
27497 now[2] = pos[0];
27498 now[3] = pos[1];
27499 }
27500
27501 if (e.touches[2]) {
27502 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
27503 now[4] = pos[0];
27504 now[5] = pos[1];
27505 } // record starting points for pinch-to-zoom
27506
27507
27508 if (e.touches[1]) {
27509 r.touchData.singleTouchMoved = true;
27510 freeDraggedElements(r.dragData.touchDragEles);
27511 var offsets = r.findContainerClientCoords();
27512 offsetLeft = offsets[0];
27513 offsetTop = offsets[1];
27514 containerWidth = offsets[2];
27515 containerHeight = offsets[3];
27516 f1x1 = e.touches[0].clientX - offsetLeft;
27517 f1y1 = e.touches[0].clientY - offsetTop;
27518 f2x1 = e.touches[1].clientX - offsetLeft;
27519 f2y1 = e.touches[1].clientY - offsetTop;
27520 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
27521 var pan = cy.pan();
27522 var zoom = cy.zoom();
27523 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
27524 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
27525 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
27526 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
27527
27528 var cxtDistThreshold = 200;
27529 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
27530
27531 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
27532 var near1 = r.findNearestElement(now[0], now[1], true, true);
27533 var near2 = r.findNearestElement(now[2], now[3], true, true);
27534
27535 if (near1 && near1.isNode()) {
27536 near1.activate().emit({
27537 originalEvent: e,
27538 type: 'cxttapstart',
27539 position: {
27540 x: now[0],
27541 y: now[1]
27542 }
27543 });
27544 r.touchData.start = near1;
27545 } else if (near2 && near2.isNode()) {
27546 near2.activate().emit({
27547 originalEvent: e,
27548 type: 'cxttapstart',
27549 position: {
27550 x: now[0],
27551 y: now[1]
27552 }
27553 });
27554 r.touchData.start = near2;
27555 } else {
27556 cy.emit({
27557 originalEvent: e,
27558 type: 'cxttapstart',
27559 position: {
27560 x: now[0],
27561 y: now[1]
27562 }
27563 });
27564 }
27565
27566 if (r.touchData.start) {
27567 r.touchData.start._private.grabbed = false;
27568 }
27569
27570 r.touchData.cxt = true;
27571 r.touchData.cxtDragged = false;
27572 r.data.bgActivePosistion = undefined;
27573 r.redraw();
27574 return;
27575 }
27576 }
27577
27578 if (e.touches[2]) {
27579 // ignore
27580 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
27581 if (cy.boxSelectionEnabled()) {
27582 e.preventDefault();
27583 }
27584 } else if (e.touches[1]) ; else if (e.touches[0]) {
27585 var nears = r.findNearestElements(now[0], now[1], true, true);
27586 var near = nears[0];
27587
27588 if (near != null) {
27589 near.activate();
27590 r.touchData.start = near;
27591 r.touchData.starts = nears;
27592
27593 if (r.nodeIsGrabbable(near)) {
27594 var draggedEles = r.dragData.touchDragEles = cy.collection();
27595 var selectedNodes = null;
27596 r.redrawHint('eles', true);
27597 r.redrawHint('drag', true);
27598
27599 if (near.selected()) {
27600 // reset drag elements, since near will be added again
27601 selectedNodes = cy.$(function (ele) {
27602 return ele.selected() && r.nodeIsGrabbable(ele);
27603 });
27604 addNodesToDrag(selectedNodes, {
27605 addToList: draggedEles
27606 });
27607 } else {
27608 addNodeToDrag(near, {
27609 addToList: draggedEles
27610 });
27611 }
27612
27613 setGrabTarget(near);
27614
27615 var makeEvent = function makeEvent(type) {
27616 return {
27617 originalEvent: e,
27618 type: type,
27619 position: {
27620 x: now[0],
27621 y: now[1]
27622 }
27623 };
27624 };
27625
27626 near.emit(makeEvent('grabon'));
27627
27628 if (selectedNodes) {
27629 selectedNodes.forEach(function (n) {
27630 n.emit(makeEvent('grab'));
27631 });
27632 } else {
27633 near.emit(makeEvent('grab'));
27634 }
27635 }
27636 }
27637
27638 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
27639 x: now[0],
27640 y: now[1]
27641 });
27642
27643 if (near == null) {
27644 r.data.bgActivePosistion = {
27645 x: pos[0],
27646 y: pos[1]
27647 };
27648 r.redrawHint('select', true);
27649 r.redraw();
27650 } // Tap, taphold
27651 // -----
27652
27653
27654 r.touchData.singleTouchMoved = false;
27655 r.touchData.singleTouchStartTime = +new Date();
27656 clearTimeout(r.touchData.tapholdTimeout);
27657 r.touchData.tapholdTimeout = setTimeout(function () {
27658 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
27659 && !r.touchData.selecting // box selection shouldn't allow taphold through
27660 ) {
27661 triggerEvents(r.touchData.start, ['taphold'], e, {
27662 x: now[0],
27663 y: now[1]
27664 });
27665 }
27666 }, r.tapholdDuration);
27667 }
27668
27669 if (e.touches.length >= 1) {
27670 var sPos = r.touchData.startPosition = [];
27671
27672 for (var i = 0; i < now.length; i++) {
27673 sPos[i] = earlier[i] = now[i];
27674 }
27675
27676 var touch0 = e.touches[0];
27677 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
27678 }
27679 }, false);
27680 var touchmoveHandler;
27681 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
27682 // eslint-disable-line no-undef
27683 var capture = r.touchData.capture;
27684
27685 if (!capture && !eventInContainer(e)) {
27686 return;
27687 }
27688
27689 var select = r.selection;
27690 var cy = r.cy;
27691 var now = r.touchData.now;
27692 var earlier = r.touchData.earlier;
27693 var zoom = cy.zoom();
27694
27695 if (e.touches[0]) {
27696 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
27697 now[0] = pos[0];
27698 now[1] = pos[1];
27699 }
27700
27701 if (e.touches[1]) {
27702 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
27703 now[2] = pos[0];
27704 now[3] = pos[1];
27705 }
27706
27707 if (e.touches[2]) {
27708 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
27709 now[4] = pos[0];
27710 now[5] = pos[1];
27711 }
27712
27713 var startGPos = r.touchData.startGPosition;
27714 var isOverThresholdDrag;
27715
27716 if (capture && e.touches[0] && startGPos) {
27717 var disp = [];
27718
27719 for (var j = 0; j < now.length; j++) {
27720 disp[j] = now[j] - earlier[j];
27721 }
27722
27723 var dx = e.touches[0].clientX - startGPos[0];
27724 var dx2 = dx * dx;
27725 var dy = e.touches[0].clientY - startGPos[1];
27726 var dy2 = dy * dy;
27727 var dist2 = dx2 + dy2;
27728 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
27729 } // context swipe cancelling
27730
27731
27732 if (capture && r.touchData.cxt) {
27733 e.preventDefault();
27734 var f1x2 = e.touches[0].clientX - offsetLeft,
27735 f1y2 = e.touches[0].clientY - offsetTop;
27736 var f2x2 = e.touches[1].clientX - offsetLeft,
27737 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
27738
27739 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
27740 var factorSq = distance2Sq / distance1Sq;
27741 var distThreshold = 150;
27742 var distThresholdSq = distThreshold * distThreshold;
27743 var factorThreshold = 1.5;
27744 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
27745
27746 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
27747 r.touchData.cxt = false;
27748 r.data.bgActivePosistion = undefined;
27749 r.redrawHint('select', true);
27750 var cxtEvt = {
27751 originalEvent: e,
27752 type: 'cxttapend',
27753 position: {
27754 x: now[0],
27755 y: now[1]
27756 }
27757 };
27758
27759 if (r.touchData.start) {
27760 r.touchData.start.unactivate().emit(cxtEvt);
27761 r.touchData.start = null;
27762 } else {
27763 cy.emit(cxtEvt);
27764 }
27765 }
27766 } // context swipe
27767
27768
27769 if (capture && r.touchData.cxt) {
27770 var cxtEvt = {
27771 originalEvent: e,
27772 type: 'cxtdrag',
27773 position: {
27774 x: now[0],
27775 y: now[1]
27776 }
27777 };
27778 r.data.bgActivePosistion = undefined;
27779 r.redrawHint('select', true);
27780
27781 if (r.touchData.start) {
27782 r.touchData.start.emit(cxtEvt);
27783 } else {
27784 cy.emit(cxtEvt);
27785 }
27786
27787 if (r.touchData.start) {
27788 r.touchData.start._private.grabbed = false;
27789 }
27790
27791 r.touchData.cxtDragged = true;
27792 var near = r.findNearestElement(now[0], now[1], true, true);
27793
27794 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
27795 if (r.touchData.cxtOver) {
27796 r.touchData.cxtOver.emit({
27797 originalEvent: e,
27798 type: 'cxtdragout',
27799 position: {
27800 x: now[0],
27801 y: now[1]
27802 }
27803 });
27804 }
27805
27806 r.touchData.cxtOver = near;
27807
27808 if (near) {
27809 near.emit({
27810 originalEvent: e,
27811 type: 'cxtdragover',
27812 position: {
27813 x: now[0],
27814 y: now[1]
27815 }
27816 });
27817 }
27818 } // box selection
27819
27820 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
27821 e.preventDefault();
27822 r.data.bgActivePosistion = undefined;
27823 this.lastThreeTouch = +new Date();
27824
27825 if (!r.touchData.selecting) {
27826 cy.emit({
27827 originalEvent: e,
27828 type: 'boxstart',
27829 position: {
27830 x: now[0],
27831 y: now[1]
27832 }
27833 });
27834 }
27835
27836 r.touchData.selecting = true;
27837 r.touchData.didSelect = true;
27838 select[4] = 1;
27839
27840 if (!select || select.length === 0 || select[0] === undefined) {
27841 select[0] = (now[0] + now[2] + now[4]) / 3;
27842 select[1] = (now[1] + now[3] + now[5]) / 3;
27843 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
27844 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
27845 } else {
27846 select[2] = (now[0] + now[2] + now[4]) / 3;
27847 select[3] = (now[1] + now[3] + now[5]) / 3;
27848 }
27849
27850 r.redrawHint('select', true);
27851 r.redraw(); // pinch to zoom
27852 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
27853 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
27854 // two fingers => pinch to zoom
27855 e.preventDefault();
27856 r.data.bgActivePosistion = undefined;
27857 r.redrawHint('select', true);
27858 var draggedEles = r.dragData.touchDragEles;
27859
27860 if (draggedEles) {
27861 r.redrawHint('drag', true);
27862
27863 for (var i = 0; i < draggedEles.length; i++) {
27864 var de_p = draggedEles[i]._private;
27865 de_p.grabbed = false;
27866 de_p.rscratch.inDragLayer = false;
27867 }
27868 }
27869
27870 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
27871
27872 var f1x2 = e.touches[0].clientX - offsetLeft,
27873 f1y2 = e.touches[0].clientY - offsetTop;
27874 var f2x2 = e.touches[1].clientX - offsetLeft,
27875 f2y2 = e.touches[1].clientY - offsetTop;
27876 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
27877 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
27878
27879 var factor = distance2 / distance1;
27880
27881 if (twoFingersStartInside) {
27882 // delta finger1
27883 var df1x = f1x2 - f1x1;
27884 var df1y = f1y2 - f1y1; // delta finger 2
27885
27886 var df2x = f2x2 - f2x1;
27887 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
27888 // i.e. so pinching cancels out and moving together pans
27889
27890 var tx = (df1x + df2x) / 2;
27891 var ty = (df1y + df2y) / 2; // now calculate the zoom
27892
27893 var zoom1 = cy.zoom();
27894 var zoom2 = zoom1 * factor;
27895 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
27896
27897 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
27898 var ctry = modelCenter1[1] * zoom1 + pan1.y;
27899 var pan2 = {
27900 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
27901 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
27902 }; // remove dragged eles
27903
27904 if (_start && _start.active()) {
27905 var draggedEles = r.dragData.touchDragEles;
27906 freeDraggedElements(draggedEles);
27907 r.redrawHint('drag', true);
27908 r.redrawHint('eles', true);
27909
27910 _start.unactivate().emit('freeon');
27911
27912 draggedEles.emit('free');
27913
27914 if (r.dragData.didDrag) {
27915 _start.emit('dragfreeon');
27916
27917 draggedEles.emit('dragfree');
27918 }
27919 }
27920
27921 cy.viewport({
27922 zoom: zoom2,
27923 pan: pan2,
27924 cancelOnFailedZoom: true
27925 });
27926 cy.emit('pinchzoom');
27927 distance1 = distance2;
27928 f1x1 = f1x2;
27929 f1y1 = f1y2;
27930 f2x1 = f2x2;
27931 f2y1 = f2y2;
27932 r.pinching = true;
27933 } // Re-project
27934
27935
27936 if (e.touches[0]) {
27937 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
27938 now[0] = pos[0];
27939 now[1] = pos[1];
27940 }
27941
27942 if (e.touches[1]) {
27943 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
27944 now[2] = pos[0];
27945 now[3] = pos[1];
27946 }
27947
27948 if (e.touches[2]) {
27949 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
27950 now[4] = pos[0];
27951 now[5] = pos[1];
27952 }
27953 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
27954 ) {
27955 var start = r.touchData.start;
27956 var last = r.touchData.last;
27957 var near;
27958
27959 if (!r.hoverData.draggingEles && !r.swipePanning) {
27960 near = r.findNearestElement(now[0], now[1], true, true);
27961 }
27962
27963 if (capture && start != null) {
27964 e.preventDefault();
27965 } // dragging nodes
27966
27967
27968 if (capture && start != null && r.nodeIsDraggable(start)) {
27969 if (isOverThresholdDrag) {
27970 // then dragging can happen
27971 var draggedEles = r.dragData.touchDragEles;
27972 var justStartedDrag = !r.dragData.didDrag;
27973
27974 if (justStartedDrag) {
27975 addNodesToDrag(draggedEles, {
27976 inDragLayer: true
27977 });
27978 }
27979
27980 r.dragData.didDrag = true;
27981 var totalShift = {
27982 x: 0,
27983 y: 0
27984 };
27985
27986 if (number$1(disp[0]) && number$1(disp[1])) {
27987 totalShift.x += disp[0];
27988 totalShift.y += disp[1];
27989
27990 if (justStartedDrag) {
27991 r.redrawHint('eles', true);
27992 var dragDelta = r.touchData.dragDelta;
27993
27994 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
27995 totalShift.x += dragDelta[0];
27996 totalShift.y += dragDelta[1];
27997 }
27998 }
27999 }
28000
28001 r.hoverData.draggingEles = true;
28002 draggedEles.silentShift(totalShift).emit('position drag');
28003 r.redrawHint('drag', true);
28004
28005 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
28006 r.redrawHint('eles', true);
28007 }
28008
28009 r.redraw();
28010 } else {
28011 // otherise keep track of drag delta for later
28012 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
28013
28014 if (dragDelta.length === 0) {
28015 dragDelta.push(disp[0]);
28016 dragDelta.push(disp[1]);
28017 } else {
28018 dragDelta[0] += disp[0];
28019 dragDelta[1] += disp[1];
28020 }
28021 }
28022 } // touchmove
28023
28024
28025 {
28026 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
28027 x: now[0],
28028 y: now[1]
28029 });
28030
28031 if ((!start || !start.grabbed()) && near != last) {
28032 if (last) {
28033 last.emit({
28034 originalEvent: e,
28035 type: 'tapdragout',
28036 position: {
28037 x: now[0],
28038 y: now[1]
28039 }
28040 });
28041 }
28042
28043 if (near) {
28044 near.emit({
28045 originalEvent: e,
28046 type: 'tapdragover',
28047 position: {
28048 x: now[0],
28049 y: now[1]
28050 }
28051 });
28052 }
28053 }
28054
28055 r.touchData.last = near;
28056 } // check to cancel taphold
28057
28058 if (capture) {
28059 for (var i = 0; i < now.length; i++) {
28060 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
28061 r.touchData.singleTouchMoved = true;
28062 }
28063 }
28064 } // panning
28065
28066
28067 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
28068 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
28069
28070 if (allowPassthrough) {
28071 e.preventDefault();
28072
28073 if (!r.data.bgActivePosistion) {
28074 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
28075 }
28076
28077 if (r.swipePanning) {
28078 cy.panBy({
28079 x: disp[0] * zoom,
28080 y: disp[1] * zoom
28081 });
28082 cy.emit('dragpan');
28083 } else if (isOverThresholdDrag) {
28084 r.swipePanning = true;
28085 cy.panBy({
28086 x: dx * zoom,
28087 y: dy * zoom
28088 });
28089 cy.emit('dragpan');
28090
28091 if (start) {
28092 start.unactivate();
28093 r.redrawHint('select', true);
28094 r.touchData.start = null;
28095 }
28096 }
28097 } // Re-project
28098
28099
28100 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
28101 now[0] = pos[0];
28102 now[1] = pos[1];
28103 }
28104 }
28105
28106 for (var j = 0; j < now.length; j++) {
28107 earlier[j] = now[j];
28108 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
28109
28110
28111 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
28112 r.data.bgActivePosistion = undefined;
28113 r.redrawHint('select', true);
28114 r.redraw();
28115 }
28116 }, false);
28117 var touchcancelHandler;
28118 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
28119 // eslint-disable-line no-unused-vars
28120 var start = r.touchData.start;
28121 r.touchData.capture = false;
28122
28123 if (start) {
28124 start.unactivate();
28125 }
28126 });
28127 var touchendHandler, didDoubleTouch, touchTimeout, prevTouchTimeStamp;
28128 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
28129 // eslint-disable-line no-unused-vars
28130 var start = r.touchData.start;
28131 var capture = r.touchData.capture;
28132
28133 if (capture) {
28134 if (e.touches.length === 0) {
28135 r.touchData.capture = false;
28136 }
28137
28138 e.preventDefault();
28139 } else {
28140 return;
28141 }
28142
28143 var select = r.selection;
28144 r.swipePanning = false;
28145 r.hoverData.draggingEles = false;
28146 var cy = r.cy;
28147 var zoom = cy.zoom();
28148 var now = r.touchData.now;
28149 var earlier = r.touchData.earlier;
28150
28151 if (e.touches[0]) {
28152 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
28153 now[0] = pos[0];
28154 now[1] = pos[1];
28155 }
28156
28157 if (e.touches[1]) {
28158 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
28159 now[2] = pos[0];
28160 now[3] = pos[1];
28161 }
28162
28163 if (e.touches[2]) {
28164 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
28165 now[4] = pos[0];
28166 now[5] = pos[1];
28167 }
28168
28169 if (start) {
28170 start.unactivate();
28171 }
28172
28173 var ctxTapend;
28174
28175 if (r.touchData.cxt) {
28176 ctxTapend = {
28177 originalEvent: e,
28178 type: 'cxttapend',
28179 position: {
28180 x: now[0],
28181 y: now[1]
28182 }
28183 };
28184
28185 if (start) {
28186 start.emit(ctxTapend);
28187 } else {
28188 cy.emit(ctxTapend);
28189 }
28190
28191 if (!r.touchData.cxtDragged) {
28192 var ctxTap = {
28193 originalEvent: e,
28194 type: 'cxttap',
28195 position: {
28196 x: now[0],
28197 y: now[1]
28198 }
28199 };
28200
28201 if (start) {
28202 start.emit(ctxTap);
28203 } else {
28204 cy.emit(ctxTap);
28205 }
28206 }
28207
28208 if (r.touchData.start) {
28209 r.touchData.start._private.grabbed = false;
28210 }
28211
28212 r.touchData.cxt = false;
28213 r.touchData.start = null;
28214 r.redraw();
28215 return;
28216 } // no more box selection if we don't have three fingers
28217
28218
28219 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
28220 r.touchData.selecting = false;
28221 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
28222 select[0] = undefined;
28223 select[1] = undefined;
28224 select[2] = undefined;
28225 select[3] = undefined;
28226 select[4] = 0;
28227 r.redrawHint('select', true);
28228 cy.emit({
28229 type: 'boxend',
28230 originalEvent: e,
28231 position: {
28232 x: now[0],
28233 y: now[1]
28234 }
28235 });
28236
28237 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
28238 return ele.selectable() && !ele.selected();
28239 };
28240
28241 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
28242
28243 if (box.nonempty()) {
28244 r.redrawHint('eles', true);
28245 }
28246
28247 r.redraw();
28248 }
28249
28250 if (start != null) {
28251 start.unactivate();
28252 }
28253
28254 if (e.touches[2]) {
28255 r.data.bgActivePosistion = undefined;
28256 r.redrawHint('select', true);
28257 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
28258 r.data.bgActivePosistion = undefined;
28259 r.redrawHint('select', true);
28260 var draggedEles = r.dragData.touchDragEles;
28261
28262 if (start != null) {
28263 var startWasGrabbed = start._private.grabbed;
28264 freeDraggedElements(draggedEles);
28265 r.redrawHint('drag', true);
28266 r.redrawHint('eles', true);
28267
28268 if (startWasGrabbed) {
28269 start.emit('freeon');
28270 draggedEles.emit('free');
28271
28272 if (r.dragData.didDrag) {
28273 start.emit('dragfreeon');
28274 draggedEles.emit('dragfree');
28275 }
28276 }
28277
28278 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
28279 x: now[0],
28280 y: now[1]
28281 });
28282 start.unactivate();
28283 r.touchData.start = null;
28284 } else {
28285 var near = r.findNearestElement(now[0], now[1], true, true);
28286 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
28287 x: now[0],
28288 y: now[1]
28289 });
28290 }
28291
28292 var dx = r.touchData.startPosition[0] - now[0];
28293 var dx2 = dx * dx;
28294 var dy = r.touchData.startPosition[1] - now[1];
28295 var dy2 = dy * dy;
28296 var dist2 = dx2 + dy2;
28297 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
28298
28299 if (!r.touchData.singleTouchMoved) {
28300 if (!start) {
28301 cy.$(':selected').unselect(['tapunselect']);
28302 }
28303
28304 triggerEvents(start, ['tap', 'vclick'], e, {
28305 x: now[0],
28306 y: now[1]
28307 });
28308 didDoubleTouch = false;
28309
28310 if (e.timeStamp - prevTouchTimeStamp <= cy.multiClickDebounceTime()) {
28311 touchTimeout && clearTimeout(touchTimeout);
28312 didDoubleTouch = true;
28313 prevTouchTimeStamp = null;
28314 triggerEvents(start, ['dbltap', 'vdblclick'], e, {
28315 x: now[0],
28316 y: now[1]
28317 });
28318 } else {
28319 touchTimeout = setTimeout(function () {
28320 if (didDoubleTouch) return;
28321 triggerEvents(start, ['onetap', 'voneclick'], e, {
28322 x: now[0],
28323 y: now[1]
28324 });
28325 }, cy.multiClickDebounceTime());
28326 prevTouchTimeStamp = e.timeStamp;
28327 }
28328 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
28329
28330
28331 if (start != null && !r.dragData.didDrag // didn't drag nodes around
28332 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
28333 ) {
28334 if (cy.selectionType() === 'single') {
28335 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
28336 start.select(['tapselect']);
28337 } else {
28338 if (start.selected()) {
28339 start.unselect(['tapunselect']);
28340 } else {
28341 start.select(['tapselect']);
28342 }
28343 }
28344
28345 r.redrawHint('eles', true);
28346 }
28347
28348 r.touchData.singleTouchMoved = true;
28349 }
28350
28351 for (var j = 0; j < now.length; j++) {
28352 earlier[j] = now[j];
28353 }
28354
28355 r.dragData.didDrag = false; // reset for next touchstart
28356
28357 if (e.touches.length === 0) {
28358 r.touchData.dragDelta = [];
28359 r.touchData.startPosition = null;
28360 r.touchData.startGPosition = null;
28361 r.touchData.didSelect = false;
28362 }
28363
28364 if (e.touches.length < 2) {
28365 if (e.touches.length === 1) {
28366 // the old start global pos'n may not be the same finger that remains
28367 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
28368 }
28369
28370 r.pinching = false;
28371 r.redrawHint('eles', true);
28372 r.redraw();
28373 } //r.redraw();
28374
28375 }, false); // fallback compatibility layer for ms pointer events
28376
28377 if (typeof TouchEvent === 'undefined') {
28378 var pointers = [];
28379
28380 var makeTouch = function makeTouch(e) {
28381 return {
28382 clientX: e.clientX,
28383 clientY: e.clientY,
28384 force: 1,
28385 identifier: e.pointerId,
28386 pageX: e.pageX,
28387 pageY: e.pageY,
28388 radiusX: e.width / 2,
28389 radiusY: e.height / 2,
28390 screenX: e.screenX,
28391 screenY: e.screenY,
28392 target: e.target
28393 };
28394 };
28395
28396 var makePointer = function makePointer(e) {
28397 return {
28398 event: e,
28399 touch: makeTouch(e)
28400 };
28401 };
28402
28403 var addPointer = function addPointer(e) {
28404 pointers.push(makePointer(e));
28405 };
28406
28407 var removePointer = function removePointer(e) {
28408 for (var i = 0; i < pointers.length; i++) {
28409 var p = pointers[i];
28410
28411 if (p.event.pointerId === e.pointerId) {
28412 pointers.splice(i, 1);
28413 return;
28414 }
28415 }
28416 };
28417
28418 var updatePointer = function updatePointer(e) {
28419 var p = pointers.filter(function (p) {
28420 return p.event.pointerId === e.pointerId;
28421 })[0];
28422 p.event = e;
28423 p.touch = makeTouch(e);
28424 };
28425
28426 var addTouchesToEvent = function addTouchesToEvent(e) {
28427 e.touches = pointers.map(function (p) {
28428 return p.touch;
28429 });
28430 };
28431
28432 var pointerIsMouse = function pointerIsMouse(e) {
28433 return e.pointerType === 'mouse' || e.pointerType === 4;
28434 };
28435
28436 r.registerBinding(r.container, 'pointerdown', function (e) {
28437 if (pointerIsMouse(e)) {
28438 return;
28439 } // mouse already handled
28440
28441
28442 e.preventDefault();
28443 addPointer(e);
28444 addTouchesToEvent(e);
28445 touchstartHandler(e);
28446 });
28447 r.registerBinding(r.container, 'pointerup', function (e) {
28448 if (pointerIsMouse(e)) {
28449 return;
28450 } // mouse already handled
28451
28452
28453 removePointer(e);
28454 addTouchesToEvent(e);
28455 touchendHandler(e);
28456 });
28457 r.registerBinding(r.container, 'pointercancel', function (e) {
28458 if (pointerIsMouse(e)) {
28459 return;
28460 } // mouse already handled
28461
28462
28463 removePointer(e);
28464 addTouchesToEvent(e);
28465 touchcancelHandler(e);
28466 });
28467 r.registerBinding(r.container, 'pointermove', function (e) {
28468 if (pointerIsMouse(e)) {
28469 return;
28470 } // mouse already handled
28471
28472
28473 e.preventDefault();
28474 updatePointer(e);
28475 addTouchesToEvent(e);
28476 touchmoveHandler(e);
28477 });
28478 }
28479 };
28480
28481 var BRp$2 = {};
28482
28483 BRp$2.generatePolygon = function (name, points) {
28484 return this.nodeShapes[name] = {
28485 renderer: this,
28486 name: name,
28487 points: points,
28488 draw: function draw(context, centerX, centerY, width, height) {
28489 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
28490 },
28491 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
28492 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
28493 },
28494 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
28495 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
28496 }
28497 };
28498 };
28499
28500 BRp$2.generateEllipse = function () {
28501 return this.nodeShapes['ellipse'] = {
28502 renderer: this,
28503 name: 'ellipse',
28504 draw: function draw(context, centerX, centerY, width, height) {
28505 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
28506 },
28507 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
28508 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
28509 },
28510 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
28511 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
28512 }
28513 };
28514 };
28515
28516 BRp$2.generateRoundPolygon = function (name, points) {
28517 // Pre-compute control points
28518 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
28519 // the unit vectors.
28520 // For simplicity the layout will be:
28521 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
28522 var allPoints = new Array(points.length * 2);
28523
28524 for (var i = 0; i < points.length / 2; i++) {
28525 var sourceIndex = i * 2;
28526 var destIndex = void 0;
28527
28528 if (i < points.length / 2 - 1) {
28529 destIndex = (i + 1) * 2;
28530 } else {
28531 destIndex = 0;
28532 }
28533
28534 allPoints[i * 4] = points[sourceIndex];
28535 allPoints[i * 4 + 1] = points[sourceIndex + 1];
28536 var xDest = points[destIndex] - points[sourceIndex];
28537 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
28538 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
28539 allPoints[i * 4 + 2] = xDest / norm;
28540 allPoints[i * 4 + 3] = yDest / norm;
28541 }
28542
28543 return this.nodeShapes[name] = {
28544 renderer: this,
28545 name: name,
28546 points: allPoints,
28547 draw: function draw(context, centerX, centerY, width, height) {
28548 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
28549 },
28550 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
28551 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
28552 },
28553 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
28554 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
28555 }
28556 };
28557 };
28558
28559 BRp$2.generateRoundRectangle = function () {
28560 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
28561 renderer: this,
28562 name: 'round-rectangle',
28563 points: generateUnitNgonPointsFitToSquare(4, 0),
28564 draw: function draw(context, centerX, centerY, width, height) {
28565 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
28566 },
28567 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
28568 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
28569 },
28570 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
28571 var cornerRadius = getRoundRectangleRadius(width, height);
28572 var diam = cornerRadius * 2; // Check hBox
28573
28574 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
28575 return true;
28576 } // Check vBox
28577
28578
28579 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
28580 return true;
28581 } // Check top left quarter circle
28582
28583
28584 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
28585 return true;
28586 } // Check top right quarter circle
28587
28588
28589 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
28590 return true;
28591 } // Check bottom right quarter circle
28592
28593
28594 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
28595 return true;
28596 } // Check bottom left quarter circle
28597
28598
28599 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
28600 return true;
28601 }
28602
28603 return false;
28604 }
28605 };
28606 };
28607
28608 BRp$2.generateCutRectangle = function () {
28609 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
28610 renderer: this,
28611 name: 'cut-rectangle',
28612 cornerLength: getCutRectangleCornerLength(),
28613 points: generateUnitNgonPointsFitToSquare(4, 0),
28614 draw: function draw(context, centerX, centerY, width, height) {
28615 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
28616 },
28617 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
28618 var cl = this.cornerLength;
28619 var hh = height / 2;
28620 var hw = width / 2;
28621 var xBegin = centerX - hw;
28622 var xEnd = centerX + hw;
28623 var yBegin = centerY - hh;
28624 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
28625
28626 return {
28627 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
28628 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
28629 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
28630 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
28631 };
28632 },
28633 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
28634 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
28635 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
28636 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
28637 },
28638 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
28639 // Check hBox
28640 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
28641 return true;
28642 } // Check vBox
28643
28644
28645 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
28646 return true;
28647 }
28648
28649 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
28650 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
28651 }
28652 };
28653 };
28654
28655 BRp$2.generateBarrel = function () {
28656 return this.nodeShapes['barrel'] = {
28657 renderer: this,
28658 name: 'barrel',
28659 points: generateUnitNgonPointsFitToSquare(4, 0),
28660 draw: function draw(context, centerX, centerY, width, height) {
28661 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
28662 },
28663 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
28664 // use two fixed t values for the bezier curve approximation
28665 var t0 = 0.15;
28666 var t1 = 0.5;
28667 var t2 = 0.85;
28668 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
28669
28670 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
28671 // approximate curve pts based on the two t values
28672 var m0 = qbezierPtAt({
28673 x: pts[0],
28674 y: pts[1]
28675 }, {
28676 x: pts[2],
28677 y: pts[3]
28678 }, {
28679 x: pts[4],
28680 y: pts[5]
28681 }, t0);
28682 var m1 = qbezierPtAt({
28683 x: pts[0],
28684 y: pts[1]
28685 }, {
28686 x: pts[2],
28687 y: pts[3]
28688 }, {
28689 x: pts[4],
28690 y: pts[5]
28691 }, t1);
28692 var m2 = qbezierPtAt({
28693 x: pts[0],
28694 y: pts[1]
28695 }, {
28696 x: pts[2],
28697 y: pts[3]
28698 }, {
28699 x: pts[4],
28700 y: pts[5]
28701 }, t2);
28702 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
28703 };
28704
28705 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
28706 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
28707 },
28708 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
28709 var hh = height / 2;
28710 var hw = width / 2;
28711 var xBegin = centerX - hw;
28712 var xEnd = centerX + hw;
28713 var yBegin = centerY - hh;
28714 var yEnd = centerY + hh;
28715 var curveConstants = getBarrelCurveConstants(width, height);
28716 var hOffset = curveConstants.heightOffset;
28717 var wOffset = curveConstants.widthOffset;
28718 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
28719
28720 var pts = {
28721 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
28722 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
28723 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
28724 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
28725 };
28726 pts.topLeft.isTop = true;
28727 pts.topRight.isTop = true;
28728 pts.bottomLeft.isBottom = true;
28729 pts.bottomRight.isBottom = true;
28730 return pts;
28731 },
28732 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
28733 var curveConstants = getBarrelCurveConstants(width, height);
28734 var hOffset = curveConstants.heightOffset;
28735 var wOffset = curveConstants.widthOffset; // Check hBox
28736
28737 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
28738 return true;
28739 } // Check vBox
28740
28741
28742 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
28743 return true;
28744 }
28745
28746 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
28747
28748 var getCurveT = function getCurveT(x, y, curvePts) {
28749 var x0 = curvePts[4];
28750 var x1 = curvePts[2];
28751 var x2 = curvePts[0];
28752 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
28753
28754 var y2 = curvePts[1];
28755 var xMin = Math.min(x0, x2);
28756 var xMax = Math.max(x0, x2);
28757 var yMin = Math.min(y0, y2);
28758 var yMax = Math.max(y0, y2);
28759
28760 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
28761 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
28762 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
28763 var validRoots = roots.filter(function (r) {
28764 return 0 <= r && r <= 1;
28765 });
28766
28767 if (validRoots.length > 0) {
28768 return validRoots[0];
28769 }
28770 }
28771
28772 return null;
28773 };
28774
28775 var curveRegions = Object.keys(barrelCurvePts);
28776
28777 for (var i = 0; i < curveRegions.length; i++) {
28778 var corner = curveRegions[i];
28779 var cornerPts = barrelCurvePts[corner];
28780 var t = getCurveT(x, y, cornerPts);
28781
28782 if (t == null) {
28783 continue;
28784 }
28785
28786 var y0 = cornerPts[5];
28787 var y1 = cornerPts[3];
28788 var y2 = cornerPts[1];
28789 var bezY = qbezierAt(y0, y1, y2, t);
28790
28791 if (cornerPts.isTop && bezY <= y) {
28792 return true;
28793 }
28794
28795 if (cornerPts.isBottom && y <= bezY) {
28796 return true;
28797 }
28798 }
28799
28800 return false;
28801 }
28802 };
28803 };
28804
28805 BRp$2.generateBottomRoundrectangle = function () {
28806 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
28807 renderer: this,
28808 name: 'bottom-round-rectangle',
28809 points: generateUnitNgonPointsFitToSquare(4, 0),
28810 draw: function draw(context, centerX, centerY, width, height) {
28811 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
28812 },
28813 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
28814 var topStartX = nodeX - (width / 2 + padding);
28815 var topStartY = nodeY - (height / 2 + padding);
28816 var topEndY = topStartY;
28817 var topEndX = nodeX + (width / 2 + padding);
28818 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
28819
28820 if (topIntersections.length > 0) {
28821 return topIntersections;
28822 }
28823
28824 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
28825 },
28826 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
28827 var cornerRadius = getRoundRectangleRadius(width, height);
28828 var diam = 2 * cornerRadius; // Check hBox
28829
28830 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
28831 return true;
28832 } // Check vBox
28833
28834
28835 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
28836 return true;
28837 } // check non-rounded top side
28838
28839
28840 var outerWidth = width / 2 + 2 * padding;
28841 var outerHeight = height / 2 + 2 * padding;
28842 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
28843
28844 if (pointInsidePolygonPoints(x, y, points)) {
28845 return true;
28846 } // Check bottom right quarter circle
28847
28848
28849 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
28850 return true;
28851 } // Check bottom left quarter circle
28852
28853
28854 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
28855 return true;
28856 }
28857
28858 return false;
28859 }
28860 };
28861 };
28862
28863 BRp$2.registerNodeShapes = function () {
28864 var nodeShapes = this.nodeShapes = {};
28865 var renderer = this;
28866 this.generateEllipse();
28867 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
28868 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
28869 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
28870 nodeShapes['square'] = nodeShapes['rectangle'];
28871 this.generateRoundRectangle();
28872 this.generateCutRectangle();
28873 this.generateBarrel();
28874 this.generateBottomRoundrectangle();
28875 {
28876 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
28877 this.generatePolygon('diamond', diamondPoints);
28878 this.generateRoundPolygon('round-diamond', diamondPoints);
28879 }
28880 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
28881 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
28882 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
28883 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
28884 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
28885 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
28886 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
28887 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
28888 var star5Points = new Array(20);
28889 {
28890 var outerPoints = generateUnitNgonPoints(5, 0);
28891 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
28892
28893 var innerRadius = 0.5 * (3 - Math.sqrt(5));
28894 innerRadius *= 1.57;
28895
28896 for (var i = 0; i < innerPoints.length / 2; i++) {
28897 innerPoints[i * 2] *= innerRadius;
28898 innerPoints[i * 2 + 1] *= innerRadius;
28899 }
28900
28901 for (var i = 0; i < 20 / 4; i++) {
28902 star5Points[i * 4] = outerPoints[i * 2];
28903 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
28904 star5Points[i * 4 + 2] = innerPoints[i * 2];
28905 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
28906 }
28907 }
28908 star5Points = fitPolygonToSquare(star5Points);
28909 this.generatePolygon('star', star5Points);
28910 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
28911 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
28912 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]);
28913 {
28914 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
28915 this.generatePolygon('tag', tagPoints);
28916 this.generateRoundPolygon('round-tag', tagPoints);
28917 }
28918
28919 nodeShapes.makePolygon = function (points) {
28920 // use caching on user-specified polygons so they are as fast as native shapes
28921 var key = points.join('$');
28922 var name = 'polygon-' + key;
28923 var shape;
28924
28925 if (shape = this[name]) {
28926 // got cached shape
28927 return shape;
28928 } // create and cache new shape
28929
28930
28931 return renderer.generatePolygon(name, points);
28932 };
28933 };
28934
28935 var BRp$1 = {};
28936
28937 BRp$1.timeToRender = function () {
28938 return this.redrawTotalTime / this.redrawCount;
28939 };
28940
28941 BRp$1.redraw = function (options) {
28942 options = options || staticEmptyObject();
28943 var r = this;
28944
28945 if (r.averageRedrawTime === undefined) {
28946 r.averageRedrawTime = 0;
28947 }
28948
28949 if (r.lastRedrawTime === undefined) {
28950 r.lastRedrawTime = 0;
28951 }
28952
28953 if (r.lastDrawTime === undefined) {
28954 r.lastDrawTime = 0;
28955 }
28956
28957 r.requestedFrame = true;
28958 r.renderOptions = options;
28959 };
28960
28961 BRp$1.beforeRender = function (fn, priority) {
28962 // the renderer can't add tick callbacks when destroyed
28963 if (this.destroyed) {
28964 return;
28965 }
28966
28967 if (priority == null) {
28968 error('Priority is not optional for beforeRender');
28969 }
28970
28971 var cbs = this.beforeRenderCallbacks;
28972 cbs.push({
28973 fn: fn,
28974 priority: priority
28975 }); // higher priority callbacks executed first
28976
28977 cbs.sort(function (a, b) {
28978 return b.priority - a.priority;
28979 });
28980 };
28981
28982 var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
28983 var cbs = r.beforeRenderCallbacks;
28984
28985 for (var i = 0; i < cbs.length; i++) {
28986 cbs[i].fn(willDraw, startTime);
28987 }
28988 };
28989
28990 BRp$1.startRenderLoop = function () {
28991 var r = this;
28992 var cy = r.cy;
28993
28994 if (r.renderLoopStarted) {
28995 return;
28996 } else {
28997 r.renderLoopStarted = true;
28998 }
28999
29000 var renderFn = function renderFn(requestTime) {
29001 if (r.destroyed) {
29002 return;
29003 }
29004
29005 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
29006 beforeRenderCallbacks(r, true, requestTime);
29007 var startTime = performanceNow();
29008 r.render(r.renderOptions);
29009 var endTime = r.lastDrawTime = performanceNow();
29010
29011 if (r.averageRedrawTime === undefined) {
29012 r.averageRedrawTime = endTime - startTime;
29013 }
29014
29015 if (r.redrawCount === undefined) {
29016 r.redrawCount = 0;
29017 }
29018
29019 r.redrawCount++;
29020
29021 if (r.redrawTotalTime === undefined) {
29022 r.redrawTotalTime = 0;
29023 }
29024
29025 var duration = endTime - startTime;
29026 r.redrawTotalTime += duration;
29027 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
29028
29029 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
29030 r.requestedFrame = false;
29031 } else {
29032 beforeRenderCallbacks(r, false, requestTime);
29033 }
29034
29035 r.skipFrame = false;
29036 requestAnimationFrame(renderFn);
29037 };
29038
29039 requestAnimationFrame(renderFn);
29040 };
29041
29042 var BaseRenderer = function BaseRenderer(options) {
29043 this.init(options);
29044 };
29045
29046 var BR = BaseRenderer;
29047 var BRp = BR.prototype;
29048 BRp.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
29049
29050 BRp.init = function (options) {
29051 var r = this;
29052 r.options = options;
29053 r.cy = options.cy;
29054 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
29055
29056 if (window$1) {
29057 var document = window$1.document;
29058 var head = document.head;
29059 var stylesheetId = '__________cytoscape_stylesheet';
29060 var className = '__________cytoscape_container';
29061 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
29062
29063 if (ctr.className.indexOf(className) < 0) {
29064 ctr.className = (ctr.className || '') + ' ' + className;
29065 }
29066
29067 if (!stylesheetAlreadyExists) {
29068 var stylesheet = document.createElement('style');
29069 stylesheet.id = stylesheetId;
29070 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
29071 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
29072 }
29073
29074 var computedStyle = window$1.getComputedStyle(ctr);
29075 var position = computedStyle.getPropertyValue('position');
29076
29077 if (position === 'static') {
29078 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
29079 }
29080 }
29081
29082 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
29083
29084 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
29085
29086 r.hoverData = {
29087 down: null,
29088 last: null,
29089 downTime: null,
29090 triggerMode: null,
29091 dragging: false,
29092 initialPan: [null, null],
29093 capture: false
29094 };
29095 r.dragData = {
29096 possibleDragElements: []
29097 };
29098 r.touchData = {
29099 start: null,
29100 capture: false,
29101 // These 3 fields related to tap, taphold events
29102 startPosition: [null, null, null, null, null, null],
29103 singleTouchStartTime: null,
29104 singleTouchMoved: true,
29105 now: [null, null, null, null, null, null],
29106 earlier: [null, null, null, null, null, null]
29107 };
29108 r.redraws = 0;
29109 r.showFps = options.showFps;
29110 r.debug = options.debug;
29111 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
29112 r.textureOnViewport = options.textureOnViewport;
29113 r.wheelSensitivity = options.wheelSensitivity;
29114 r.motionBlurEnabled = options.motionBlur; // on by default
29115
29116 r.forcedPixelRatio = number$1(options.pixelRatio) ? options.pixelRatio : null;
29117 r.motionBlur = options.motionBlur; // for initial kick off
29118
29119 r.motionBlurOpacity = options.motionBlurOpacity;
29120 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
29121 r.motionBlurPxRatio = 1;
29122 r.mbPxRBlurry = 1; //0.8;
29123
29124 r.minMbLowQualFrames = 4;
29125 r.fullQualityMb = false;
29126 r.clearedForMotionBlur = [];
29127 r.desktopTapThreshold = options.desktopTapThreshold;
29128 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
29129 r.touchTapThreshold = options.touchTapThreshold;
29130 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
29131 r.tapholdDuration = 500;
29132 r.bindings = [];
29133 r.beforeRenderCallbacks = [];
29134 r.beforeRenderPriorities = {
29135 // higher priority execs before lower one
29136 animations: 400,
29137 eleCalcs: 300,
29138 eleTxrDeq: 200,
29139 lyrTxrDeq: 150,
29140 lyrTxrSkip: 100
29141 };
29142 r.registerNodeShapes();
29143 r.registerArrowShapes();
29144 r.registerCalculationListeners();
29145 };
29146
29147 BRp.notify = function (eventName, eles) {
29148 var r = this;
29149 var cy = r.cy; // the renderer can't be notified after it's destroyed
29150
29151 if (this.destroyed) {
29152 return;
29153 }
29154
29155 if (eventName === 'init') {
29156 r.load();
29157 return;
29158 }
29159
29160 if (eventName === 'destroy') {
29161 r.destroy();
29162 return;
29163 }
29164
29165 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
29166 r.invalidateCachedZSortedEles();
29167 }
29168
29169 if (eventName === 'viewport') {
29170 r.redrawHint('select', true);
29171 }
29172
29173 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
29174 r.invalidateContainerClientCoordsCache();
29175 r.matchCanvasSize(r.container);
29176 }
29177
29178 r.redrawHint('eles', true);
29179 r.redrawHint('drag', true);
29180 this.startRenderLoop();
29181 this.redraw();
29182 };
29183
29184 BRp.destroy = function () {
29185 var r = this;
29186 r.destroyed = true;
29187 r.cy.stopAnimationLoop();
29188
29189 for (var i = 0; i < r.bindings.length; i++) {
29190 var binding = r.bindings[i];
29191 var b = binding;
29192 var tgt = b.target;
29193 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
29194 }
29195
29196 r.bindings = [];
29197 r.beforeRenderCallbacks = [];
29198 r.onUpdateEleCalcsFns = [];
29199
29200 if (r.removeObserver) {
29201 r.removeObserver.disconnect();
29202 }
29203
29204 if (r.styleObserver) {
29205 r.styleObserver.disconnect();
29206 }
29207
29208 if (r.resizeObserver) {
29209 r.resizeObserver.disconnect();
29210 }
29211
29212 if (r.labelCalcDiv) {
29213 try {
29214 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
29215 } catch (e) {// ie10 issue #1014
29216 }
29217 }
29218 };
29219
29220 BRp.isHeadless = function () {
29221 return false;
29222 };
29223
29224 [BRp$f, BRp$5, BRp$4, BRp$3, BRp$2, BRp$1].forEach(function (props) {
29225 extend(BRp, props);
29226 });
29227
29228 var fullFpsTime = 1000 / 60; // assume 60 frames per second
29229
29230 var defs = {
29231 setupDequeueing: function setupDequeueing(opts) {
29232 return function setupDequeueingImpl() {
29233 var self = this;
29234 var r = this.renderer;
29235
29236 if (self.dequeueingSetup) {
29237 return;
29238 } else {
29239 self.dequeueingSetup = true;
29240 }
29241
29242 var queueRedraw = debounce_1(function () {
29243 r.redrawHint('eles', true);
29244 r.redrawHint('drag', true);
29245 r.redraw();
29246 }, opts.deqRedrawThreshold);
29247
29248 var dequeue = function dequeue(willDraw, frameStartTime) {
29249 var startTime = performanceNow();
29250 var avgRenderTime = r.averageRedrawTime;
29251 var renderTime = r.lastRedrawTime;
29252 var deqd = [];
29253 var extent = r.cy.extent();
29254 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
29255 // queue won't automatically be flushed before dequeueing starts
29256
29257 if (!willDraw) {
29258 r.flushRenderedStyleQueue();
29259 }
29260
29261 while (true) {
29262 // eslint-disable-line no-constant-condition
29263 var now = performanceNow();
29264 var duration = now - startTime;
29265 var frameDuration = now - frameStartTime;
29266
29267 if (renderTime < fullFpsTime) {
29268 // if we're rendering faster than the ideal fps, then do dequeueing
29269 // during all of the remaining frame time
29270 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
29271
29272 if (frameDuration >= opts.deqFastCost * timeAvailable) {
29273 break;
29274 }
29275 } else {
29276 if (willDraw) {
29277 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
29278 break;
29279 }
29280 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
29281 break;
29282 }
29283 }
29284
29285 var thisDeqd = opts.deq(self, pixelRatio, extent);
29286
29287 if (thisDeqd.length > 0) {
29288 for (var i = 0; i < thisDeqd.length; i++) {
29289 deqd.push(thisDeqd[i]);
29290 }
29291 } else {
29292 break;
29293 }
29294 } // callbacks on dequeue
29295
29296
29297 if (deqd.length > 0) {
29298 opts.onDeqd(self, deqd);
29299
29300 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
29301 queueRedraw();
29302 }
29303 }
29304 };
29305
29306 var priority = opts.priority || noop$1;
29307 r.beforeRender(dequeue, priority(self));
29308 };
29309 }
29310 };
29311
29312 // Uses keys so elements may share the same cache.
29313
29314 var ElementTextureCacheLookup = /*#__PURE__*/function () {
29315 function ElementTextureCacheLookup(getKey) {
29316 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
29317
29318 _classCallCheck(this, ElementTextureCacheLookup);
29319
29320 this.idsByKey = new Map$2();
29321 this.keyForId = new Map$2();
29322 this.cachesByLvl = new Map$2();
29323 this.lvls = [];
29324 this.getKey = getKey;
29325 this.doesEleInvalidateKey = doesEleInvalidateKey;
29326 }
29327
29328 _createClass(ElementTextureCacheLookup, [{
29329 key: "getIdsFor",
29330 value: function getIdsFor(key) {
29331 if (key == null) {
29332 error("Can not get id list for null key");
29333 }
29334
29335 var idsByKey = this.idsByKey;
29336 var ids = this.idsByKey.get(key);
29337
29338 if (!ids) {
29339 ids = new Set$1();
29340 idsByKey.set(key, ids);
29341 }
29342
29343 return ids;
29344 }
29345 }, {
29346 key: "addIdForKey",
29347 value: function addIdForKey(key, id) {
29348 if (key != null) {
29349 this.getIdsFor(key).add(id);
29350 }
29351 }
29352 }, {
29353 key: "deleteIdForKey",
29354 value: function deleteIdForKey(key, id) {
29355 if (key != null) {
29356 this.getIdsFor(key)["delete"](id);
29357 }
29358 }
29359 }, {
29360 key: "getNumberOfIdsForKey",
29361 value: function getNumberOfIdsForKey(key) {
29362 if (key == null) {
29363 return 0;
29364 } else {
29365 return this.getIdsFor(key).size;
29366 }
29367 }
29368 }, {
29369 key: "updateKeyMappingFor",
29370 value: function updateKeyMappingFor(ele) {
29371 var id = ele.id();
29372 var prevKey = this.keyForId.get(id);
29373 var currKey = this.getKey(ele);
29374 this.deleteIdForKey(prevKey, id);
29375 this.addIdForKey(currKey, id);
29376 this.keyForId.set(id, currKey);
29377 }
29378 }, {
29379 key: "deleteKeyMappingFor",
29380 value: function deleteKeyMappingFor(ele) {
29381 var id = ele.id();
29382 var prevKey = this.keyForId.get(id);
29383 this.deleteIdForKey(prevKey, id);
29384 this.keyForId["delete"](id);
29385 }
29386 }, {
29387 key: "keyHasChangedFor",
29388 value: function keyHasChangedFor(ele) {
29389 var id = ele.id();
29390 var prevKey = this.keyForId.get(id);
29391 var newKey = this.getKey(ele);
29392 return prevKey !== newKey;
29393 }
29394 }, {
29395 key: "isInvalid",
29396 value: function isInvalid(ele) {
29397 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
29398 }
29399 }, {
29400 key: "getCachesAt",
29401 value: function getCachesAt(lvl) {
29402 var cachesByLvl = this.cachesByLvl,
29403 lvls = this.lvls;
29404 var caches = cachesByLvl.get(lvl);
29405
29406 if (!caches) {
29407 caches = new Map$2();
29408 cachesByLvl.set(lvl, caches);
29409 lvls.push(lvl);
29410 }
29411
29412 return caches;
29413 }
29414 }, {
29415 key: "getCache",
29416 value: function getCache(key, lvl) {
29417 return this.getCachesAt(lvl).get(key);
29418 }
29419 }, {
29420 key: "get",
29421 value: function get(ele, lvl) {
29422 var key = this.getKey(ele);
29423 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
29424
29425 if (cache != null) {
29426 this.updateKeyMappingFor(ele);
29427 }
29428
29429 return cache;
29430 }
29431 }, {
29432 key: "getForCachedKey",
29433 value: function getForCachedKey(ele, lvl) {
29434 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
29435
29436 var cache = this.getCache(key, lvl);
29437 return cache;
29438 }
29439 }, {
29440 key: "hasCache",
29441 value: function hasCache(key, lvl) {
29442 return this.getCachesAt(lvl).has(key);
29443 }
29444 }, {
29445 key: "has",
29446 value: function has(ele, lvl) {
29447 var key = this.getKey(ele);
29448 return this.hasCache(key, lvl);
29449 }
29450 }, {
29451 key: "setCache",
29452 value: function setCache(key, lvl, cache) {
29453 cache.key = key;
29454 this.getCachesAt(lvl).set(key, cache);
29455 }
29456 }, {
29457 key: "set",
29458 value: function set(ele, lvl, cache) {
29459 var key = this.getKey(ele);
29460 this.setCache(key, lvl, cache);
29461 this.updateKeyMappingFor(ele);
29462 }
29463 }, {
29464 key: "deleteCache",
29465 value: function deleteCache(key, lvl) {
29466 this.getCachesAt(lvl)["delete"](key);
29467 }
29468 }, {
29469 key: "delete",
29470 value: function _delete(ele, lvl) {
29471 var key = this.getKey(ele);
29472 this.deleteCache(key, lvl);
29473 }
29474 }, {
29475 key: "invalidateKey",
29476 value: function invalidateKey(key) {
29477 var _this = this;
29478
29479 this.lvls.forEach(function (lvl) {
29480 return _this.deleteCache(key, lvl);
29481 });
29482 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
29483
29484 }, {
29485 key: "invalidate",
29486 value: function invalidate(ele) {
29487 var id = ele.id();
29488 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
29489
29490 this.deleteKeyMappingFor(ele);
29491 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
29492
29493 if (entireKeyInvalidated) {
29494 // clear mapping for current key
29495 this.invalidateKey(key);
29496 }
29497
29498 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
29499 }
29500 }]);
29501
29502 return ElementTextureCacheLookup;
29503 }();
29504
29505 var minTxrH = 25; // the size of the texture cache for small height eles (special case)
29506
29507 var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
29508
29509 var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
29510
29511 var maxLvl$1 = 3; // when larger than this scale just render directly (caching is not helpful)
29512
29513 var maxZoom$1 = 7.99; // beyond this zoom level, layered textures are not used
29514
29515 var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
29516
29517 var defTxrWidth = 1024; // default/minimum texture width
29518
29519 var maxTxrW = 1024; // the maximum width of a texture
29520
29521 var maxTxrH = 1024; // the maximum height of a texture
29522
29523 var minUtility = 0.2; // if usage of texture is less than this, it is retired
29524
29525 var maxFullness = 0.8; // fullness of texture after which queue removal is checked
29526
29527 var maxFullnessChecks = 10; // dequeued after this many checks
29528
29529 var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
29530
29531 var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
29532
29533 var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
29534
29535 var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
29536
29537 var deqRedrawThreshold$1 = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
29538
29539 var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
29540
29541 var getTxrReasons = {
29542 dequeue: 'dequeue',
29543 downscale: 'downscale',
29544 highQuality: 'highQuality'
29545 };
29546 var initDefaults = defaults$g({
29547 getKey: null,
29548 doesEleInvalidateKey: falsify,
29549 drawElement: null,
29550 getBoundingBox: null,
29551 getRotationPoint: null,
29552 getRotationOffset: null,
29553 isVisible: trueify,
29554 allowEdgeTxrCaching: true,
29555 allowParentTxrCaching: true
29556 });
29557
29558 var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
29559 var self = this;
29560 self.renderer = renderer;
29561 self.onDequeues = [];
29562 var opts = initDefaults(initOptions);
29563 extend(self, opts);
29564 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
29565 self.setupDequeueing();
29566 };
29567
29568 var ETCp = ElementTextureCache.prototype;
29569 ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
29570
29571 ETCp.getTextureQueue = function (txrH) {
29572 var self = this;
29573 self.eleImgCaches = self.eleImgCaches || {};
29574 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
29575 }; // the list of usused textures which can be recycled (in use in texture queue)
29576
29577
29578 ETCp.getRetiredTextureQueue = function (txrH) {
29579 var self = this;
29580 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
29581 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
29582 return rtxtrQ;
29583 }; // queue of element draw requests at different scale levels
29584
29585
29586 ETCp.getElementQueue = function () {
29587 var self = this;
29588 var q = self.eleCacheQueue = self.eleCacheQueue || new heap(function (a, b) {
29589 return b.reqs - a.reqs;
29590 });
29591 return q;
29592 }; // queue of element draw requests at different scale levels (element id lookup)
29593
29594
29595 ETCp.getElementKeyToQueue = function () {
29596 var self = this;
29597 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
29598 return k2q;
29599 };
29600
29601 ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
29602 var self = this;
29603 var r = this.renderer;
29604 var zoom = r.cy.zoom();
29605 var lookup = this.lookup;
29606
29607 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
29608 return null;
29609 }
29610
29611 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
29612 return null;
29613 }
29614
29615 if (lvl == null) {
29616 lvl = Math.ceil(log2(zoom * pxRatio));
29617 }
29618
29619 if (lvl < minLvl$1) {
29620 lvl = minLvl$1;
29621 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
29622 return null;
29623 }
29624
29625 var scale = Math.pow(2, lvl);
29626 var eleScaledH = bb.h * scale;
29627 var eleScaledW = bb.w * scale;
29628 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
29629
29630 if (!this.isVisible(ele, scaledLabelShown)) {
29631 return null;
29632 }
29633
29634 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
29635
29636 if (eleCache && eleCache.invalidated) {
29637 eleCache.invalidated = false;
29638 eleCache.texture.invalidatedWidth -= eleCache.width;
29639 }
29640
29641 if (eleCache) {
29642 return eleCache;
29643 }
29644
29645 var txrH; // which texture height this ele belongs to
29646
29647 if (eleScaledH <= minTxrH) {
29648 txrH = minTxrH;
29649 } else if (eleScaledH <= txrStepH) {
29650 txrH = txrStepH;
29651 } else {
29652 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
29653 }
29654
29655 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
29656 return null; // caching large elements is not efficient
29657 }
29658
29659 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
29660
29661 var txr = txrQ[txrQ.length - 2];
29662
29663 var addNewTxr = function addNewTxr() {
29664 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
29665 }; // try the last one if there is no second last one
29666
29667
29668 if (!txr) {
29669 txr = txrQ[txrQ.length - 1];
29670 } // if the last one doesn't exist, we need a first one
29671
29672
29673 if (!txr) {
29674 txr = addNewTxr();
29675 } // if there's no room in the current texture, we need a new one
29676
29677
29678 if (txr.width - txr.usedWidth < eleScaledW) {
29679 txr = addNewTxr();
29680 }
29681
29682 var scalableFrom = function scalableFrom(otherCache) {
29683 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
29684 };
29685
29686 var deqing = reason && reason === getTxrReasons.dequeue;
29687 var highQualityReq = reason && reason === getTxrReasons.highQuality;
29688 var downscaleReq = reason && reason === getTxrReasons.downscale;
29689 var higherCache; // the nearest cache with a higher level
29690
29691 for (var l = lvl + 1; l <= maxLvl$1; l++) {
29692 var c = lookup.get(ele, l);
29693
29694 if (c) {
29695 higherCache = c;
29696 break;
29697 }
29698 }
29699
29700 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
29701
29702 var downscale = function downscale() {
29703 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
29704 }; // reset ele area in texture
29705
29706
29707 txr.context.setTransform(1, 0, 0, 1, 0, 0);
29708 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
29709
29710 if (scalableFrom(oneUpCache)) {
29711 // then we can relatively cheaply rescale the existing image w/o rerendering
29712 downscale();
29713 } else if (scalableFrom(higherCache)) {
29714 // then use the higher cache for now and queue the next level down
29715 // to cheaply scale towards the smaller level
29716 if (highQualityReq) {
29717 for (var _l = higherCache.level; _l > lvl; _l--) {
29718 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
29719 }
29720
29721 downscale();
29722 } else {
29723 self.queueElement(ele, higherCache.level - 1);
29724 return higherCache;
29725 }
29726 } else {
29727 var lowerCache; // the nearest cache with a lower level
29728
29729 if (!deqing && !highQualityReq && !downscaleReq) {
29730 for (var _l2 = lvl - 1; _l2 >= minLvl$1; _l2--) {
29731 var _c = lookup.get(ele, _l2);
29732
29733 if (_c) {
29734 lowerCache = _c;
29735 break;
29736 }
29737 }
29738 }
29739
29740 if (scalableFrom(lowerCache)) {
29741 // then use the lower quality cache for now and queue the better one for later
29742 self.queueElement(ele, lvl);
29743 return lowerCache;
29744 }
29745
29746 txr.context.translate(txr.usedWidth, 0);
29747 txr.context.scale(scale, scale);
29748 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
29749 txr.context.scale(1 / scale, 1 / scale);
29750 txr.context.translate(-txr.usedWidth, 0);
29751 }
29752
29753 eleCache = {
29754 x: txr.usedWidth,
29755 texture: txr,
29756 level: lvl,
29757 scale: scale,
29758 width: eleScaledW,
29759 height: eleScaledH,
29760 scaledLabelShown: scaledLabelShown
29761 };
29762 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
29763 txr.eleCaches.push(eleCache);
29764 lookup.set(ele, lvl, eleCache);
29765 self.checkTextureFullness(txr);
29766 return eleCache;
29767 };
29768
29769 ETCp.invalidateElements = function (eles) {
29770 for (var i = 0; i < eles.length; i++) {
29771 this.invalidateElement(eles[i]);
29772 }
29773 };
29774
29775 ETCp.invalidateElement = function (ele) {
29776 var self = this;
29777 var lookup = self.lookup;
29778 var caches = [];
29779 var invalid = lookup.isInvalid(ele);
29780
29781 if (!invalid) {
29782 return; // override the invalidation request if the element key has not changed
29783 }
29784
29785 for (var lvl = minLvl$1; lvl <= maxLvl$1; lvl++) {
29786 var cache = lookup.getForCachedKey(ele, lvl);
29787
29788 if (cache) {
29789 caches.push(cache);
29790 }
29791 }
29792
29793 var noOtherElesUseCache = lookup.invalidate(ele);
29794
29795 if (noOtherElesUseCache) {
29796 for (var i = 0; i < caches.length; i++) {
29797 var _cache = caches[i];
29798 var txr = _cache.texture; // remove space from the texture it belongs to
29799
29800 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
29801
29802 _cache.invalidated = true; // retire the texture if its utility is low
29803
29804 self.checkTextureUtility(txr);
29805 }
29806 } // remove from queue since the old req was for the old state
29807
29808
29809 self.removeFromQueue(ele);
29810 };
29811
29812 ETCp.checkTextureUtility = function (txr) {
29813 // invalidate all entries in the cache if the cache size is small
29814 if (txr.invalidatedWidth >= minUtility * txr.width) {
29815 this.retireTexture(txr);
29816 }
29817 };
29818
29819 ETCp.checkTextureFullness = function (txr) {
29820 // if texture has been mostly filled and passed over several times, remove
29821 // it from the queue so we don't need to waste time looking at it to put new things
29822 var self = this;
29823 var txrQ = self.getTextureQueue(txr.height);
29824
29825 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
29826 removeFromArray(txrQ, txr);
29827 } else {
29828 txr.fullnessChecks++;
29829 }
29830 };
29831
29832 ETCp.retireTexture = function (txr) {
29833 var self = this;
29834 var txrH = txr.height;
29835 var txrQ = self.getTextureQueue(txrH);
29836 var lookup = this.lookup; // retire the texture from the active / searchable queue:
29837
29838 removeFromArray(txrQ, txr);
29839 txr.retired = true; // remove the refs from the eles to the caches:
29840
29841 var eleCaches = txr.eleCaches;
29842
29843 for (var i = 0; i < eleCaches.length; i++) {
29844 var eleCache = eleCaches[i];
29845 lookup.deleteCache(eleCache.key, eleCache.level);
29846 }
29847
29848 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
29849
29850 var rtxtrQ = self.getRetiredTextureQueue(txrH);
29851 rtxtrQ.push(txr);
29852 };
29853
29854 ETCp.addTexture = function (txrH, minW) {
29855 var self = this;
29856 var txrQ = self.getTextureQueue(txrH);
29857 var txr = {};
29858 txrQ.push(txr);
29859 txr.eleCaches = [];
29860 txr.height = txrH;
29861 txr.width = Math.max(defTxrWidth, minW);
29862 txr.usedWidth = 0;
29863 txr.invalidatedWidth = 0;
29864 txr.fullnessChecks = 0;
29865 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
29866 txr.context = txr.canvas.getContext('2d');
29867 return txr;
29868 };
29869
29870 ETCp.recycleTexture = function (txrH, minW) {
29871 var self = this;
29872 var txrQ = self.getTextureQueue(txrH);
29873 var rtxtrQ = self.getRetiredTextureQueue(txrH);
29874
29875 for (var i = 0; i < rtxtrQ.length; i++) {
29876 var txr = rtxtrQ[i];
29877
29878 if (txr.width >= minW) {
29879 txr.retired = false;
29880 txr.usedWidth = 0;
29881 txr.invalidatedWidth = 0;
29882 txr.fullnessChecks = 0;
29883 clearArray(txr.eleCaches);
29884 txr.context.setTransform(1, 0, 0, 1, 0, 0);
29885 txr.context.clearRect(0, 0, txr.width, txr.height);
29886 removeFromArray(rtxtrQ, txr);
29887 txrQ.push(txr);
29888 return txr;
29889 }
29890 }
29891 };
29892
29893 ETCp.queueElement = function (ele, lvl) {
29894 var self = this;
29895 var q = self.getElementQueue();
29896 var k2q = self.getElementKeyToQueue();
29897 var key = this.getKey(ele);
29898 var existingReq = k2q[key];
29899
29900 if (existingReq) {
29901 // use the max lvl b/c in between lvls are cheap to make
29902 existingReq.level = Math.max(existingReq.level, lvl);
29903 existingReq.eles.merge(ele);
29904 existingReq.reqs++;
29905 q.updateItem(existingReq);
29906 } else {
29907 var req = {
29908 eles: ele.spawn().merge(ele),
29909 level: lvl,
29910 reqs: 1,
29911 key: key
29912 };
29913 q.push(req);
29914 k2q[key] = req;
29915 }
29916 };
29917
29918 ETCp.dequeue = function (pxRatio
29919 /*, extent*/
29920 ) {
29921 var self = this;
29922 var q = self.getElementQueue();
29923 var k2q = self.getElementKeyToQueue();
29924 var dequeued = [];
29925 var lookup = self.lookup;
29926
29927 for (var i = 0; i < maxDeqSize$1; i++) {
29928 if (q.size() > 0) {
29929 var req = q.pop();
29930 var key = req.key;
29931 var ele = req.eles[0]; // all eles have the same key
29932
29933 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
29934
29935 k2q[key] = null; // dequeueing isn't necessary with an existing cache
29936
29937 if (cacheExists) {
29938 continue;
29939 }
29940
29941 dequeued.push(req);
29942 var bb = self.getBoundingBox(ele);
29943 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
29944 } else {
29945 break;
29946 }
29947 }
29948
29949 return dequeued;
29950 };
29951
29952 ETCp.removeFromQueue = function (ele) {
29953 var self = this;
29954 var q = self.getElementQueue();
29955 var k2q = self.getElementKeyToQueue();
29956 var key = this.getKey(ele);
29957 var req = k2q[key];
29958
29959 if (req != null) {
29960 if (req.eles.length === 1) {
29961 // remove if last ele in the req
29962 // bring to front of queue
29963 req.reqs = MAX_INT$1;
29964 q.updateItem(req);
29965 q.pop(); // remove from queue
29966
29967 k2q[key] = null; // remove from lookup map
29968 } else {
29969 // otherwise just remove ele from req
29970 req.eles.unmerge(ele);
29971 }
29972 }
29973 };
29974
29975 ETCp.onDequeue = function (fn) {
29976 this.onDequeues.push(fn);
29977 };
29978
29979 ETCp.offDequeue = function (fn) {
29980 removeFromArray(this.onDequeues, fn);
29981 };
29982
29983 ETCp.setupDequeueing = defs.setupDequeueing({
29984 deqRedrawThreshold: deqRedrawThreshold$1,
29985 deqCost: deqCost$1,
29986 deqAvgCost: deqAvgCost$1,
29987 deqNoDrawCost: deqNoDrawCost$1,
29988 deqFastCost: deqFastCost$1,
29989 deq: function deq(self, pxRatio, extent) {
29990 return self.dequeue(pxRatio, extent);
29991 },
29992 onDeqd: function onDeqd(self, deqd) {
29993 for (var i = 0; i < self.onDequeues.length; i++) {
29994 var fn = self.onDequeues[i];
29995 fn(deqd);
29996 }
29997 },
29998 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
29999 for (var i = 0; i < deqd.length; i++) {
30000 var eles = deqd[i].eles;
30001
30002 for (var j = 0; j < eles.length; j++) {
30003 var bb = eles[j].boundingBox();
30004
30005 if (boundingBoxesIntersect(bb, extent)) {
30006 return true;
30007 }
30008 }
30009 }
30010
30011 return false;
30012 },
30013 priority: function priority(self) {
30014 return self.renderer.beforeRenderPriorities.eleTxrDeq;
30015 }
30016 });
30017
30018 var defNumLayers = 1; // default number of layers to use
30019
30020 var minLvl = -4; // when scaling smaller than that we don't need to re-render
30021
30022 var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful)
30023
30024 var maxZoom = 3.99; // beyond this zoom level, layered textures are not used
30025
30026 var deqRedrawThreshold = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
30027
30028 var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
30029
30030 var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
30031
30032 var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
30033
30034 var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
30035
30036 var deqFastCost = 0.9; // % of frame time to be used when >60fps
30037
30038 var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
30039
30040 var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
30041
30042 var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
30043
30044 var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
30045 // var log = function(){ console.log.apply( console, arguments ); };
30046
30047 var LayeredTextureCache = function LayeredTextureCache(renderer) {
30048 var self = this;
30049 var r = self.renderer = renderer;
30050 var cy = r.cy;
30051 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
30052
30053 self.firstGet = true;
30054 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
30055 self.skipping = false;
30056 self.eleTxrDeqs = cy.collection();
30057 self.scheduleElementRefinement = debounce_1(function () {
30058 self.refineElementTextures(self.eleTxrDeqs);
30059 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
30060 }, refineEleDebounceTime);
30061 r.beforeRender(function (willDraw, now) {
30062 if (now - self.lastInvalidationTime <= invalidThreshold) {
30063 self.skipping = true;
30064 } else {
30065 self.skipping = false;
30066 }
30067 }, r.beforeRenderPriorities.lyrTxrSkip);
30068
30069 var qSort = function qSort(a, b) {
30070 return b.reqs - a.reqs;
30071 };
30072
30073 self.layersQueue = new heap(qSort);
30074 self.setupDequeueing();
30075 };
30076
30077 var LTCp = LayeredTextureCache.prototype;
30078 var layerIdPool = 0;
30079 var MAX_INT = Math.pow(2, 53) - 1;
30080
30081 LTCp.makeLayer = function (bb, lvl) {
30082 var scale = Math.pow(2, lvl);
30083 var w = Math.ceil(bb.w * scale);
30084 var h = Math.ceil(bb.h * scale);
30085 var canvas = this.renderer.makeOffscreenCanvas(w, h);
30086 var layer = {
30087 id: layerIdPool = ++layerIdPool % MAX_INT,
30088 bb: bb,
30089 level: lvl,
30090 width: w,
30091 height: h,
30092 canvas: canvas,
30093 context: canvas.getContext('2d'),
30094 eles: [],
30095 elesQueue: [],
30096 reqs: 0
30097 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
30098
30099 var cxt = layer.context;
30100 var dx = -layer.bb.x1;
30101 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
30102
30103 cxt.scale(scale, scale);
30104 cxt.translate(dx, dy);
30105 return layer;
30106 };
30107
30108 LTCp.getLayers = function (eles, pxRatio, lvl) {
30109 var self = this;
30110 var r = self.renderer;
30111 var cy = r.cy;
30112 var zoom = cy.zoom();
30113 var firstGet = self.firstGet;
30114 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
30115 //log eles.map(function(ele){ return ele.id() }) );
30116
30117 if (lvl == null) {
30118 lvl = Math.ceil(log2(zoom * pxRatio));
30119
30120 if (lvl < minLvl) {
30121 lvl = minLvl;
30122 } else if (zoom >= maxZoom || lvl > maxLvl) {
30123 return null;
30124 }
30125 }
30126
30127 self.validateLayersElesOrdering(lvl, eles);
30128 var layersByLvl = self.layersByLevel;
30129 var scale = Math.pow(2, lvl);
30130 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
30131 var bb;
30132 var lvlComplete = self.levelIsComplete(lvl, eles);
30133 var tmpLayers;
30134
30135 var checkTempLevels = function checkTempLevels() {
30136 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
30137 self.validateLayersElesOrdering(l, eles);
30138
30139 if (self.levelIsComplete(l, eles)) {
30140 tmpLayers = layersByLvl[l];
30141 return true;
30142 }
30143 };
30144
30145 var checkLvls = function checkLvls(dir) {
30146 if (tmpLayers) {
30147 return;
30148 }
30149
30150 for (var l = lvl + dir; minLvl <= l && l <= maxLvl; l += dir) {
30151 if (canUseAsTmpLvl(l)) {
30152 break;
30153 }
30154 }
30155 };
30156
30157 checkLvls(+1);
30158 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
30159
30160 for (var i = layers.length - 1; i >= 0; i--) {
30161 var layer = layers[i];
30162
30163 if (layer.invalid) {
30164 removeFromArray(layers, layer);
30165 }
30166 }
30167 };
30168
30169 if (!lvlComplete) {
30170 // if the current level is incomplete, then use the closest, best quality layerset temporarily
30171 // and later queue the current layerset so we can get the proper quality level soon
30172 checkTempLevels();
30173 } else {
30174 // log('level complete, using existing layers\n--');
30175 return layers;
30176 }
30177
30178 var getBb = function getBb() {
30179 if (!bb) {
30180 bb = makeBoundingBox();
30181
30182 for (var i = 0; i < eles.length; i++) {
30183 updateBoundingBox(bb, eles[i].boundingBox());
30184 }
30185 }
30186
30187 return bb;
30188 };
30189
30190 var makeLayer = function makeLayer(opts) {
30191 opts = opts || {};
30192 var after = opts.after;
30193 getBb();
30194 var area = bb.w * scale * (bb.h * scale);
30195
30196 if (area > maxLayerArea) {
30197 return null;
30198 }
30199
30200 var layer = self.makeLayer(bb, lvl);
30201
30202 if (after != null) {
30203 var index = layers.indexOf(after) + 1;
30204 layers.splice(index, 0, layer);
30205 } else if (opts.insert === undefined || opts.insert) {
30206 // no after specified => first layer made so put at start
30207 layers.unshift(layer);
30208 } // if( tmpLayers ){
30209 //self.queueLayer( layer );
30210 // }
30211
30212
30213 return layer;
30214 };
30215
30216 if (self.skipping && !firstGet) {
30217 // log('skip layers');
30218 return null;
30219 } // log('do layers');
30220
30221
30222 var layer = null;
30223 var maxElesPerLayer = eles.length / defNumLayers;
30224 var allowLazyQueueing = !firstGet;
30225
30226 for (var i = 0; i < eles.length; i++) {
30227 var ele = eles[i];
30228 var rs = ele._private.rscratch;
30229 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
30230
30231 var existingLayer = caches[lvl];
30232
30233 if (existingLayer) {
30234 // reuse layer for later eles
30235 // log('reuse layer for', ele.id());
30236 layer = existingLayer;
30237 continue;
30238 }
30239
30240 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
30241 // log('make new layer for ele %s', ele.id());
30242 layer = makeLayer({
30243 insert: true,
30244 after: layer
30245 }); // if now layer can be built then we can't use layers at this level
30246
30247 if (!layer) {
30248 return null;
30249 } // log('new layer with id %s', layer.id);
30250
30251 }
30252
30253 if (tmpLayers || allowLazyQueueing) {
30254 // log('queue ele %s in layer %s', ele.id(), layer.id);
30255 self.queueLayer(layer, ele);
30256 } else {
30257 // log('draw ele %s in layer %s', ele.id(), layer.id);
30258 self.drawEleInLayer(layer, ele, lvl, pxRatio);
30259 }
30260
30261 layer.eles.push(ele);
30262 caches[lvl] = layer;
30263 } // log('--');
30264
30265
30266 if (tmpLayers) {
30267 // then we only queued the current layerset and can't draw it yet
30268 return tmpLayers;
30269 }
30270
30271 if (allowLazyQueueing) {
30272 // log('lazy queue level', lvl);
30273 return null;
30274 }
30275
30276 return layers;
30277 }; // a layer may want to use an ele cache of a higher level to avoid blurriness
30278 // so the layer level might not equal the ele level
30279
30280
30281 LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
30282 return lvl;
30283 };
30284
30285 LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
30286 var self = this;
30287 var r = this.renderer;
30288 var context = layer.context;
30289 var bb = ele.boundingBox();
30290
30291 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
30292 return;
30293 }
30294
30295 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
30296
30297 {
30298 r.setImgSmoothing(context, false);
30299 }
30300
30301 {
30302 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
30303 }
30304
30305 {
30306 r.setImgSmoothing(context, true);
30307 }
30308 };
30309
30310 LTCp.levelIsComplete = function (lvl, eles) {
30311 var self = this;
30312 var layers = self.layersByLevel[lvl];
30313
30314 if (!layers || layers.length === 0) {
30315 return false;
30316 }
30317
30318 var numElesInLayers = 0;
30319
30320 for (var i = 0; i < layers.length; i++) {
30321 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
30322
30323 if (layer.reqs > 0) {
30324 return false;
30325 } // if the layer is invalid, the level is not complete
30326
30327
30328 if (layer.invalid) {
30329 return false;
30330 }
30331
30332 numElesInLayers += layer.eles.length;
30333 } // we should have exactly the number of eles passed in to be complete
30334
30335
30336 if (numElesInLayers !== eles.length) {
30337 return false;
30338 }
30339
30340 return true;
30341 };
30342
30343 LTCp.validateLayersElesOrdering = function (lvl, eles) {
30344 var layers = this.layersByLevel[lvl];
30345
30346 if (!layers) {
30347 return;
30348 } // if in a layer the eles are not in the same order, then the layer is invalid
30349 // (i.e. there is an ele in between the eles in the layer)
30350
30351
30352 for (var i = 0; i < layers.length; i++) {
30353 var layer = layers[i];
30354 var offset = -1; // find the offset
30355
30356 for (var j = 0; j < eles.length; j++) {
30357 if (layer.eles[0] === eles[j]) {
30358 offset = j;
30359 break;
30360 }
30361 }
30362
30363 if (offset < 0) {
30364 // then the layer has nonexistant elements and is invalid
30365 this.invalidateLayer(layer);
30366 continue;
30367 } // the eles in the layer must be in the same continuous order, else the layer is invalid
30368
30369
30370 var o = offset;
30371
30372 for (var j = 0; j < layer.eles.length; j++) {
30373 if (layer.eles[j] !== eles[o + j]) {
30374 // log('invalidate based on ordering', layer.id);
30375 this.invalidateLayer(layer);
30376 break;
30377 }
30378 }
30379 }
30380 };
30381
30382 LTCp.updateElementsInLayers = function (eles, update) {
30383 var self = this;
30384 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
30385 // layer itself along the way
30386
30387 for (var i = 0; i < eles.length; i++) {
30388 var req = isEles ? null : eles[i];
30389 var ele = isEles ? eles[i] : eles[i].ele;
30390 var rs = ele._private.rscratch;
30391 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
30392
30393 for (var l = minLvl; l <= maxLvl; l++) {
30394 var layer = caches[l];
30395
30396 if (!layer) {
30397 continue;
30398 } // if update is a request from the ele cache, then it affects only
30399 // the matching level
30400
30401
30402 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
30403 continue;
30404 }
30405
30406 update(layer, ele, req);
30407 }
30408 }
30409 };
30410
30411 LTCp.haveLayers = function () {
30412 var self = this;
30413 var haveLayers = false;
30414
30415 for (var l = minLvl; l <= maxLvl; l++) {
30416 var layers = self.layersByLevel[l];
30417
30418 if (layers && layers.length > 0) {
30419 haveLayers = true;
30420 break;
30421 }
30422 }
30423
30424 return haveLayers;
30425 };
30426
30427 LTCp.invalidateElements = function (eles) {
30428 var self = this;
30429
30430 if (eles.length === 0) {
30431 return;
30432 }
30433
30434 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
30435
30436 if (eles.length === 0 || !self.haveLayers()) {
30437 return;
30438 }
30439
30440 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
30441 self.invalidateLayer(layer);
30442 });
30443 };
30444
30445 LTCp.invalidateLayer = function (layer) {
30446 // log('update invalidate layer time');
30447 this.lastInvalidationTime = performanceNow();
30448
30449 if (layer.invalid) {
30450 return;
30451 } // save cycles
30452
30453
30454 var lvl = layer.level;
30455 var eles = layer.eles;
30456 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
30457
30458 removeFromArray(layers, layer); // layer.eles = [];
30459
30460 layer.elesQueue = [];
30461 layer.invalid = true;
30462
30463 if (layer.replacement) {
30464 layer.replacement.invalid = true;
30465 }
30466
30467 for (var i = 0; i < eles.length; i++) {
30468 var caches = eles[i]._private.rscratch.imgLayerCaches;
30469
30470 if (caches) {
30471 caches[lvl] = null;
30472 }
30473 }
30474 };
30475
30476 LTCp.refineElementTextures = function (eles) {
30477 var self = this; // log('refine', eles.length);
30478
30479 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
30480 var rLyr = layer.replacement;
30481
30482 if (!rLyr) {
30483 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
30484 rLyr.replaces = layer;
30485 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
30486 }
30487
30488 if (!rLyr.reqs) {
30489 for (var i = 0; i < rLyr.eles.length; i++) {
30490 self.queueLayer(rLyr, rLyr.eles[i]);
30491 } // log('queue replacement layer refinement', rLyr.id);
30492
30493 }
30494 });
30495 };
30496
30497 LTCp.enqueueElementRefinement = function (ele) {
30498
30499 this.eleTxrDeqs.merge(ele);
30500 this.scheduleElementRefinement();
30501 };
30502
30503 LTCp.queueLayer = function (layer, ele) {
30504 var self = this;
30505 var q = self.layersQueue;
30506 var elesQ = layer.elesQueue;
30507 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
30508
30509 if (layer.replacement) {
30510 return;
30511 }
30512
30513 if (ele) {
30514 if (hasId[ele.id()]) {
30515 return;
30516 }
30517
30518 elesQ.push(ele);
30519 hasId[ele.id()] = true;
30520 }
30521
30522 if (layer.reqs) {
30523 layer.reqs++;
30524 q.updateItem(layer);
30525 } else {
30526 layer.reqs = 1;
30527 q.push(layer);
30528 }
30529 };
30530
30531 LTCp.dequeue = function (pxRatio) {
30532 var self = this;
30533 var q = self.layersQueue;
30534 var deqd = [];
30535 var eleDeqs = 0;
30536
30537 while (eleDeqs < maxDeqSize) {
30538 if (q.size() === 0) {
30539 break;
30540 }
30541
30542 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
30543
30544 if (layer.replacement) {
30545 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
30546 q.pop();
30547 continue;
30548 } // if this is a replacement layer that has been superceded, then forget it
30549
30550
30551 if (layer.replaces && layer !== layer.replaces.replacement) {
30552 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
30553 q.pop();
30554 continue;
30555 }
30556
30557 if (layer.invalid) {
30558 // log('replacement layer %s is invalid; dequeued', layer.id);
30559 q.pop();
30560 continue;
30561 }
30562
30563 var ele = layer.elesQueue.shift();
30564
30565 if (ele) {
30566 // log('dequeue layer %s', layer.id);
30567 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
30568 eleDeqs++;
30569 }
30570
30571 if (deqd.length === 0) {
30572 // we need only one entry in deqd to queue redrawing etc
30573 deqd.push(true);
30574 } // if the layer has all its eles done, then remove from the queue
30575
30576
30577 if (layer.elesQueue.length === 0) {
30578 q.pop();
30579 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
30580 // when a replacement layer is dequeued, it replaces the old layer in the level
30581
30582 if (layer.replaces) {
30583 self.applyLayerReplacement(layer);
30584 }
30585
30586 self.requestRedraw();
30587 }
30588 }
30589
30590 return deqd;
30591 };
30592
30593 LTCp.applyLayerReplacement = function (layer) {
30594 var self = this;
30595 var layersInLevel = self.layersByLevel[layer.level];
30596 var replaced = layer.replaces;
30597 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
30598 // refs would be a mistake (i.e. overwriting the true active layer)
30599
30600 if (index < 0 || replaced.invalid) {
30601 // log('replacement layer would have no effect', layer.id);
30602 return;
30603 }
30604
30605 layersInLevel[index] = layer; // replace level ref
30606 // replace refs in eles
30607
30608 for (var i = 0; i < layer.eles.length; i++) {
30609 var _p = layer.eles[i]._private;
30610 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
30611
30612 if (cache) {
30613 cache[layer.level] = layer;
30614 }
30615 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
30616
30617
30618 self.requestRedraw();
30619 };
30620
30621 LTCp.requestRedraw = debounce_1(function () {
30622 var r = this.renderer;
30623 r.redrawHint('eles', true);
30624 r.redrawHint('drag', true);
30625 r.redraw();
30626 }, 100);
30627 LTCp.setupDequeueing = defs.setupDequeueing({
30628 deqRedrawThreshold: deqRedrawThreshold,
30629 deqCost: deqCost,
30630 deqAvgCost: deqAvgCost,
30631 deqNoDrawCost: deqNoDrawCost,
30632 deqFastCost: deqFastCost,
30633 deq: function deq(self, pxRatio) {
30634 return self.dequeue(pxRatio);
30635 },
30636 onDeqd: noop$1,
30637 shouldRedraw: trueify,
30638 priority: function priority(self) {
30639 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
30640 }
30641 });
30642
30643 var CRp$a = {};
30644 var impl;
30645
30646 function polygon(context, points) {
30647 for (var i = 0; i < points.length; i++) {
30648 var pt = points[i];
30649 context.lineTo(pt.x, pt.y);
30650 }
30651 }
30652
30653 function triangleBackcurve(context, points, controlPoint) {
30654 var firstPt;
30655
30656 for (var i = 0; i < points.length; i++) {
30657 var pt = points[i];
30658
30659 if (i === 0) {
30660 firstPt = pt;
30661 }
30662
30663 context.lineTo(pt.x, pt.y);
30664 }
30665
30666 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
30667 }
30668
30669 function triangleTee(context, trianglePoints, teePoints) {
30670 if (context.beginPath) {
30671 context.beginPath();
30672 }
30673
30674 var triPts = trianglePoints;
30675
30676 for (var i = 0; i < triPts.length; i++) {
30677 var pt = triPts[i];
30678 context.lineTo(pt.x, pt.y);
30679 }
30680
30681 var teePts = teePoints;
30682 var firstTeePt = teePoints[0];
30683 context.moveTo(firstTeePt.x, firstTeePt.y);
30684
30685 for (var i = 1; i < teePts.length; i++) {
30686 var pt = teePts[i];
30687 context.lineTo(pt.x, pt.y);
30688 }
30689
30690 if (context.closePath) {
30691 context.closePath();
30692 }
30693 }
30694
30695 function circleTriangle(context, trianglePoints, rx, ry, r) {
30696 if (context.beginPath) {
30697 context.beginPath();
30698 }
30699
30700 context.arc(rx, ry, r, 0, Math.PI * 2, false);
30701 var triPts = trianglePoints;
30702 var firstTrPt = triPts[0];
30703 context.moveTo(firstTrPt.x, firstTrPt.y);
30704
30705 for (var i = 0; i < triPts.length; i++) {
30706 var pt = triPts[i];
30707 context.lineTo(pt.x, pt.y);
30708 }
30709
30710 if (context.closePath) {
30711 context.closePath();
30712 }
30713 }
30714
30715 function circle(context, rx, ry, r) {
30716 context.arc(rx, ry, r, 0, Math.PI * 2, false);
30717 }
30718
30719 CRp$a.arrowShapeImpl = function (name) {
30720 return (impl || (impl = {
30721 'polygon': polygon,
30722 'triangle-backcurve': triangleBackcurve,
30723 'triangle-tee': triangleTee,
30724 'circle-triangle': circleTriangle,
30725 'triangle-cross': triangleTee,
30726 'circle': circle
30727 }))[name];
30728 };
30729
30730 var CRp$9 = {};
30731
30732 CRp$9.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
30733 var r = this;
30734
30735 if (ele.isNode()) {
30736 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
30737 } else {
30738 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
30739 }
30740 };
30741
30742 CRp$9.drawElementOverlay = function (context, ele) {
30743 var r = this;
30744
30745 if (ele.isNode()) {
30746 r.drawNodeOverlay(context, ele);
30747 } else {
30748 r.drawEdgeOverlay(context, ele);
30749 }
30750 };
30751
30752 CRp$9.drawElementUnderlay = function (context, ele) {
30753 var r = this;
30754
30755 if (ele.isNode()) {
30756 r.drawNodeUnderlay(context, ele);
30757 } else {
30758 r.drawEdgeUnderlay(context, ele);
30759 }
30760 };
30761
30762 CRp$9.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
30763 var r = this;
30764 var bb = eleTxrCache.getBoundingBox(ele);
30765
30766 if (bb.w === 0 || bb.h === 0) {
30767 return;
30768 } // ignore zero size case
30769
30770
30771 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
30772
30773 if (eleCache != null) {
30774 var opacity = getOpacity(r, ele);
30775
30776 if (opacity === 0) {
30777 return;
30778 }
30779
30780 var theta = getRotation(r, ele);
30781 var x1 = bb.x1,
30782 y1 = bb.y1,
30783 w = bb.w,
30784 h = bb.h;
30785 var x, y, sx, sy, smooth;
30786
30787 if (theta !== 0) {
30788 var rotPt = eleTxrCache.getRotationPoint(ele);
30789 sx = rotPt.x;
30790 sy = rotPt.y;
30791 context.translate(sx, sy);
30792 context.rotate(theta);
30793 smooth = r.getImgSmoothing(context);
30794
30795 if (!smooth) {
30796 r.setImgSmoothing(context, true);
30797 }
30798
30799 var off = eleTxrCache.getRotationOffset(ele);
30800 x = off.x;
30801 y = off.y;
30802 } else {
30803 x = x1;
30804 y = y1;
30805 }
30806
30807 var oldGlobalAlpha;
30808
30809 if (opacity !== 1) {
30810 oldGlobalAlpha = context.globalAlpha;
30811 context.globalAlpha = oldGlobalAlpha * opacity;
30812 }
30813
30814 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
30815
30816 if (opacity !== 1) {
30817 context.globalAlpha = oldGlobalAlpha;
30818 }
30819
30820 if (theta !== 0) {
30821 context.rotate(-theta);
30822 context.translate(-sx, -sy);
30823
30824 if (!smooth) {
30825 r.setImgSmoothing(context, false);
30826 }
30827 }
30828 } else {
30829 eleTxrCache.drawElement(context, ele); // direct draw fallback
30830 }
30831 };
30832
30833 var getZeroRotation = function getZeroRotation() {
30834 return 0;
30835 };
30836
30837 var getLabelRotation = function getLabelRotation(r, ele) {
30838 return r.getTextAngle(ele, null);
30839 };
30840
30841 var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
30842 return r.getTextAngle(ele, 'source');
30843 };
30844
30845 var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
30846 return r.getTextAngle(ele, 'target');
30847 };
30848
30849 var getOpacity = function getOpacity(r, ele) {
30850 return ele.effectiveOpacity();
30851 };
30852
30853 var getTextOpacity = function getTextOpacity(e, ele) {
30854 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
30855 };
30856
30857 CRp$9.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
30858 var r = this;
30859 var _r$data = r.data,
30860 eleTxrCache = _r$data.eleTxrCache,
30861 lblTxrCache = _r$data.lblTxrCache,
30862 slbTxrCache = _r$data.slbTxrCache,
30863 tlbTxrCache = _r$data.tlbTxrCache;
30864 var bb = ele.boundingBox();
30865 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
30866
30867 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
30868 return;
30869 }
30870
30871 if (!extent || boundingBoxesIntersect(bb, extent)) {
30872 var isEdge = ele.isEdge();
30873
30874 var badLine = ele.element()._private.rscratch.badLine;
30875
30876 r.drawElementUnderlay(context, ele);
30877 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
30878
30879 if (!isEdge || !badLine) {
30880 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
30881 }
30882
30883 if (isEdge && !badLine) {
30884 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
30885 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
30886 }
30887
30888 r.drawElementOverlay(context, ele);
30889 }
30890 };
30891
30892 CRp$9.drawElements = function (context, eles) {
30893 var r = this;
30894
30895 for (var i = 0; i < eles.length; i++) {
30896 var ele = eles[i];
30897 r.drawElement(context, ele);
30898 }
30899 };
30900
30901 CRp$9.drawCachedElements = function (context, eles, pxRatio, extent) {
30902 var r = this;
30903
30904 for (var i = 0; i < eles.length; i++) {
30905 var ele = eles[i];
30906 r.drawCachedElement(context, ele, pxRatio, extent);
30907 }
30908 };
30909
30910 CRp$9.drawCachedNodes = function (context, eles, pxRatio, extent) {
30911 var r = this;
30912
30913 for (var i = 0; i < eles.length; i++) {
30914 var ele = eles[i];
30915
30916 if (!ele.isNode()) {
30917 continue;
30918 }
30919
30920 r.drawCachedElement(context, ele, pxRatio, extent);
30921 }
30922 };
30923
30924 CRp$9.drawLayeredElements = function (context, eles, pxRatio, extent) {
30925 var r = this;
30926 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
30927
30928 if (layers) {
30929 for (var i = 0; i < layers.length; i++) {
30930 var layer = layers[i];
30931 var bb = layer.bb;
30932
30933 if (bb.w === 0 || bb.h === 0) {
30934 continue;
30935 }
30936
30937 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
30938 }
30939 } else {
30940 // fall back on plain caching if no layers
30941 r.drawCachedElements(context, eles, pxRatio, extent);
30942 }
30943 };
30944
30945 /* global Path2D */
30946 var CRp$8 = {};
30947
30948 CRp$8.drawEdge = function (context, edge, shiftToOriginWithBb) {
30949 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
30950 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
30951 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
30952 var r = this;
30953 var rs = edge._private.rscratch;
30954
30955 if (shouldDrawOpacity && !edge.visible()) {
30956 return;
30957 } // if bezier ctrl pts can not be calculated, then die
30958
30959
30960 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
30961 // isNaN in case edge is impossible and browser bugs (e.g. safari)
30962 return;
30963 }
30964
30965 var bb;
30966
30967 if (shiftToOriginWithBb) {
30968 bb = shiftToOriginWithBb;
30969 context.translate(-bb.x1, -bb.y1);
30970 }
30971
30972 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
30973 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
30974 var curveStyle = edge.pstyle('curve-style').value;
30975 var lineStyle = edge.pstyle('line-style').value;
30976 var edgeWidth = edge.pstyle('width').pfValue;
30977 var lineCap = edge.pstyle('line-cap').value;
30978 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
30979
30980 var effectiveArrowOpacity = opacity * lineOpacity;
30981
30982 var drawLine = function drawLine() {
30983 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
30984
30985 if (curveStyle === 'straight-triangle') {
30986 r.eleStrokeStyle(context, edge, strokeOpacity);
30987 r.drawEdgeTrianglePath(edge, context, rs.allpts);
30988 } else {
30989 context.lineWidth = edgeWidth;
30990 context.lineCap = lineCap;
30991 r.eleStrokeStyle(context, edge, strokeOpacity);
30992 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
30993 context.lineCap = 'butt'; // reset for other drawing functions
30994 }
30995 };
30996
30997 var drawOverlay = function drawOverlay() {
30998 if (!shouldDrawOverlay) {
30999 return;
31000 }
31001
31002 r.drawEdgeOverlay(context, edge);
31003 };
31004
31005 var drawUnderlay = function drawUnderlay() {
31006 if (!shouldDrawOverlay) {
31007 return;
31008 }
31009
31010 r.drawEdgeUnderlay(context, edge);
31011 };
31012
31013 var drawArrows = function drawArrows() {
31014 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
31015 r.drawArrowheads(context, edge, arrowOpacity);
31016 };
31017
31018 var drawText = function drawText() {
31019 r.drawElementText(context, edge, null, drawLabel);
31020 };
31021
31022 context.lineJoin = 'round';
31023 var ghost = edge.pstyle('ghost').value === 'yes';
31024
31025 if (ghost) {
31026 var gx = edge.pstyle('ghost-offset-x').pfValue;
31027 var gy = edge.pstyle('ghost-offset-y').pfValue;
31028 var ghostOpacity = edge.pstyle('ghost-opacity').value;
31029 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
31030 context.translate(gx, gy);
31031 drawLine(effectiveGhostOpacity);
31032 drawArrows(effectiveGhostOpacity);
31033 context.translate(-gx, -gy);
31034 }
31035
31036 drawUnderlay();
31037 drawLine();
31038 drawArrows();
31039 drawOverlay();
31040 drawText();
31041
31042 if (shiftToOriginWithBb) {
31043 context.translate(bb.x1, bb.y1);
31044 }
31045 };
31046
31047 var drawEdgeOverlayUnderlay = function drawEdgeOverlayUnderlay(overlayOrUnderlay) {
31048 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
31049 throw new Error('Invalid state');
31050 }
31051
31052 return function (context, edge) {
31053 if (!edge.visible()) {
31054 return;
31055 }
31056
31057 var opacity = edge.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
31058
31059 if (opacity === 0) {
31060 return;
31061 }
31062
31063 var r = this;
31064 var usePaths = r.usePaths();
31065 var rs = edge._private.rscratch;
31066 var padding = edge.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
31067 var width = 2 * padding;
31068 var color = edge.pstyle("".concat(overlayOrUnderlay, "-color")).value;
31069 context.lineWidth = width;
31070
31071 if (rs.edgeType === 'self' && !usePaths) {
31072 context.lineCap = 'butt';
31073 } else {
31074 context.lineCap = 'round';
31075 }
31076
31077 r.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
31078 r.drawEdgePath(edge, context, rs.allpts, 'solid');
31079 };
31080 };
31081
31082 CRp$8.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
31083 CRp$8.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
31084
31085 CRp$8.drawEdgePath = function (edge, context, pts, type) {
31086 var rs = edge._private.rscratch;
31087 var canvasCxt = context;
31088 var path;
31089 var pathCacheHit = false;
31090 var usePaths = this.usePaths();
31091 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
31092 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
31093
31094 if (usePaths) {
31095 var pathCacheKey = pts.join('$');
31096 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
31097
31098 if (keyMatches) {
31099 path = context = rs.pathCache;
31100 pathCacheHit = true;
31101 } else {
31102 path = context = new Path2D();
31103 rs.pathCacheKey = pathCacheKey;
31104 rs.pathCache = path;
31105 }
31106 }
31107
31108 if (canvasCxt.setLineDash) {
31109 // for very outofdate browsers
31110 switch (type) {
31111 case 'dotted':
31112 canvasCxt.setLineDash([1, 1]);
31113 break;
31114
31115 case 'dashed':
31116 canvasCxt.setLineDash(lineDashPattern);
31117 canvasCxt.lineDashOffset = lineDashOffset;
31118 break;
31119
31120 case 'solid':
31121 canvasCxt.setLineDash([]);
31122 break;
31123 }
31124 }
31125
31126 if (!pathCacheHit && !rs.badLine) {
31127 if (context.beginPath) {
31128 context.beginPath();
31129 }
31130
31131 context.moveTo(pts[0], pts[1]);
31132
31133 switch (rs.edgeType) {
31134 case 'bezier':
31135 case 'self':
31136 case 'compound':
31137 case 'multibezier':
31138 for (var i = 2; i + 3 < pts.length; i += 4) {
31139 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
31140 }
31141
31142 break;
31143
31144 case 'straight':
31145 case 'segments':
31146 case 'haystack':
31147 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
31148 context.lineTo(pts[_i], pts[_i + 1]);
31149 }
31150
31151 break;
31152 }
31153 }
31154
31155 context = canvasCxt;
31156
31157 if (usePaths) {
31158 context.stroke(path);
31159 } else {
31160 context.stroke();
31161 } // reset any line dashes
31162
31163
31164 if (context.setLineDash) {
31165 // for very outofdate browsers
31166 context.setLineDash([]);
31167 }
31168 };
31169
31170 CRp$8.drawEdgeTrianglePath = function (edge, context, pts) {
31171 // use line stroke style for triangle fill style
31172 context.fillStyle = context.strokeStyle;
31173 var edgeWidth = edge.pstyle('width').pfValue;
31174
31175 for (var i = 0; i + 1 < pts.length; i += 2) {
31176 var vector = [pts[i + 2] - pts[i], pts[i + 3] - pts[i + 1]];
31177 var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
31178 var normal = [vector[1] / length, -vector[0] / length];
31179 var triangleHead = [normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2];
31180 context.beginPath();
31181 context.moveTo(pts[i] - triangleHead[0], pts[i + 1] - triangleHead[1]);
31182 context.lineTo(pts[i] + triangleHead[0], pts[i + 1] + triangleHead[1]);
31183 context.lineTo(pts[i + 2], pts[i + 3]);
31184 context.closePath();
31185 context.fill();
31186 }
31187 };
31188
31189 CRp$8.drawArrowheads = function (context, edge, opacity) {
31190 var rs = edge._private.rscratch;
31191 var isHaystack = rs.edgeType === 'haystack';
31192
31193 if (!isHaystack) {
31194 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
31195 }
31196
31197 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
31198 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
31199
31200 if (!isHaystack) {
31201 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
31202 }
31203 };
31204
31205 CRp$8.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
31206 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
31207 return;
31208 }
31209
31210 var self = this;
31211 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
31212
31213 if (arrowShape === 'none') {
31214 return;
31215 }
31216
31217 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
31218 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
31219 var edgeWidth = edge.pstyle('width').pfValue;
31220 var edgeOpacity = edge.pstyle('opacity').value;
31221
31222 if (opacity === undefined) {
31223 opacity = edgeOpacity;
31224 }
31225
31226 var gco = context.globalCompositeOperation;
31227
31228 if (opacity !== 1 || arrowFill === 'hollow') {
31229 // then extra clear is needed
31230 context.globalCompositeOperation = 'destination-out';
31231 self.colorFillStyle(context, 255, 255, 255, 1);
31232 self.colorStrokeStyle(context, 255, 255, 255, 1);
31233 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
31234 context.globalCompositeOperation = gco;
31235 } // otherwise, the opaque arrow clears it for free :)
31236
31237
31238 var color = edge.pstyle(prefix + '-arrow-color').value;
31239 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
31240 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
31241 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
31242 };
31243
31244 CRp$8.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
31245 var r = this;
31246 var usePaths = this.usePaths() && shape !== 'triangle-cross';
31247 var pathCacheHit = false;
31248 var path;
31249 var canvasContext = context;
31250 var translation = {
31251 x: x,
31252 y: y
31253 };
31254 var scale = edge.pstyle('arrow-scale').value;
31255 var size = this.getArrowWidth(edgeWidth, scale);
31256 var shapeImpl = r.arrowShapes[shape];
31257
31258 if (usePaths) {
31259 var cache = r.arrowPathCache = r.arrowPathCache || [];
31260 var key = hashString(shape);
31261 var cachedPath = cache[key];
31262
31263 if (cachedPath != null) {
31264 path = context = cachedPath;
31265 pathCacheHit = true;
31266 } else {
31267 path = context = new Path2D();
31268 cache[key] = path;
31269 }
31270 }
31271
31272 if (!pathCacheHit) {
31273 if (context.beginPath) {
31274 context.beginPath();
31275 }
31276
31277 if (usePaths) {
31278 // store in the path cache with values easily manipulated later
31279 shapeImpl.draw(context, 1, 0, {
31280 x: 0,
31281 y: 0
31282 }, 1);
31283 } else {
31284 shapeImpl.draw(context, size, angle, translation, edgeWidth);
31285 }
31286
31287 if (context.closePath) {
31288 context.closePath();
31289 }
31290 }
31291
31292 context = canvasContext;
31293
31294 if (usePaths) {
31295 // set transform to arrow position/orientation
31296 context.translate(x, y);
31297 context.rotate(angle);
31298 context.scale(size, size);
31299 }
31300
31301 if (fill === 'filled' || fill === 'both') {
31302 if (usePaths) {
31303 context.fill(path);
31304 } else {
31305 context.fill();
31306 }
31307 }
31308
31309 if (fill === 'hollow' || fill === 'both') {
31310 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
31311 context.lineJoin = 'miter';
31312
31313 if (usePaths) {
31314 context.stroke(path);
31315 } else {
31316 context.stroke();
31317 }
31318 }
31319
31320 if (usePaths) {
31321 // reset transform by applying inverse
31322 context.scale(1 / size, 1 / size);
31323 context.rotate(-angle);
31324 context.translate(-x, -y);
31325 }
31326 };
31327
31328 var CRp$7 = {};
31329
31330 CRp$7.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
31331 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
31332 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
31333 return;
31334 }
31335
31336 try {
31337 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
31338 } catch (e) {
31339 warn(e);
31340 }
31341 };
31342
31343 CRp$7.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
31344 var r = this;
31345 var pos = node.position();
31346 var nodeX = pos.x;
31347 var nodeY = pos.y;
31348 var styleObj = node.cy().style();
31349 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
31350 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
31351 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
31352 var nodeW = node.width();
31353 var nodeH = node.height();
31354 var paddingX2 = node.padding() * 2;
31355 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
31356 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
31357 var rs = node._private.rscratch;
31358 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
31359 var shouldClip = clip === 'node';
31360 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
31361 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
31362 var imgW = img.width || img.cachedW;
31363 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
31364
31365 if (null == imgW || null == imgH) {
31366 document.body.appendChild(img); // eslint-disable-line no-undef
31367
31368 imgW = img.cachedW = img.width || img.offsetWidth;
31369 imgH = img.cachedH = img.height || img.offsetHeight;
31370 document.body.removeChild(img); // eslint-disable-line no-undef
31371 }
31372
31373 var w = imgW;
31374 var h = imgH;
31375
31376 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
31377 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
31378 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
31379 } else {
31380 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
31381 }
31382 }
31383
31384 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
31385 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
31386 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
31387 } else {
31388 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
31389 }
31390 }
31391
31392 if (w === 0 || h === 0) {
31393 return; // no point in drawing empty image (and chrome is broken in this case)
31394 }
31395
31396 if (fit === 'contain') {
31397 var scale = Math.min(nodeTW / w, nodeTH / h);
31398 w *= scale;
31399 h *= scale;
31400 } else if (fit === 'cover') {
31401 var scale = Math.max(nodeTW / w, nodeTH / h);
31402 w *= scale;
31403 h *= scale;
31404 }
31405
31406 var x = nodeX - nodeTW / 2; // left
31407
31408 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
31409 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
31410
31411 if (posXUnits === '%') {
31412 x += (nodeTW - w) * posXPfVal;
31413 } else {
31414 x += posXPfVal;
31415 }
31416
31417 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
31418 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
31419
31420 if (offXUnits === '%') {
31421 x += (nodeTW - w) * offXPfVal;
31422 } else {
31423 x += offXPfVal;
31424 }
31425
31426 var y = nodeY - nodeTH / 2; // top
31427
31428 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
31429 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
31430
31431 if (posYUnits === '%') {
31432 y += (nodeTH - h) * posYPfVal;
31433 } else {
31434 y += posYPfVal;
31435 }
31436
31437 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
31438 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
31439
31440 if (offYUnits === '%') {
31441 y += (nodeTH - h) * offYPfVal;
31442 } else {
31443 y += offYPfVal;
31444 }
31445
31446 if (rs.pathCache) {
31447 x -= nodeX;
31448 y -= nodeY;
31449 nodeX = 0;
31450 nodeY = 0;
31451 }
31452
31453 var gAlpha = context.globalAlpha;
31454 context.globalAlpha = imgOpacity;
31455 var smoothingEnabled = r.getImgSmoothing(context);
31456 var isSmoothingSwitched = false;
31457
31458 if (smooth === 'no' && smoothingEnabled) {
31459 r.setImgSmoothing(context, false);
31460 isSmoothingSwitched = true;
31461 } else if (smooth === 'yes' && !smoothingEnabled) {
31462 r.setImgSmoothing(context, true);
31463 isSmoothingSwitched = true;
31464 }
31465
31466 if (repeat === 'no-repeat') {
31467 if (shouldClip) {
31468 context.save();
31469
31470 if (rs.pathCache) {
31471 context.clip(rs.pathCache);
31472 } else {
31473 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
31474 context.clip();
31475 }
31476 }
31477
31478 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
31479
31480 if (shouldClip) {
31481 context.restore();
31482 }
31483 } else {
31484 var pattern = context.createPattern(img, repeat);
31485 context.fillStyle = pattern;
31486 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
31487 context.translate(x, y);
31488 context.fill();
31489 context.translate(-x, -y);
31490 }
31491
31492 context.globalAlpha = gAlpha;
31493
31494 if (isSmoothingSwitched) {
31495 r.setImgSmoothing(context, smoothingEnabled);
31496 }
31497 };
31498
31499 var CRp$6 = {};
31500
31501 CRp$6.eleTextBiggerThanMin = function (ele, scale) {
31502 if (!scale) {
31503 var zoom = ele.cy().zoom();
31504 var pxRatio = this.getPixelRatio();
31505 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
31506
31507 scale = Math.pow(2, lvl);
31508 }
31509
31510 var computedSize = ele.pstyle('font-size').pfValue * scale;
31511 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
31512
31513 if (computedSize < minSize) {
31514 return false;
31515 }
31516
31517 return true;
31518 };
31519
31520 CRp$6.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
31521 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
31522 var r = this;
31523
31524 if (force == null) {
31525 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
31526 return;
31527 }
31528 } else if (force === false) {
31529 return;
31530 }
31531
31532 if (ele.isNode()) {
31533 var label = ele.pstyle('label');
31534
31535 if (!label || !label.value) {
31536 return;
31537 }
31538
31539 var justification = r.getLabelJustification(ele);
31540 context.textAlign = justification;
31541 context.textBaseline = 'bottom';
31542 } else {
31543 var badLine = ele.element()._private.rscratch.badLine;
31544
31545 var _label = ele.pstyle('label');
31546
31547 var srcLabel = ele.pstyle('source-label');
31548 var tgtLabel = ele.pstyle('target-label');
31549
31550 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
31551 return;
31552 }
31553
31554 context.textAlign = 'center';
31555 context.textBaseline = 'bottom';
31556 }
31557
31558 var applyRotation = !shiftToOriginWithBb;
31559 var bb;
31560
31561 if (shiftToOriginWithBb) {
31562 bb = shiftToOriginWithBb;
31563 context.translate(-bb.x1, -bb.y1);
31564 }
31565
31566 if (prefix == null) {
31567 r.drawText(context, ele, null, applyRotation, useEleOpacity);
31568
31569 if (ele.isEdge()) {
31570 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
31571 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
31572 }
31573 } else {
31574 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
31575 }
31576
31577 if (shiftToOriginWithBb) {
31578 context.translate(bb.x1, bb.y1);
31579 }
31580 };
31581
31582 CRp$6.getFontCache = function (context) {
31583 var cache;
31584 this.fontCaches = this.fontCaches || [];
31585
31586 for (var i = 0; i < this.fontCaches.length; i++) {
31587 cache = this.fontCaches[i];
31588
31589 if (cache.context === context) {
31590 return cache;
31591 }
31592 }
31593
31594 cache = {
31595 context: context
31596 };
31597 this.fontCaches.push(cache);
31598 return cache;
31599 }; // set up canvas context with font
31600 // returns transformed text string
31601
31602
31603 CRp$6.setupTextStyle = function (context, ele) {
31604 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
31605 // Font style
31606 var labelStyle = ele.pstyle('font-style').strValue;
31607 var labelSize = ele.pstyle('font-size').pfValue + 'px';
31608 var labelFamily = ele.pstyle('font-family').strValue;
31609 var labelWeight = ele.pstyle('font-weight').strValue;
31610 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
31611 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
31612 var color = ele.pstyle('color').value;
31613 var outlineColor = ele.pstyle('text-outline-color').value;
31614 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
31615 context.lineJoin = 'round'; // so text outlines aren't jagged
31616
31617 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
31618 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
31619 }; // TODO ensure re-used
31620
31621
31622 function roundRect(ctx, x, y, width, height) {
31623 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
31624 ctx.beginPath();
31625 ctx.moveTo(x + radius, y);
31626 ctx.lineTo(x + width - radius, y);
31627 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
31628 ctx.lineTo(x + width, y + height - radius);
31629 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
31630 ctx.lineTo(x + radius, y + height);
31631 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
31632 ctx.lineTo(x, y + radius);
31633 ctx.quadraticCurveTo(x, y, x + radius, y);
31634 ctx.closePath();
31635 ctx.fill();
31636 }
31637
31638 CRp$6.getTextAngle = function (ele, prefix) {
31639 var theta;
31640 var _p = ele._private;
31641 var rscratch = _p.rscratch;
31642 var pdash = prefix ? prefix + '-' : '';
31643 var rotation = ele.pstyle(pdash + 'text-rotation');
31644 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
31645
31646 if (rotation.strValue === 'autorotate') {
31647 theta = ele.isEdge() ? textAngle : 0;
31648 } else if (rotation.strValue === 'none') {
31649 theta = 0;
31650 } else {
31651 theta = rotation.pfValue;
31652 }
31653
31654 return theta;
31655 };
31656
31657 CRp$6.drawText = function (context, ele, prefix) {
31658 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
31659 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
31660 var _p = ele._private;
31661 var rscratch = _p.rscratch;
31662 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
31663
31664 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
31665 return;
31666 } // use 'main' as an alias for the main label (i.e. null prefix)
31667
31668
31669 if (prefix === 'main') {
31670 prefix = null;
31671 }
31672
31673 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
31674 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
31675 var orgTextX, orgTextY; // used for rotation
31676
31677 var text = this.getLabelText(ele, prefix);
31678
31679 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
31680 this.setupTextStyle(context, ele, useEleOpacity);
31681 var pdash = prefix ? prefix + '-' : '';
31682 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
31683 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
31684 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
31685 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
31686 var isEdge = ele.isEdge();
31687 var halign = ele.pstyle('text-halign').value;
31688 var valign = ele.pstyle('text-valign').value;
31689
31690 if (isEdge) {
31691 halign = 'center';
31692 valign = 'center';
31693 }
31694
31695 textX += marginX;
31696 textY += marginY;
31697 var theta;
31698
31699 if (!applyRotation) {
31700 theta = 0;
31701 } else {
31702 theta = this.getTextAngle(ele, prefix);
31703 }
31704
31705 if (theta !== 0) {
31706 orgTextX = textX;
31707 orgTextY = textY;
31708 context.translate(orgTextX, orgTextY);
31709 context.rotate(theta);
31710 textX = 0;
31711 textY = 0;
31712 }
31713
31714 switch (valign) {
31715 case 'top':
31716 break;
31717
31718 case 'center':
31719 textY += textH / 2;
31720 break;
31721
31722 case 'bottom':
31723 textY += textH;
31724 break;
31725 }
31726
31727 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
31728 var borderOpacity = ele.pstyle('text-border-opacity').value;
31729 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
31730 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
31731
31732 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
31733 var bgX = textX - backgroundPadding;
31734
31735 switch (halign) {
31736 case 'left':
31737 bgX -= textW;
31738 break;
31739
31740 case 'center':
31741 bgX -= textW / 2;
31742 break;
31743 }
31744
31745 var bgY = textY - textH - backgroundPadding;
31746 var bgW = textW + 2 * backgroundPadding;
31747 var bgH = textH + 2 * backgroundPadding;
31748
31749 if (backgroundOpacity > 0) {
31750 var textFill = context.fillStyle;
31751 var textBackgroundColor = ele.pstyle('text-background-color').value;
31752 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
31753 var styleShape = ele.pstyle('text-background-shape').strValue;
31754
31755 if (styleShape.indexOf('round') === 0) {
31756 roundRect(context, bgX, bgY, bgW, bgH, 2);
31757 } else {
31758 context.fillRect(bgX, bgY, bgW, bgH);
31759 }
31760
31761 context.fillStyle = textFill;
31762 }
31763
31764 if (textBorderWidth > 0 && borderOpacity > 0) {
31765 var textStroke = context.strokeStyle;
31766 var textLineWidth = context.lineWidth;
31767 var textBorderColor = ele.pstyle('text-border-color').value;
31768 var textBorderStyle = ele.pstyle('text-border-style').value;
31769 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
31770 context.lineWidth = textBorderWidth;
31771
31772 if (context.setLineDash) {
31773 // for very outofdate browsers
31774 switch (textBorderStyle) {
31775 case 'dotted':
31776 context.setLineDash([1, 1]);
31777 break;
31778
31779 case 'dashed':
31780 context.setLineDash([4, 2]);
31781 break;
31782
31783 case 'double':
31784 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
31785
31786 context.setLineDash([]);
31787 break;
31788
31789 case 'solid':
31790 context.setLineDash([]);
31791 break;
31792 }
31793 }
31794
31795 context.strokeRect(bgX, bgY, bgW, bgH);
31796
31797 if (textBorderStyle === 'double') {
31798 var whiteWidth = textBorderWidth / 2;
31799 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
31800 }
31801
31802 if (context.setLineDash) {
31803 // for very outofdate browsers
31804 context.setLineDash([]);
31805 }
31806
31807 context.lineWidth = textLineWidth;
31808 context.strokeStyle = textStroke;
31809 }
31810 }
31811
31812 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
31813
31814 if (lineWidth > 0) {
31815 context.lineWidth = lineWidth;
31816 }
31817
31818 if (ele.pstyle('text-wrap').value === 'wrap') {
31819 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
31820 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
31821 var halfTextW = textW / 2;
31822 var justification = this.getLabelJustification(ele);
31823
31824 if (justification === 'auto') ; else if (halign === 'left') {
31825 // auto justification : right
31826 if (justification === 'left') {
31827 textX += -textW;
31828 } else if (justification === 'center') {
31829 textX += -halfTextW;
31830 } // else same as auto
31831
31832 } else if (halign === 'center') {
31833 // auto justfication : center
31834 if (justification === 'left') {
31835 textX += -halfTextW;
31836 } else if (justification === 'right') {
31837 textX += halfTextW;
31838 } // else same as auto
31839
31840 } else if (halign === 'right') {
31841 // auto justification : left
31842 if (justification === 'center') {
31843 textX += halfTextW;
31844 } else if (justification === 'right') {
31845 textX += textW;
31846 } // else same as auto
31847
31848 }
31849
31850 switch (valign) {
31851 case 'top':
31852 textY -= (lines.length - 1) * lineHeight;
31853 break;
31854
31855 case 'center':
31856 case 'bottom':
31857 textY -= (lines.length - 1) * lineHeight;
31858 break;
31859 }
31860
31861 for (var l = 0; l < lines.length; l++) {
31862 if (lineWidth > 0) {
31863 context.strokeText(lines[l], textX, textY);
31864 }
31865
31866 context.fillText(lines[l], textX, textY);
31867 textY += lineHeight;
31868 }
31869 } else {
31870 if (lineWidth > 0) {
31871 context.strokeText(text, textX, textY);
31872 }
31873
31874 context.fillText(text, textX, textY);
31875 }
31876
31877 if (theta !== 0) {
31878 context.rotate(-theta);
31879 context.translate(-orgTextX, -orgTextY);
31880 }
31881 }
31882 };
31883
31884 /* global Path2D */
31885 var CRp$5 = {};
31886
31887 CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
31888 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
31889 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
31890 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
31891 var r = this;
31892 var nodeWidth, nodeHeight;
31893 var _p = node._private;
31894 var rs = _p.rscratch;
31895 var pos = node.position();
31896
31897 if (!number$1(pos.x) || !number$1(pos.y)) {
31898 return; // can't draw node with undefined position
31899 }
31900
31901 if (shouldDrawOpacity && !node.visible()) {
31902 return;
31903 }
31904
31905 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
31906 var usePaths = r.usePaths();
31907 var path;
31908 var pathCacheHit = false;
31909 var padding = node.padding();
31910 nodeWidth = node.width() + 2 * padding;
31911 nodeHeight = node.height() + 2 * padding; //
31912 // setup shift
31913
31914 var bb;
31915
31916 if (shiftToOriginWithBb) {
31917 bb = shiftToOriginWithBb;
31918 context.translate(-bb.x1, -bb.y1);
31919 } //
31920 // load bg image
31921
31922
31923 var bgImgProp = node.pstyle('background-image');
31924 var urls = bgImgProp.value;
31925 var urlDefined = new Array(urls.length);
31926 var image = new Array(urls.length);
31927 var numImages = 0;
31928
31929 for (var i = 0; i < urls.length; i++) {
31930 var url = urls[i];
31931 var defd = urlDefined[i] = url != null && url !== 'none';
31932
31933 if (defd) {
31934 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
31935 numImages++; // get image, and if not loaded then ask to redraw when later loaded
31936
31937 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
31938 _p.backgroundTimestamp = Date.now();
31939 node.emitAndNotify('background');
31940 });
31941 }
31942 } //
31943 // setup styles
31944
31945
31946 var darkness = node.pstyle('background-blacken').value;
31947 var borderWidth = node.pstyle('border-width').pfValue;
31948 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
31949 var borderColor = node.pstyle('border-color').value;
31950 var borderStyle = node.pstyle('border-style').value;
31951 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
31952 context.lineJoin = 'miter'; // so borders are square with the node shape
31953
31954 var setupShapeColor = function setupShapeColor() {
31955 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
31956 r.eleFillStyle(context, node, bgOpy);
31957 };
31958
31959 var setupBorderColor = function setupBorderColor() {
31960 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
31961 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
31962 }; //
31963 // setup shape
31964
31965
31966 var styleShape = node.pstyle('shape').strValue;
31967 var shapePts = node.pstyle('shape-polygon-points').pfValue;
31968
31969 if (usePaths) {
31970 context.translate(pos.x, pos.y);
31971 var pathCache = r.nodePathCache = r.nodePathCache || [];
31972 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
31973 var cachedPath = pathCache[key];
31974
31975 if (cachedPath != null) {
31976 path = cachedPath;
31977 pathCacheHit = true;
31978 rs.pathCache = path;
31979 } else {
31980 path = new Path2D();
31981 pathCache[key] = rs.pathCache = path;
31982 }
31983 }
31984
31985 var drawShape = function drawShape() {
31986 if (!pathCacheHit) {
31987 var npos = pos;
31988
31989 if (usePaths) {
31990 npos = {
31991 x: 0,
31992 y: 0
31993 };
31994 }
31995
31996 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
31997 }
31998
31999 if (usePaths) {
32000 context.fill(path);
32001 } else {
32002 context.fill();
32003 }
32004 };
32005
32006 var drawImages = function drawImages() {
32007 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
32008 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
32009 var prevBging = _p.backgrounding;
32010 var totalCompleted = 0;
32011
32012 for (var _i = 0; _i < image.length; _i++) {
32013 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
32014
32015 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
32016 totalCompleted++;
32017 continue;
32018 }
32019
32020 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
32021 totalCompleted++;
32022 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
32023 }
32024 }
32025
32026 _p.backgrounding = !(totalCompleted === numImages);
32027
32028 if (prevBging !== _p.backgrounding) {
32029 // update style b/c :backgrounding state changed
32030 node.updateStyle(false);
32031 }
32032 };
32033
32034 var drawPie = function drawPie() {
32035 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
32036 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
32037
32038 if (r.hasPie(node)) {
32039 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
32040
32041 if (redrawShape) {
32042 if (!usePaths) {
32043 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
32044 }
32045 }
32046 }
32047 };
32048
32049 var darken = function darken() {
32050 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
32051 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
32052 var c = darkness > 0 ? 0 : 255;
32053
32054 if (darkness !== 0) {
32055 r.colorFillStyle(context, c, c, c, opacity);
32056
32057 if (usePaths) {
32058 context.fill(path);
32059 } else {
32060 context.fill();
32061 }
32062 }
32063 };
32064
32065 var drawBorder = function drawBorder() {
32066 if (borderWidth > 0) {
32067 context.lineWidth = borderWidth;
32068 context.lineCap = 'butt';
32069
32070 if (context.setLineDash) {
32071 // for very outofdate browsers
32072 switch (borderStyle) {
32073 case 'dotted':
32074 context.setLineDash([1, 1]);
32075 break;
32076
32077 case 'dashed':
32078 context.setLineDash([4, 2]);
32079 break;
32080
32081 case 'solid':
32082 case 'double':
32083 context.setLineDash([]);
32084 break;
32085 }
32086 }
32087
32088 if (usePaths) {
32089 context.stroke(path);
32090 } else {
32091 context.stroke();
32092 }
32093
32094 if (borderStyle === 'double') {
32095 context.lineWidth = borderWidth / 3;
32096 var gco = context.globalCompositeOperation;
32097 context.globalCompositeOperation = 'destination-out';
32098
32099 if (usePaths) {
32100 context.stroke(path);
32101 } else {
32102 context.stroke();
32103 }
32104
32105 context.globalCompositeOperation = gco;
32106 } // reset in case we changed the border style
32107
32108
32109 if (context.setLineDash) {
32110 // for very outofdate browsers
32111 context.setLineDash([]);
32112 }
32113 }
32114 };
32115
32116 var drawOverlay = function drawOverlay() {
32117 if (shouldDrawOverlay) {
32118 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
32119 }
32120 };
32121
32122 var drawUnderlay = function drawUnderlay() {
32123 if (shouldDrawOverlay) {
32124 r.drawNodeUnderlay(context, node, pos, nodeWidth, nodeHeight);
32125 }
32126 };
32127
32128 var drawText = function drawText() {
32129 r.drawElementText(context, node, null, drawLabel);
32130 };
32131
32132 var ghost = node.pstyle('ghost').value === 'yes';
32133
32134 if (ghost) {
32135 var gx = node.pstyle('ghost-offset-x').pfValue;
32136 var gy = node.pstyle('ghost-offset-y').pfValue;
32137 var ghostOpacity = node.pstyle('ghost-opacity').value;
32138 var effGhostOpacity = ghostOpacity * eleOpacity;
32139 context.translate(gx, gy);
32140 setupShapeColor(ghostOpacity * bgOpacity);
32141 drawShape();
32142 drawImages(effGhostOpacity, true);
32143 setupBorderColor(ghostOpacity * borderOpacity);
32144 drawBorder();
32145 drawPie(darkness !== 0 || borderWidth !== 0);
32146 drawImages(effGhostOpacity, false);
32147 darken(effGhostOpacity);
32148 context.translate(-gx, -gy);
32149 }
32150
32151 if (usePaths) {
32152 context.translate(-pos.x, -pos.y);
32153 }
32154
32155 drawUnderlay();
32156
32157 if (usePaths) {
32158 context.translate(pos.x, pos.y);
32159 }
32160
32161 setupShapeColor();
32162 drawShape();
32163 drawImages(eleOpacity, true);
32164 setupBorderColor();
32165 drawBorder();
32166 drawPie(darkness !== 0 || borderWidth !== 0);
32167 drawImages(eleOpacity, false);
32168 darken();
32169
32170 if (usePaths) {
32171 context.translate(-pos.x, -pos.y);
32172 }
32173
32174 drawText();
32175 drawOverlay(); //
32176 // clean up shift
32177
32178 if (shiftToOriginWithBb) {
32179 context.translate(bb.x1, bb.y1);
32180 }
32181 };
32182
32183 var drawNodeOverlayUnderlay = function drawNodeOverlayUnderlay(overlayOrUnderlay) {
32184 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
32185 throw new Error('Invalid state');
32186 }
32187
32188 return function (context, node, pos, nodeWidth, nodeHeight) {
32189 var r = this;
32190
32191 if (!node.visible()) {
32192 return;
32193 }
32194
32195 var padding = node.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
32196 var opacity = node.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
32197 var color = node.pstyle("".concat(overlayOrUnderlay, "-color")).value;
32198 var shape = node.pstyle("".concat(overlayOrUnderlay, "-shape")).value;
32199
32200 if (opacity > 0) {
32201 pos = pos || node.position();
32202
32203 if (nodeWidth == null || nodeHeight == null) {
32204 var _padding = node.padding();
32205
32206 nodeWidth = node.width() + 2 * _padding;
32207 nodeHeight = node.height() + 2 * _padding;
32208 }
32209
32210 r.colorFillStyle(context, color[0], color[1], color[2], opacity);
32211 r.nodeShapes[shape].draw(context, pos.x, pos.y, nodeWidth + padding * 2, nodeHeight + padding * 2);
32212 context.fill();
32213 }
32214 };
32215 };
32216
32217 CRp$5.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
32218 CRp$5.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay'); // does the node have at least one pie piece?
32219
32220 CRp$5.hasPie = function (node) {
32221 node = node[0]; // ensure ele ref
32222
32223 return node._private.hasPie;
32224 };
32225
32226 CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
32227 node = node[0]; // ensure ele ref
32228
32229 pos = pos || node.position();
32230 var cyStyle = node.cy().style();
32231 var pieSize = node.pstyle('pie-size');
32232 var x = pos.x;
32233 var y = pos.y;
32234 var nodeW = node.width();
32235 var nodeH = node.height();
32236 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
32237
32238 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
32239
32240 var usePaths = this.usePaths();
32241
32242 if (usePaths) {
32243 x = 0;
32244 y = 0;
32245 }
32246
32247 if (pieSize.units === '%') {
32248 radius = radius * pieSize.pfValue;
32249 } else if (pieSize.pfValue !== undefined) {
32250 radius = pieSize.pfValue / 2;
32251 }
32252
32253 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
32254 // 1..N
32255 var size = node.pstyle('pie-' + i + '-background-size').value;
32256 var color = node.pstyle('pie-' + i + '-background-color').value;
32257 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
32258 var percent = size / 100; // map integer range [0, 100] to [0, 1]
32259 // percent can't push beyond 1
32260
32261 if (percent + lastPercent > 1) {
32262 percent = 1 - lastPercent;
32263 }
32264
32265 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
32266
32267 var angleDelta = 2 * Math.PI * percent;
32268 var angleEnd = angleStart + angleDelta; // ignore if
32269 // - zero size
32270 // - we're already beyond the full circle
32271 // - adding the current slice would go beyond the full circle
32272
32273 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
32274 continue;
32275 }
32276
32277 context.beginPath();
32278 context.moveTo(x, y);
32279 context.arc(x, y, radius, angleStart, angleEnd);
32280 context.closePath();
32281 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
32282 context.fill();
32283 lastPercent += percent;
32284 }
32285 };
32286
32287 var CRp$4 = {};
32288 var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
32289
32290 CRp$4.getPixelRatio = function () {
32291 var context = this.data.contexts[0];
32292
32293 if (this.forcedPixelRatio != null) {
32294 return this.forcedPixelRatio;
32295 }
32296
32297 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
32298 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
32299 };
32300
32301 CRp$4.paintCache = function (context) {
32302 var caches = this.paintCaches = this.paintCaches || [];
32303 var needToCreateCache = true;
32304 var cache;
32305
32306 for (var i = 0; i < caches.length; i++) {
32307 cache = caches[i];
32308
32309 if (cache.context === context) {
32310 needToCreateCache = false;
32311 break;
32312 }
32313 }
32314
32315 if (needToCreateCache) {
32316 cache = {
32317 context: context
32318 };
32319 caches.push(cache);
32320 }
32321
32322 return cache;
32323 };
32324
32325 CRp$4.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
32326 var gradientStyle;
32327 var usePaths = this.usePaths();
32328 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
32329 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
32330
32331 if (fill === 'radial-gradient') {
32332 if (ele.isEdge()) {
32333 var start = ele.sourceEndpoint(),
32334 end = ele.targetEndpoint(),
32335 mid = ele.midpoint();
32336 var d1 = dist(start, mid);
32337 var d2 = dist(end, mid);
32338 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
32339 } else {
32340 var pos = usePaths ? {
32341 x: 0,
32342 y: 0
32343 } : ele.position(),
32344 width = ele.paddedWidth(),
32345 height = ele.paddedHeight();
32346 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
32347 }
32348 } else {
32349 if (ele.isEdge()) {
32350 var _start = ele.sourceEndpoint(),
32351 _end = ele.targetEndpoint();
32352
32353 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
32354 } else {
32355 var _pos = usePaths ? {
32356 x: 0,
32357 y: 0
32358 } : ele.position(),
32359 _width = ele.paddedWidth(),
32360 _height = ele.paddedHeight(),
32361 halfWidth = _width / 2,
32362 halfHeight = _height / 2;
32363
32364 var direction = ele.pstyle('background-gradient-direction').value;
32365
32366 switch (direction) {
32367 case 'to-bottom':
32368 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
32369 break;
32370
32371 case 'to-top':
32372 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
32373 break;
32374
32375 case 'to-left':
32376 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
32377 break;
32378
32379 case 'to-right':
32380 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
32381 break;
32382
32383 case 'to-bottom-right':
32384 case 'to-right-bottom':
32385 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
32386 break;
32387
32388 case 'to-top-right':
32389 case 'to-right-top':
32390 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
32391 break;
32392
32393 case 'to-bottom-left':
32394 case 'to-left-bottom':
32395 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
32396 break;
32397
32398 case 'to-top-left':
32399 case 'to-left-top':
32400 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
32401 break;
32402 }
32403 }
32404 }
32405
32406 if (!gradientStyle) return null; // invalid gradient style
32407
32408 var hasPositions = positions.length === colors.length;
32409 var length = colors.length;
32410
32411 for (var i = 0; i < length; i++) {
32412 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
32413 }
32414
32415 return gradientStyle;
32416 };
32417
32418 CRp$4.gradientFillStyle = function (context, ele, fill, opacity) {
32419 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
32420 if (!gradientStyle) return null; // error
32421
32422 context.fillStyle = gradientStyle;
32423 };
32424
32425 CRp$4.colorFillStyle = function (context, r, g, b, a) {
32426 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
32427 // var cache = this.paintCache(context);
32428 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
32429 // if( cache.fillStyle !== fillStyle ){
32430 // context.fillStyle = cache.fillStyle = fillStyle;
32431 // }
32432 };
32433
32434 CRp$4.eleFillStyle = function (context, ele, opacity) {
32435 var backgroundFill = ele.pstyle('background-fill').value;
32436
32437 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
32438 this.gradientFillStyle(context, ele, backgroundFill, opacity);
32439 } else {
32440 var backgroundColor = ele.pstyle('background-color').value;
32441 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
32442 }
32443 };
32444
32445 CRp$4.gradientStrokeStyle = function (context, ele, fill, opacity) {
32446 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
32447 if (!gradientStyle) return null; // error
32448
32449 context.strokeStyle = gradientStyle;
32450 };
32451
32452 CRp$4.colorStrokeStyle = function (context, r, g, b, a) {
32453 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
32454 // var cache = this.paintCache(context);
32455 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
32456 // if( cache.strokeStyle !== strokeStyle ){
32457 // context.strokeStyle = cache.strokeStyle = strokeStyle;
32458 // }
32459 };
32460
32461 CRp$4.eleStrokeStyle = function (context, ele, opacity) {
32462 var lineFill = ele.pstyle('line-fill').value;
32463
32464 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
32465 this.gradientStrokeStyle(context, ele, lineFill, opacity);
32466 } else {
32467 var lineColor = ele.pstyle('line-color').value;
32468 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
32469 }
32470 }; // Resize canvas
32471
32472
32473 CRp$4.matchCanvasSize = function (container) {
32474 var r = this;
32475 var data = r.data;
32476 var bb = r.findContainerClientCoords();
32477 var width = bb[2];
32478 var height = bb[3];
32479 var pixelRatio = r.getPixelRatio();
32480 var mbPxRatio = r.motionBlurPxRatio;
32481
32482 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
32483 pixelRatio = mbPxRatio;
32484 }
32485
32486 var canvasWidth = width * pixelRatio;
32487 var canvasHeight = height * pixelRatio;
32488 var canvas;
32489
32490 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
32491 return; // save cycles if same
32492 }
32493
32494 r.fontCaches = null; // resizing resets the style
32495
32496 var canvasContainer = data.canvasContainer;
32497 canvasContainer.style.width = width + 'px';
32498 canvasContainer.style.height = height + 'px';
32499
32500 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
32501 canvas = data.canvases[i];
32502 canvas.width = canvasWidth;
32503 canvas.height = canvasHeight;
32504 canvas.style.width = width + 'px';
32505 canvas.style.height = height + 'px';
32506 }
32507
32508 for (var i = 0; i < r.BUFFER_COUNT; i++) {
32509 canvas = data.bufferCanvases[i];
32510 canvas.width = canvasWidth;
32511 canvas.height = canvasHeight;
32512 canvas.style.width = width + 'px';
32513 canvas.style.height = height + 'px';
32514 }
32515
32516 r.textureMult = 1;
32517
32518 if (pixelRatio <= 1) {
32519 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
32520 r.textureMult = 2;
32521 canvas.width = canvasWidth * r.textureMult;
32522 canvas.height = canvasHeight * r.textureMult;
32523 }
32524
32525 r.canvasWidth = canvasWidth;
32526 r.canvasHeight = canvasHeight;
32527 };
32528
32529 CRp$4.renderTo = function (cxt, zoom, pan, pxRatio) {
32530 this.render({
32531 forcedContext: cxt,
32532 forcedZoom: zoom,
32533 forcedPan: pan,
32534 drawAllLayers: true,
32535 forcedPxRatio: pxRatio
32536 });
32537 };
32538
32539 CRp$4.render = function (options) {
32540 options = options || staticEmptyObject();
32541 var forcedContext = options.forcedContext;
32542 var drawAllLayers = options.drawAllLayers;
32543 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
32544 var forcedZoom = options.forcedZoom;
32545 var forcedPan = options.forcedPan;
32546 var r = this;
32547 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
32548 var cy = r.cy;
32549 var data = r.data;
32550 var needDraw = data.canvasNeedsRedraw;
32551 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
32552 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
32553 var mbPxRatio = r.motionBlurPxRatio;
32554 var hasCompoundNodes = cy.hasCompoundNodes();
32555 var inNodeDragGesture = r.hoverData.draggingEles;
32556 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
32557 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
32558 var motionBlurFadeEffect = motionBlur;
32559
32560 if (!forcedContext) {
32561 if (r.prevPxRatio !== pixelRatio) {
32562 r.invalidateContainerClientCoordsCache();
32563 r.matchCanvasSize(r.container);
32564 r.redrawHint('eles', true);
32565 r.redrawHint('drag', true);
32566 }
32567
32568 r.prevPxRatio = pixelRatio;
32569 }
32570
32571 if (!forcedContext && r.motionBlurTimeout) {
32572 clearTimeout(r.motionBlurTimeout);
32573 }
32574
32575 if (motionBlur) {
32576 if (r.mbFrames == null) {
32577 r.mbFrames = 0;
32578 }
32579
32580 r.mbFrames++;
32581
32582 if (r.mbFrames < 3) {
32583 // need several frames before even high quality motionblur
32584 motionBlurFadeEffect = false;
32585 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
32586
32587
32588 if (r.mbFrames > r.minMbLowQualFrames) {
32589 //r.fullQualityMb = false;
32590 r.motionBlurPxRatio = r.mbPxRBlurry;
32591 }
32592 }
32593
32594 if (r.clearingMotionBlur) {
32595 r.motionBlurPxRatio = 1;
32596 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
32597 // because a rogue async texture frame would clear needDraw
32598
32599
32600 if (r.textureDrawLastFrame && !textureDraw) {
32601 needDraw[r.NODE] = true;
32602 needDraw[r.SELECT_BOX] = true;
32603 }
32604
32605 var style = cy.style();
32606 var zoom = cy.zoom();
32607 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
32608 var pan = cy.pan();
32609 var effectivePan = {
32610 x: pan.x,
32611 y: pan.y
32612 };
32613 var vp = {
32614 zoom: zoom,
32615 pan: {
32616 x: pan.x,
32617 y: pan.y
32618 }
32619 };
32620 var prevVp = r.prevViewport;
32621 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)
32622
32623 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
32624 r.motionBlurPxRatio = 1;
32625 }
32626
32627 if (forcedPan) {
32628 effectivePan = forcedPan;
32629 } // apply pixel ratio
32630
32631
32632 effectiveZoom *= pixelRatio;
32633 effectivePan.x *= pixelRatio;
32634 effectivePan.y *= pixelRatio;
32635 var eles = r.getCachedZSortedEles();
32636
32637 function mbclear(context, x, y, w, h) {
32638 var gco = context.globalCompositeOperation;
32639 context.globalCompositeOperation = 'destination-out';
32640 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
32641 context.fillRect(x, y, w, h);
32642 context.globalCompositeOperation = gco;
32643 }
32644
32645 function setContextTransform(context, clear) {
32646 var ePan, eZoom, w, h;
32647
32648 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
32649 ePan = {
32650 x: pan.x * mbPxRatio,
32651 y: pan.y * mbPxRatio
32652 };
32653 eZoom = zoom * mbPxRatio;
32654 w = r.canvasWidth * mbPxRatio;
32655 h = r.canvasHeight * mbPxRatio;
32656 } else {
32657 ePan = effectivePan;
32658 eZoom = effectiveZoom;
32659 w = r.canvasWidth;
32660 h = r.canvasHeight;
32661 }
32662
32663 context.setTransform(1, 0, 0, 1, 0, 0);
32664
32665 if (clear === 'motionBlur') {
32666 mbclear(context, 0, 0, w, h);
32667 } else if (!forcedContext && (clear === undefined || clear)) {
32668 context.clearRect(0, 0, w, h);
32669 }
32670
32671 if (!drawAllLayers) {
32672 context.translate(ePan.x, ePan.y);
32673 context.scale(eZoom, eZoom);
32674 }
32675
32676 if (forcedPan) {
32677 context.translate(forcedPan.x, forcedPan.y);
32678 }
32679
32680 if (forcedZoom) {
32681 context.scale(forcedZoom, forcedZoom);
32682 }
32683 }
32684
32685 if (!textureDraw) {
32686 r.textureDrawLastFrame = false;
32687 }
32688
32689 if (textureDraw) {
32690 r.textureDrawLastFrame = true;
32691
32692 if (!r.textureCache) {
32693 r.textureCache = {};
32694 r.textureCache.bb = cy.mutableElements().boundingBox();
32695 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
32696 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
32697 cxt.setTransform(1, 0, 0, 1, 0, 0);
32698 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
32699 r.render({
32700 forcedContext: cxt,
32701 drawOnlyNodeLayer: true,
32702 forcedPxRatio: pixelRatio * r.textureMult
32703 });
32704 var vp = r.textureCache.viewport = {
32705 zoom: cy.zoom(),
32706 pan: cy.pan(),
32707 width: r.canvasWidth,
32708 height: r.canvasHeight
32709 };
32710 vp.mpan = {
32711 x: (0 - vp.pan.x) / vp.zoom,
32712 y: (0 - vp.pan.y) / vp.zoom
32713 };
32714 }
32715
32716 needDraw[r.DRAG] = false;
32717 needDraw[r.NODE] = false;
32718 var context = data.contexts[r.NODE];
32719 var texture = r.textureCache.texture;
32720 var vp = r.textureCache.viewport;
32721 context.setTransform(1, 0, 0, 1, 0, 0);
32722
32723 if (motionBlur) {
32724 mbclear(context, 0, 0, vp.width, vp.height);
32725 } else {
32726 context.clearRect(0, 0, vp.width, vp.height);
32727 }
32728
32729 var outsideBgColor = style.core('outside-texture-bg-color').value;
32730 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
32731 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
32732 context.fillRect(0, 0, vp.width, vp.height);
32733 var zoom = cy.zoom();
32734 setContextTransform(context, false);
32735 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
32736 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
32737 } else if (r.textureOnViewport && !forcedContext) {
32738 // clear the cache since we don't need it
32739 r.textureCache = null;
32740 }
32741
32742 var extent = cy.extent();
32743 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
32744 var hideEdges = r.hideEdgesOnViewport && vpManip;
32745 var needMbClear = [];
32746 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
32747
32748 if (needMbClear[r.NODE]) {
32749 r.clearedForMotionBlur[r.NODE] = true;
32750 }
32751
32752 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
32753
32754 if (needMbClear[r.DRAG]) {
32755 r.clearedForMotionBlur[r.DRAG] = true;
32756 }
32757
32758 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
32759 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
32760 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
32761 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
32762 setContextTransform(context, clear);
32763
32764 if (hideEdges) {
32765 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
32766 } else {
32767 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
32768 }
32769
32770 if (r.debug) {
32771 r.drawDebugPoints(context, eles.nondrag);
32772 }
32773
32774 if (!drawAllLayers && !motionBlur) {
32775 needDraw[r.NODE] = false;
32776 }
32777 }
32778
32779 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
32780 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
32781 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
32782 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
32783
32784 if (hideEdges) {
32785 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
32786 } else {
32787 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
32788 }
32789
32790 if (r.debug) {
32791 r.drawDebugPoints(context, eles.drag);
32792 }
32793
32794 if (!drawAllLayers && !motionBlur) {
32795 needDraw[r.DRAG] = false;
32796 }
32797 }
32798
32799 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
32800 var context = forcedContext || data.contexts[r.SELECT_BOX];
32801 setContextTransform(context);
32802
32803 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
32804 var zoom = r.cy.zoom();
32805 var borderWidth = style.core('selection-box-border-width').value / zoom;
32806 context.lineWidth = borderWidth;
32807 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 + ')';
32808 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
32809
32810 if (borderWidth > 0) {
32811 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 + ')';
32812 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
32813 }
32814 }
32815
32816 if (data.bgActivePosistion && !r.hoverData.selecting) {
32817 var zoom = r.cy.zoom();
32818 var pos = data.bgActivePosistion;
32819 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 + ')';
32820 context.beginPath();
32821 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
32822 context.fill();
32823 }
32824
32825 var timeToRender = r.lastRedrawTime;
32826
32827 if (r.showFps && timeToRender) {
32828 timeToRender = Math.round(timeToRender);
32829 var fps = Math.round(1000 / timeToRender);
32830 context.setTransform(1, 0, 0, 1, 0, 0);
32831 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
32832 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
32833 context.lineWidth = 1;
32834 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
32835 var maxFps = 60;
32836 context.strokeRect(0, 30, 250, 20);
32837 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
32838 }
32839
32840 if (!drawAllLayers) {
32841 needDraw[r.SELECT_BOX] = false;
32842 }
32843 } // motionblur: blit rendered blurry frames
32844
32845
32846 if (motionBlur && mbPxRatio !== 1) {
32847 var cxtNode = data.contexts[r.NODE];
32848 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
32849 var cxtDrag = data.contexts[r.DRAG];
32850 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
32851
32852 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
32853 cxt.setTransform(1, 0, 0, 1, 0, 0);
32854
32855 if (needClear || !motionBlurFadeEffect) {
32856 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
32857 } else {
32858 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
32859 }
32860
32861 var pxr = mbPxRatio;
32862 cxt.drawImage(txt, // img
32863 0, 0, // sx, sy
32864 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
32865 0, 0, // x, y
32866 r.canvasWidth, r.canvasHeight // w, h
32867 );
32868 };
32869
32870 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
32871 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
32872 needDraw[r.NODE] = false;
32873 }
32874
32875 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
32876 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
32877 needDraw[r.DRAG] = false;
32878 }
32879 }
32880
32881 r.prevViewport = vp;
32882
32883 if (r.clearingMotionBlur) {
32884 r.clearingMotionBlur = false;
32885 r.motionBlurCleared = true;
32886 r.motionBlur = true;
32887 }
32888
32889 if (motionBlur) {
32890 r.motionBlurTimeout = setTimeout(function () {
32891 r.motionBlurTimeout = null;
32892 r.clearedForMotionBlur[r.NODE] = false;
32893 r.clearedForMotionBlur[r.DRAG] = false;
32894 r.motionBlur = false;
32895 r.clearingMotionBlur = !textureDraw;
32896 r.mbFrames = 0;
32897 needDraw[r.NODE] = true;
32898 needDraw[r.DRAG] = true;
32899 r.redraw();
32900 }, motionBlurDelay);
32901 }
32902
32903 if (!forcedContext) {
32904 cy.emit('render');
32905 }
32906 };
32907
32908 var CRp$3 = {}; // @O Polygon drawing
32909
32910 CRp$3.drawPolygonPath = function (context, x, y, width, height, points) {
32911 var halfW = width / 2;
32912 var halfH = height / 2;
32913
32914 if (context.beginPath) {
32915 context.beginPath();
32916 }
32917
32918 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
32919
32920 for (var i = 1; i < points.length / 2; i++) {
32921 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
32922 }
32923
32924 context.closePath();
32925 };
32926
32927 CRp$3.drawRoundPolygonPath = function (context, x, y, width, height, points) {
32928 var halfW = width / 2;
32929 var halfH = height / 2;
32930 var cornerRadius = getRoundPolygonRadius(width, height);
32931
32932 if (context.beginPath) {
32933 context.beginPath();
32934 }
32935
32936 for (var _i = 0; _i < points.length / 4; _i++) {
32937 var sourceUv = void 0,
32938 destUv = void 0;
32939
32940 if (_i === 0) {
32941 sourceUv = points.length - 2;
32942 } else {
32943 sourceUv = _i * 4 - 2;
32944 }
32945
32946 destUv = _i * 4 + 2;
32947 var px = x + halfW * points[_i * 4];
32948 var py = y + halfH * points[_i * 4 + 1];
32949 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
32950 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
32951 var cp0x = px - offset * points[sourceUv];
32952 var cp0y = py - offset * points[sourceUv + 1];
32953 var cp1x = px + offset * points[destUv];
32954 var cp1y = py + offset * points[destUv + 1];
32955
32956 if (_i === 0) {
32957 context.moveTo(cp0x, cp0y);
32958 } else {
32959 context.lineTo(cp0x, cp0y);
32960 }
32961
32962 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
32963 }
32964
32965 context.closePath();
32966 }; // Round rectangle drawing
32967
32968
32969 CRp$3.drawRoundRectanglePath = function (context, x, y, width, height) {
32970 var halfWidth = width / 2;
32971 var halfHeight = height / 2;
32972 var cornerRadius = getRoundRectangleRadius(width, height);
32973
32974 if (context.beginPath) {
32975 context.beginPath();
32976 } // Start at top middle
32977
32978
32979 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
32980
32981 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
32982
32983 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
32984
32985 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
32986
32987 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
32988
32989 context.lineTo(x, y - halfHeight);
32990 context.closePath();
32991 };
32992
32993 CRp$3.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
32994 var halfWidth = width / 2;
32995 var halfHeight = height / 2;
32996 var cornerRadius = getRoundRectangleRadius(width, height);
32997
32998 if (context.beginPath) {
32999 context.beginPath();
33000 } // Start at top middle
33001
33002
33003 context.moveTo(x, y - halfHeight);
33004 context.lineTo(x + halfWidth, y - halfHeight);
33005 context.lineTo(x + halfWidth, y);
33006 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
33007 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
33008 context.lineTo(x - halfWidth, y - halfHeight);
33009 context.lineTo(x, y - halfHeight);
33010 context.closePath();
33011 };
33012
33013 CRp$3.drawCutRectanglePath = function (context, x, y, width, height) {
33014 var halfWidth = width / 2;
33015 var halfHeight = height / 2;
33016 var cornerLength = getCutRectangleCornerLength();
33017
33018 if (context.beginPath) {
33019 context.beginPath();
33020 }
33021
33022 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
33023 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
33024 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
33025 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
33026 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
33027 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
33028 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
33029 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
33030 context.closePath();
33031 };
33032
33033 CRp$3.drawBarrelPath = function (context, x, y, width, height) {
33034 var halfWidth = width / 2;
33035 var halfHeight = height / 2;
33036 var xBegin = x - halfWidth;
33037 var xEnd = x + halfWidth;
33038 var yBegin = y - halfHeight;
33039 var yEnd = y + halfHeight;
33040 var barrelCurveConstants = getBarrelCurveConstants(width, height);
33041 var wOffset = barrelCurveConstants.widthOffset;
33042 var hOffset = barrelCurveConstants.heightOffset;
33043 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
33044
33045 if (context.beginPath) {
33046 context.beginPath();
33047 }
33048
33049 context.moveTo(xBegin, yBegin + hOffset);
33050 context.lineTo(xBegin, yEnd - hOffset);
33051 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
33052 context.lineTo(xEnd - wOffset, yEnd);
33053 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
33054 context.lineTo(xEnd, yBegin + hOffset);
33055 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
33056 context.lineTo(xBegin + wOffset, yBegin);
33057 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
33058 context.closePath();
33059 };
33060
33061 var sin0 = Math.sin(0);
33062 var cos0 = Math.cos(0);
33063 var sin = {};
33064 var cos = {};
33065 var ellipseStepSize = Math.PI / 40;
33066
33067 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
33068 sin[i] = Math.sin(i);
33069 cos[i] = Math.cos(i);
33070 }
33071
33072 CRp$3.drawEllipsePath = function (context, centerX, centerY, width, height) {
33073 if (context.beginPath) {
33074 context.beginPath();
33075 }
33076
33077 if (context.ellipse) {
33078 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
33079 } else {
33080 var xPos, yPos;
33081 var rw = width / 2;
33082 var rh = height / 2;
33083
33084 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
33085 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
33086 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
33087
33088 if (i === 0) {
33089 context.moveTo(xPos, yPos);
33090 } else {
33091 context.lineTo(xPos, yPos);
33092 }
33093 }
33094 }
33095
33096 context.closePath();
33097 };
33098
33099 /* global atob, ArrayBuffer, Uint8Array, Blob */
33100 var CRp$2 = {};
33101
33102 CRp$2.createBuffer = function (w, h) {
33103 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
33104
33105 buffer.width = w;
33106 buffer.height = h;
33107 return [buffer, buffer.getContext('2d')];
33108 };
33109
33110 CRp$2.bufferCanvasImage = function (options) {
33111 var cy = this.cy;
33112 var eles = cy.mutableElements();
33113 var bb = eles.boundingBox();
33114 var ctrRect = this.findContainerClientCoords();
33115 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
33116 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
33117 var specdMaxDims = number$1(options.maxWidth) || number$1(options.maxHeight);
33118 var pxRatio = this.getPixelRatio();
33119 var scale = 1;
33120
33121 if (options.scale !== undefined) {
33122 width *= options.scale;
33123 height *= options.scale;
33124 scale = options.scale;
33125 } else if (specdMaxDims) {
33126 var maxScaleW = Infinity;
33127 var maxScaleH = Infinity;
33128
33129 if (number$1(options.maxWidth)) {
33130 maxScaleW = scale * options.maxWidth / width;
33131 }
33132
33133 if (number$1(options.maxHeight)) {
33134 maxScaleH = scale * options.maxHeight / height;
33135 }
33136
33137 scale = Math.min(maxScaleW, maxScaleH);
33138 width *= scale;
33139 height *= scale;
33140 }
33141
33142 if (!specdMaxDims) {
33143 width *= pxRatio;
33144 height *= pxRatio;
33145 scale *= pxRatio;
33146 }
33147
33148 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
33149
33150 buffCanvas.width = width;
33151 buffCanvas.height = height;
33152 buffCanvas.style.width = width + 'px';
33153 buffCanvas.style.height = height + 'px';
33154 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
33155
33156 if (width > 0 && height > 0) {
33157 buffCxt.clearRect(0, 0, width, height);
33158 buffCxt.globalCompositeOperation = 'source-over';
33159 var zsortedEles = this.getCachedZSortedEles();
33160
33161 if (options.full) {
33162 // draw the full bounds of the graph
33163 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
33164 buffCxt.scale(scale, scale);
33165 this.drawElements(buffCxt, zsortedEles);
33166 buffCxt.scale(1 / scale, 1 / scale);
33167 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
33168 } else {
33169 // draw the current view
33170 var pan = cy.pan();
33171 var translation = {
33172 x: pan.x * scale,
33173 y: pan.y * scale
33174 };
33175 scale *= cy.zoom();
33176 buffCxt.translate(translation.x, translation.y);
33177 buffCxt.scale(scale, scale);
33178 this.drawElements(buffCxt, zsortedEles);
33179 buffCxt.scale(1 / scale, 1 / scale);
33180 buffCxt.translate(-translation.x, -translation.y);
33181 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
33182
33183
33184 if (options.bg) {
33185 buffCxt.globalCompositeOperation = 'destination-over';
33186 buffCxt.fillStyle = options.bg;
33187 buffCxt.rect(0, 0, width, height);
33188 buffCxt.fill();
33189 }
33190 }
33191
33192 return buffCanvas;
33193 };
33194
33195 function b64ToBlob(b64, mimeType) {
33196 var bytes = atob(b64);
33197 var buff = new ArrayBuffer(bytes.length);
33198 var buffUint8 = new Uint8Array(buff);
33199
33200 for (var i = 0; i < bytes.length; i++) {
33201 buffUint8[i] = bytes.charCodeAt(i);
33202 }
33203
33204 return new Blob([buff], {
33205 type: mimeType
33206 });
33207 }
33208
33209 function b64UriToB64(b64uri) {
33210 var i = b64uri.indexOf(',');
33211 return b64uri.substr(i + 1);
33212 }
33213
33214 function output(options, canvas, mimeType) {
33215 var getB64Uri = function getB64Uri() {
33216 return canvas.toDataURL(mimeType, options.quality);
33217 };
33218
33219 switch (options.output) {
33220 case 'blob-promise':
33221 return new Promise$1(function (resolve, reject) {
33222 try {
33223 canvas.toBlob(function (blob) {
33224 if (blob != null) {
33225 resolve(blob);
33226 } else {
33227 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
33228 }
33229 }, mimeType, options.quality);
33230 } catch (err) {
33231 reject(err);
33232 }
33233 });
33234
33235 case 'blob':
33236 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
33237
33238 case 'base64':
33239 return b64UriToB64(getB64Uri());
33240
33241 case 'base64uri':
33242 default:
33243 return getB64Uri();
33244 }
33245 }
33246
33247 CRp$2.png = function (options) {
33248 return output(options, this.bufferCanvasImage(options), 'image/png');
33249 };
33250
33251 CRp$2.jpg = function (options) {
33252 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
33253 };
33254
33255 var CRp$1 = {};
33256
33257 CRp$1.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
33258 switch (name) {
33259 case 'ellipse':
33260 return this.drawEllipsePath(context, centerX, centerY, width, height);
33261
33262 case 'polygon':
33263 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
33264
33265 case 'round-polygon':
33266 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
33267
33268 case 'roundrectangle':
33269 case 'round-rectangle':
33270 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
33271
33272 case 'cutrectangle':
33273 case 'cut-rectangle':
33274 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
33275
33276 case 'bottomroundrectangle':
33277 case 'bottom-round-rectangle':
33278 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
33279
33280 case 'barrel':
33281 return this.drawBarrelPath(context, centerX, centerY, width, height);
33282 }
33283 };
33284
33285 var CR = CanvasRenderer;
33286 var CRp = CanvasRenderer.prototype;
33287 CRp.CANVAS_LAYERS = 3; //
33288
33289 CRp.SELECT_BOX = 0;
33290 CRp.DRAG = 1;
33291 CRp.NODE = 2;
33292 CRp.BUFFER_COUNT = 3; //
33293
33294 CRp.TEXTURE_BUFFER = 0;
33295 CRp.MOTIONBLUR_BUFFER_NODE = 1;
33296 CRp.MOTIONBLUR_BUFFER_DRAG = 2;
33297
33298 function CanvasRenderer(options) {
33299 var r = this;
33300 r.data = {
33301 canvases: new Array(CRp.CANVAS_LAYERS),
33302 contexts: new Array(CRp.CANVAS_LAYERS),
33303 canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS),
33304 bufferCanvases: new Array(CRp.BUFFER_COUNT),
33305 bufferContexts: new Array(CRp.CANVAS_LAYERS)
33306 };
33307 var tapHlOffAttr = '-webkit-tap-highlight-color';
33308 var tapHlOffStyle = 'rgba(0,0,0,0)';
33309 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
33310
33311 var containerStyle = r.data.canvasContainer.style;
33312 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
33313 containerStyle.position = 'relative';
33314 containerStyle.zIndex = '0';
33315 containerStyle.overflow = 'hidden';
33316 var container = options.cy.container();
33317 container.appendChild(r.data.canvasContainer);
33318 container.style[tapHlOffAttr] = tapHlOffStyle;
33319 var styleMap = {
33320 '-webkit-user-select': 'none',
33321 '-moz-user-select': '-moz-none',
33322 'user-select': 'none',
33323 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
33324 'outline-style': 'none'
33325 };
33326
33327 if (ms()) {
33328 styleMap['-ms-touch-action'] = 'none';
33329 styleMap['touch-action'] = 'none';
33330 }
33331
33332 for (var i = 0; i < CRp.CANVAS_LAYERS; i++) {
33333 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
33334
33335 r.data.contexts[i] = canvas.getContext('2d');
33336 Object.keys(styleMap).forEach(function (k) {
33337 canvas.style[k] = styleMap[k];
33338 });
33339 canvas.style.position = 'absolute';
33340 canvas.setAttribute('data-id', 'layer' + i);
33341 canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i);
33342 r.data.canvasContainer.appendChild(canvas);
33343 r.data.canvasNeedsRedraw[i] = false;
33344 }
33345
33346 r.data.topCanvas = r.data.canvases[0];
33347 r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node');
33348 r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox');
33349 r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag');
33350
33351 for (var i = 0; i < CRp.BUFFER_COUNT; i++) {
33352 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
33353
33354 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
33355 r.data.bufferCanvases[i].style.position = 'absolute';
33356 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
33357 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
33358 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
33359 }
33360
33361 r.pathsEnabled = true;
33362 var emptyBb = makeBoundingBox();
33363
33364 var getBoxCenter = function getBoxCenter(bb) {
33365 return {
33366 x: (bb.x1 + bb.x2) / 2,
33367 y: (bb.y1 + bb.y2) / 2
33368 };
33369 };
33370
33371 var getCenterOffset = function getCenterOffset(bb) {
33372 return {
33373 x: -bb.w / 2,
33374 y: -bb.h / 2
33375 };
33376 };
33377
33378 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
33379 var _p = ele[0]._private;
33380 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
33381 return !same;
33382 };
33383
33384 var getStyleKey = function getStyleKey(ele) {
33385 return ele[0]._private.nodeKey;
33386 };
33387
33388 var getLabelKey = function getLabelKey(ele) {
33389 return ele[0]._private.labelStyleKey;
33390 };
33391
33392 var getSourceLabelKey = function getSourceLabelKey(ele) {
33393 return ele[0]._private.sourceLabelStyleKey;
33394 };
33395
33396 var getTargetLabelKey = function getTargetLabelKey(ele) {
33397 return ele[0]._private.targetLabelStyleKey;
33398 };
33399
33400 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
33401 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
33402 };
33403
33404 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
33405 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
33406 };
33407
33408 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
33409 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
33410 };
33411
33412 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
33413 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
33414 };
33415
33416 var getElementBox = function getElementBox(ele) {
33417 ele.boundingBox();
33418 return ele[0]._private.bodyBounds;
33419 };
33420
33421 var getLabelBox = function getLabelBox(ele) {
33422 ele.boundingBox();
33423 return ele[0]._private.labelBounds.main || emptyBb;
33424 };
33425
33426 var getSourceLabelBox = function getSourceLabelBox(ele) {
33427 ele.boundingBox();
33428 return ele[0]._private.labelBounds.source || emptyBb;
33429 };
33430
33431 var getTargetLabelBox = function getTargetLabelBox(ele) {
33432 ele.boundingBox();
33433 return ele[0]._private.labelBounds.target || emptyBb;
33434 };
33435
33436 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
33437 return scaledLabelShown;
33438 };
33439
33440 var getElementRotationPoint = function getElementRotationPoint(ele) {
33441 return getBoxCenter(getElementBox(ele));
33442 };
33443
33444 var addTextMargin = function addTextMargin(prefix, pt, ele) {
33445 var pre = prefix ? prefix + '-' : '';
33446 return {
33447 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
33448 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
33449 };
33450 };
33451
33452 var getRsPt = function getRsPt(ele, x, y) {
33453 var rs = ele[0]._private.rscratch;
33454 return {
33455 x: rs[x],
33456 y: rs[y]
33457 };
33458 };
33459
33460 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
33461 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
33462 };
33463
33464 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
33465 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
33466 };
33467
33468 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
33469 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
33470 };
33471
33472 var getElementRotationOffset = function getElementRotationOffset(ele) {
33473 return getCenterOffset(getElementBox(ele));
33474 };
33475
33476 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
33477 return getCenterOffset(getSourceLabelBox(ele));
33478 };
33479
33480 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
33481 return getCenterOffset(getTargetLabelBox(ele));
33482 };
33483
33484 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
33485 var bb = getLabelBox(ele);
33486 var p = getCenterOffset(getLabelBox(ele));
33487
33488 if (ele.isNode()) {
33489 switch (ele.pstyle('text-halign').value) {
33490 case 'left':
33491 p.x = -bb.w;
33492 break;
33493
33494 case 'right':
33495 p.x = 0;
33496 break;
33497 }
33498
33499 switch (ele.pstyle('text-valign').value) {
33500 case 'top':
33501 p.y = -bb.h;
33502 break;
33503
33504 case 'bottom':
33505 p.y = 0;
33506 break;
33507 }
33508 }
33509
33510 return p;
33511 };
33512
33513 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
33514 getKey: getStyleKey,
33515 doesEleInvalidateKey: backgroundTimestampHasChanged,
33516 drawElement: drawElement,
33517 getBoundingBox: getElementBox,
33518 getRotationPoint: getElementRotationPoint,
33519 getRotationOffset: getElementRotationOffset,
33520 allowEdgeTxrCaching: false,
33521 allowParentTxrCaching: false
33522 });
33523 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
33524 getKey: getLabelKey,
33525 drawElement: drawLabel,
33526 getBoundingBox: getLabelBox,
33527 getRotationPoint: getLabelRotationPoint,
33528 getRotationOffset: getLabelRotationOffset,
33529 isVisible: isLabelVisibleAtScale
33530 });
33531 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
33532 getKey: getSourceLabelKey,
33533 drawElement: drawSourceLabel,
33534 getBoundingBox: getSourceLabelBox,
33535 getRotationPoint: getSourceLabelRotationPoint,
33536 getRotationOffset: getSourceLabelRotationOffset,
33537 isVisible: isLabelVisibleAtScale
33538 });
33539 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
33540 getKey: getTargetLabelKey,
33541 drawElement: drawTargetLabel,
33542 getBoundingBox: getTargetLabelBox,
33543 getRotationPoint: getTargetLabelRotationPoint,
33544 getRotationOffset: getTargetLabelRotationOffset,
33545 isVisible: isLabelVisibleAtScale
33546 });
33547 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
33548 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
33549 // each cache should check for sub-key diff to see that the update affects that cache particularly
33550 eleTxrCache.invalidateElements(eles);
33551 lblTxrCache.invalidateElements(eles);
33552 slbTxrCache.invalidateElements(eles);
33553 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
33554
33555 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
33556
33557 for (var _i = 0; _i < eles.length; _i++) {
33558 var _p = eles[_i]._private;
33559 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
33560 }
33561 });
33562
33563 var refineInLayers = function refineInLayers(reqs) {
33564 for (var i = 0; i < reqs.length; i++) {
33565 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
33566 }
33567 };
33568
33569 eleTxrCache.onDequeue(refineInLayers);
33570 lblTxrCache.onDequeue(refineInLayers);
33571 slbTxrCache.onDequeue(refineInLayers);
33572 tlbTxrCache.onDequeue(refineInLayers);
33573 }
33574
33575 CRp.redrawHint = function (group, bool) {
33576 var r = this;
33577
33578 switch (group) {
33579 case 'eles':
33580 r.data.canvasNeedsRedraw[CRp.NODE] = bool;
33581 break;
33582
33583 case 'drag':
33584 r.data.canvasNeedsRedraw[CRp.DRAG] = bool;
33585 break;
33586
33587 case 'select':
33588 r.data.canvasNeedsRedraw[CRp.SELECT_BOX] = bool;
33589 break;
33590 }
33591 }; // whether to use Path2D caching for drawing
33592
33593
33594 var pathsImpld = typeof Path2D !== 'undefined';
33595
33596 CRp.path2dEnabled = function (on) {
33597 if (on === undefined) {
33598 return this.pathsEnabled;
33599 }
33600
33601 this.pathsEnabled = on ? true : false;
33602 };
33603
33604 CRp.usePaths = function () {
33605 return pathsImpld && this.pathsEnabled;
33606 };
33607
33608 CRp.setImgSmoothing = function (context, bool) {
33609 if (context.imageSmoothingEnabled != null) {
33610 context.imageSmoothingEnabled = bool;
33611 } else {
33612 context.webkitImageSmoothingEnabled = bool;
33613 context.mozImageSmoothingEnabled = bool;
33614 context.msImageSmoothingEnabled = bool;
33615 }
33616 };
33617
33618 CRp.getImgSmoothing = function (context) {
33619 if (context.imageSmoothingEnabled != null) {
33620 return context.imageSmoothingEnabled;
33621 } else {
33622 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
33623 }
33624 };
33625
33626 CRp.makeOffscreenCanvas = function (width, height) {
33627 var canvas;
33628
33629 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ("undefined" )) {
33630 canvas = new OffscreenCanvas(width, height);
33631 } else {
33632 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
33633
33634 canvas.width = width;
33635 canvas.height = height;
33636 }
33637
33638 return canvas;
33639 };
33640
33641 [CRp$a, CRp$9, CRp$8, CRp$7, CRp$6, CRp$5, CRp$4, CRp$3, CRp$2, CRp$1].forEach(function (props) {
33642 extend(CRp, props);
33643 });
33644
33645 var renderer = [{
33646 name: 'null',
33647 impl: NullRenderer
33648 }, {
33649 name: 'base',
33650 impl: BR
33651 }, {
33652 name: 'canvas',
33653 impl: CR
33654 }];
33655
33656 var incExts = [{
33657 type: 'layout',
33658 extensions: layout
33659 }, {
33660 type: 'renderer',
33661 extensions: renderer
33662 }];
33663
33664 var extensions = {}; // registered modules for extensions, indexed by name
33665
33666 var modules = {};
33667
33668 function setExtension(type, name, registrant) {
33669 var ext = registrant;
33670
33671 var overrideErr = function overrideErr(field) {
33672 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
33673 };
33674
33675 if (type === 'core') {
33676 if (Core.prototype[name]) {
33677 return overrideErr(name);
33678 } else {
33679 Core.prototype[name] = registrant;
33680 }
33681 } else if (type === 'collection') {
33682 if (Collection.prototype[name]) {
33683 return overrideErr(name);
33684 } else {
33685 Collection.prototype[name] = registrant;
33686 }
33687 } else if (type === 'layout') {
33688 // fill in missing layout functions in the prototype
33689 var Layout = function Layout(options) {
33690 this.options = options;
33691 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
33692
33693 if (!plainObject(this._private)) {
33694 this._private = {};
33695 }
33696
33697 this._private.cy = options.cy;
33698 this._private.listeners = [];
33699 this.createEmitter();
33700 };
33701
33702 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
33703 var optLayoutFns = [];
33704
33705 for (var i = 0; i < optLayoutFns.length; i++) {
33706 var fnName = optLayoutFns[i];
33707
33708 layoutProto[fnName] = layoutProto[fnName] || function () {
33709 return this;
33710 };
33711 } // either .start() or .run() is defined, so autogen the other
33712
33713
33714 if (layoutProto.start && !layoutProto.run) {
33715 layoutProto.run = function () {
33716 this.start();
33717 return this;
33718 };
33719 } else if (!layoutProto.start && layoutProto.run) {
33720 layoutProto.start = function () {
33721 this.run();
33722 return this;
33723 };
33724 }
33725
33726 var regStop = registrant.prototype.stop;
33727
33728 layoutProto.stop = function () {
33729 var opts = this.options;
33730
33731 if (opts && opts.animate) {
33732 var anis = this.animations;
33733
33734 if (anis) {
33735 for (var _i = 0; _i < anis.length; _i++) {
33736 anis[_i].stop();
33737 }
33738 }
33739 }
33740
33741 if (regStop) {
33742 regStop.call(this);
33743 } else {
33744 this.emit('layoutstop');
33745 }
33746
33747 return this;
33748 };
33749
33750 if (!layoutProto.destroy) {
33751 layoutProto.destroy = function () {
33752 return this;
33753 };
33754 }
33755
33756 layoutProto.cy = function () {
33757 return this._private.cy;
33758 };
33759
33760 var getCy = function getCy(layout) {
33761 return layout._private.cy;
33762 };
33763
33764 var emitterOpts = {
33765 addEventFields: function addEventFields(layout, evt) {
33766 evt.layout = layout;
33767 evt.cy = getCy(layout);
33768 evt.target = layout;
33769 },
33770 bubble: function bubble() {
33771 return true;
33772 },
33773 parent: function parent(layout) {
33774 return getCy(layout);
33775 }
33776 };
33777 extend(layoutProto, {
33778 createEmitter: function createEmitter() {
33779 this._private.emitter = new Emitter(emitterOpts, this);
33780 return this;
33781 },
33782 emitter: function emitter() {
33783 return this._private.emitter;
33784 },
33785 on: function on(evt, cb) {
33786 this.emitter().on(evt, cb);
33787 return this;
33788 },
33789 one: function one(evt, cb) {
33790 this.emitter().one(evt, cb);
33791 return this;
33792 },
33793 once: function once(evt, cb) {
33794 this.emitter().one(evt, cb);
33795 return this;
33796 },
33797 removeListener: function removeListener(evt, cb) {
33798 this.emitter().removeListener(evt, cb);
33799 return this;
33800 },
33801 removeAllListeners: function removeAllListeners() {
33802 this.emitter().removeAllListeners();
33803 return this;
33804 },
33805 emit: function emit(evt, params) {
33806 this.emitter().emit(evt, params);
33807 return this;
33808 }
33809 });
33810 define.eventAliasesOn(layoutProto);
33811 ext = Layout; // replace with our wrapped layout
33812 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
33813 // user registered renderers inherit from base
33814 var BaseRenderer = getExtension('renderer', 'base');
33815 var bProto = BaseRenderer.prototype;
33816 var RegistrantRenderer = registrant;
33817 var rProto = registrant.prototype;
33818
33819 var Renderer = function Renderer() {
33820 BaseRenderer.apply(this, arguments);
33821 RegistrantRenderer.apply(this, arguments);
33822 };
33823
33824 var proto = Renderer.prototype;
33825
33826 for (var pName in bProto) {
33827 var pVal = bProto[pName];
33828 var existsInR = rProto[pName] != null;
33829
33830 if (existsInR) {
33831 return overrideErr(pName);
33832 }
33833
33834 proto[pName] = pVal; // take impl from base
33835 }
33836
33837 for (var _pName in rProto) {
33838 proto[_pName] = rProto[_pName]; // take impl from registrant
33839 }
33840
33841 bProto.clientFunctions.forEach(function (name) {
33842 proto[name] = proto[name] || function () {
33843 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
33844 };
33845 });
33846 ext = Renderer;
33847 } else if (type === '__proto__' || type === 'constructor' || type === 'prototype') {
33848 // to avoid potential prototype pollution
33849 return error(type + ' is an illegal type to be registered, possibly lead to prototype pollutions');
33850 }
33851
33852 return setMap({
33853 map: extensions,
33854 keys: [type, name],
33855 value: ext
33856 });
33857 }
33858
33859 function getExtension(type, name) {
33860 return getMap({
33861 map: extensions,
33862 keys: [type, name]
33863 });
33864 }
33865
33866 function setModule(type, name, moduleType, moduleName, registrant) {
33867 return setMap({
33868 map: modules,
33869 keys: [type, name, moduleType, moduleName],
33870 value: registrant
33871 });
33872 }
33873
33874 function getModule(type, name, moduleType, moduleName) {
33875 return getMap({
33876 map: modules,
33877 keys: [type, name, moduleType, moduleName]
33878 });
33879 }
33880
33881 var extension = function extension() {
33882 // e.g. extension('renderer', 'svg')
33883 if (arguments.length === 2) {
33884 return getExtension.apply(null, arguments);
33885 } // e.g. extension('renderer', 'svg', { ... })
33886 else if (arguments.length === 3) {
33887 return setExtension.apply(null, arguments);
33888 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
33889 else if (arguments.length === 4) {
33890 return getModule.apply(null, arguments);
33891 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
33892 else if (arguments.length === 5) {
33893 return setModule.apply(null, arguments);
33894 } else {
33895 error('Invalid extension access syntax');
33896 }
33897 }; // allows a core instance to access extensions internally
33898
33899
33900 Core.prototype.extension = extension; // included extensions
33901
33902 incExts.forEach(function (group) {
33903 group.extensions.forEach(function (ext) {
33904 setExtension(group.type, ext.name, ext.impl);
33905 });
33906 });
33907
33908 // (useful for init)
33909
33910 var Stylesheet = function Stylesheet() {
33911 if (!(this instanceof Stylesheet)) {
33912 return new Stylesheet();
33913 }
33914
33915 this.length = 0;
33916 };
33917
33918 var sheetfn = Stylesheet.prototype;
33919
33920 sheetfn.instanceString = function () {
33921 return 'stylesheet';
33922 }; // just store the selector to be parsed later
33923
33924
33925 sheetfn.selector = function (selector) {
33926 var i = this.length++;
33927 this[i] = {
33928 selector: selector,
33929 properties: []
33930 };
33931 return this; // chaining
33932 }; // just store the property to be parsed later
33933
33934
33935 sheetfn.css = function (name, value) {
33936 var i = this.length - 1;
33937
33938 if (string(name)) {
33939 this[i].properties.push({
33940 name: name,
33941 value: value
33942 });
33943 } else if (plainObject(name)) {
33944 var map = name;
33945 var propNames = Object.keys(map);
33946
33947 for (var j = 0; j < propNames.length; j++) {
33948 var key = propNames[j];
33949 var mapVal = map[key];
33950
33951 if (mapVal == null) {
33952 continue;
33953 }
33954
33955 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
33956
33957 if (prop == null) {
33958 continue;
33959 }
33960
33961 var _name = prop.name;
33962 var _value = mapVal;
33963 this[i].properties.push({
33964 name: _name,
33965 value: _value
33966 });
33967 }
33968 }
33969
33970 return this; // chaining
33971 };
33972
33973 sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
33974
33975 sheetfn.generateStyle = function (cy) {
33976 var style = new Style(cy);
33977 return this.appendToStyle(style);
33978 }; // append a dummy stylesheet object on a real style object
33979
33980
33981 sheetfn.appendToStyle = function (style) {
33982 for (var i = 0; i < this.length; i++) {
33983 var context = this[i];
33984 var selector = context.selector;
33985 var props = context.properties;
33986 style.selector(selector); // apply selector
33987
33988 for (var j = 0; j < props.length; j++) {
33989 var prop = props[j];
33990 style.css(prop.name, prop.value); // apply property
33991 }
33992 }
33993
33994 return style;
33995 };
33996
33997 var version = "3.23.0";
33998
33999 var cytoscape = function cytoscape(options) {
34000 // if no options specified, use default
34001 if (options === undefined) {
34002 options = {};
34003 } // create instance
34004
34005
34006 if (plainObject(options)) {
34007 return new Core(options);
34008 } // allow for registration of extensions
34009 else if (string(options)) {
34010 return extension.apply(extension, arguments);
34011 }
34012 }; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
34013
34014
34015 cytoscape.use = function (ext) {
34016 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
34017
34018 args.unshift(cytoscape); // cytoscape is first arg to ext
34019
34020 ext.apply(null, args);
34021 return this;
34022 };
34023
34024 cytoscape.warnings = function (bool) {
34025 return warnings(bool);
34026 }; // replaced by build system
34027
34028
34029 cytoscape.version = version; // expose public apis (mostly for extensions)
34030
34031 cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
34032
34033 return cytoscape;
34034
34035}));