UNPKG

953 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2024, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23'use strict';
24
25function _typeof(obj) {
26 "@babel/helpers - typeof";
27
28 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
29 return typeof obj;
30 } : function (obj) {
31 return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
32 }, _typeof(obj);
33}
34function _classCallCheck(instance, Constructor) {
35 if (!(instance instanceof Constructor)) {
36 throw new TypeError("Cannot call a class as a function");
37 }
38}
39function _defineProperties(target, props) {
40 for (var i = 0; i < props.length; i++) {
41 var descriptor = props[i];
42 descriptor.enumerable = descriptor.enumerable || false;
43 descriptor.configurable = true;
44 if ("value" in descriptor) descriptor.writable = true;
45 Object.defineProperty(target, descriptor.key, descriptor);
46 }
47}
48function _createClass(Constructor, protoProps, staticProps) {
49 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
50 if (staticProps) _defineProperties(Constructor, staticProps);
51 Object.defineProperty(Constructor, "prototype", {
52 writable: false
53 });
54 return Constructor;
55}
56function _defineProperty$1(obj, key, value) {
57 if (key in obj) {
58 Object.defineProperty(obj, key, {
59 value: value,
60 enumerable: true,
61 configurable: true,
62 writable: true
63 });
64 } else {
65 obj[key] = value;
66 }
67 return obj;
68}
69function _slicedToArray(arr, i) {
70 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
71}
72function _arrayWithHoles(arr) {
73 if (Array.isArray(arr)) return arr;
74}
75function _iterableToArrayLimit(arr, i) {
76 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
77 if (_i == null) return;
78 var _arr = [];
79 var _n = true;
80 var _d = false;
81 var _s, _e;
82 try {
83 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
84 _arr.push(_s.value);
85 if (i && _arr.length === i) break;
86 }
87 } catch (err) {
88 _d = true;
89 _e = err;
90 } finally {
91 try {
92 if (!_n && _i["return"] != null) _i["return"]();
93 } finally {
94 if (_d) throw _e;
95 }
96 }
97 return _arr;
98}
99function _unsupportedIterableToArray(o, minLen) {
100 if (!o) return;
101 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
102 var n = Object.prototype.toString.call(o).slice(8, -1);
103 if (n === "Object" && o.constructor) n = o.constructor.name;
104 if (n === "Map" || n === "Set") return Array.from(o);
105 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
106}
107function _arrayLikeToArray(arr, len) {
108 if (len == null || len > arr.length) len = arr.length;
109 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
110 return arr2;
111}
112function _nonIterableRest() {
113 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
114}
115function _createForOfIteratorHelper(o, allowArrayLike) {
116 var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
117 if (!it) {
118 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
119 if (it) o = it;
120 var i = 0;
121 var F = function () {};
122 return {
123 s: F,
124 n: function () {
125 if (i >= o.length) return {
126 done: true
127 };
128 return {
129 done: false,
130 value: o[i++]
131 };
132 },
133 e: function (e) {
134 throw e;
135 },
136 f: F
137 };
138 }
139 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
140 }
141 var normalCompletion = true,
142 didErr = false,
143 err;
144 return {
145 s: function () {
146 it = it.call(o);
147 },
148 n: function () {
149 var step = it.next();
150 normalCompletion = step.done;
151 return step;
152 },
153 e: function (e) {
154 didErr = true;
155 err = e;
156 },
157 f: function () {
158 try {
159 if (!normalCompletion && it.return != null) it.return();
160 } finally {
161 if (didErr) throw err;
162 }
163 }
164 };
165}
166
167var _window = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
168
169var navigator = _window ? _window.navigator : null;
170_window ? _window.document : null;
171var typeofstr = _typeof('');
172var typeofobj = _typeof({});
173var typeoffn = _typeof(function () {});
174var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
175var instanceStr = function instanceStr(obj) {
176 return obj && obj.instanceString && fn$6(obj.instanceString) ? obj.instanceString() : null;
177};
178
179var string = function string(obj) {
180 return obj != null && _typeof(obj) == typeofstr;
181};
182var fn$6 = function fn(obj) {
183 return obj != null && _typeof(obj) === typeoffn;
184};
185var array = function array(obj) {
186 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
187};
188var plainObject = function plainObject(obj) {
189 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
190};
191var object = function object(obj) {
192 return obj != null && _typeof(obj) === typeofobj;
193};
194var number$1 = function number(obj) {
195 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
196};
197var integer = function integer(obj) {
198 return number$1(obj) && Math.floor(obj) === obj;
199};
200var htmlElement = function htmlElement(obj) {
201 if ('undefined' === typeofhtmlele) {
202 return undefined;
203 } else {
204 return null != obj && obj instanceof HTMLElement;
205 }
206};
207var elementOrCollection = function elementOrCollection(obj) {
208 return element(obj) || collection(obj);
209};
210var element = function element(obj) {
211 return instanceStr(obj) === 'collection' && obj._private.single;
212};
213var collection = function collection(obj) {
214 return instanceStr(obj) === 'collection' && !obj._private.single;
215};
216var core = function core(obj) {
217 return instanceStr(obj) === 'core';
218};
219var stylesheet = function stylesheet(obj) {
220 return instanceStr(obj) === 'stylesheet';
221};
222var event = function event(obj) {
223 return instanceStr(obj) === 'event';
224};
225var emptyString = function emptyString(obj) {
226 if (obj === undefined || obj === null) {
227 // null is empty
228 return true;
229 } else if (obj === '' || obj.match(/^\s+$/)) {
230 return true; // empty string is empty
231 }
232
233 return false; // otherwise, we don't know what we've got
234};
235var domElement = function domElement(obj) {
236 if (typeof HTMLElement === 'undefined') {
237 return false; // we're not in a browser so it doesn't matter
238 } else {
239 return obj instanceof HTMLElement;
240 }
241};
242var boundingBox = function boundingBox(obj) {
243 return plainObject(obj) && number$1(obj.x1) && number$1(obj.x2) && number$1(obj.y1) && number$1(obj.y2);
244};
245var promise = function promise(obj) {
246 return object(obj) && fn$6(obj.then);
247};
248var ms = function ms() {
249 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
250}; // probably a better way to detect this...
251
252var memoize$1 = function memoize(fn, keyFn) {
253 if (!keyFn) {
254 keyFn = function keyFn() {
255 if (arguments.length === 1) {
256 return arguments[0];
257 } else if (arguments.length === 0) {
258 return 'undefined';
259 }
260 var args = [];
261 for (var i = 0; i < arguments.length; i++) {
262 args.push(arguments[i]);
263 }
264 return args.join('$');
265 };
266 }
267 var memoizedFn = function memoizedFn() {
268 var self = this;
269 var args = arguments;
270 var ret;
271 var k = keyFn.apply(self, args);
272 var cache = memoizedFn.cache;
273 if (!(ret = cache[k])) {
274 ret = cache[k] = fn.apply(self, args);
275 }
276 return ret;
277 };
278 memoizedFn.cache = {};
279 return memoizedFn;
280};
281
282var camel2dash = memoize$1(function (str) {
283 return str.replace(/([A-Z])/g, function (v) {
284 return '-' + v.toLowerCase();
285 });
286});
287var dash2camel = memoize$1(function (str) {
288 return str.replace(/(-\w)/g, function (v) {
289 return v[1].toUpperCase();
290 });
291});
292var prependCamel = memoize$1(function (prefix, str) {
293 return prefix + str[0].toUpperCase() + str.substring(1);
294}, function (prefix, str) {
295 return prefix + '$' + str;
296});
297var capitalize = function capitalize(str) {
298 if (emptyString(str)) {
299 return str;
300 }
301 return str.charAt(0).toUpperCase() + str.substring(1);
302};
303
304var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
305var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)';
306var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)';
307var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)';
308var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)';
309var hex3 = '\\#[0-9a-fA-F]{3}';
310var hex6 = '\\#[0-9a-fA-F]{6}';
311
312var ascending = function ascending(a, b) {
313 if (a < b) {
314 return -1;
315 } else if (a > b) {
316 return 1;
317 } else {
318 return 0;
319 }
320};
321var descending = function descending(a, b) {
322 return -1 * ascending(a, b);
323};
324
325var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
326 var args = arguments;
327 for (var i = 1; i < args.length; i++) {
328 var obj = args[i];
329 if (obj == null) {
330 continue;
331 }
332 var keys = Object.keys(obj);
333 for (var j = 0; j < keys.length; j++) {
334 var k = keys[j];
335 tgt[k] = obj[k];
336 }
337 }
338 return tgt;
339};
340
341// get [r, g, b] from #abc or #aabbcc
342var hex2tuple = function hex2tuple(hex) {
343 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
344 return;
345 }
346 var shortHex = hex.length === 4;
347 var r, g, b;
348 var base = 16;
349 if (shortHex) {
350 r = parseInt(hex[1] + hex[1], base);
351 g = parseInt(hex[2] + hex[2], base);
352 b = parseInt(hex[3] + hex[3], base);
353 } else {
354 r = parseInt(hex[1] + hex[2], base);
355 g = parseInt(hex[3] + hex[4], base);
356 b = parseInt(hex[5] + hex[6], base);
357 }
358 return [r, g, b];
359};
360
361// get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
362var hsl2tuple = function hsl2tuple(hsl) {
363 var ret;
364 var h, s, l, a, r, g, b;
365 function hue2rgb(p, q, t) {
366 if (t < 0) t += 1;
367 if (t > 1) t -= 1;
368 if (t < 1 / 6) return p + (q - p) * 6 * t;
369 if (t < 1 / 2) return q;
370 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
371 return p;
372 }
373 var m = new RegExp('^' + hsla + '$').exec(hsl);
374 if (m) {
375 // get hue
376 h = parseInt(m[1]);
377 if (h < 0) {
378 h = (360 - -1 * h % 360) % 360;
379 } else if (h > 360) {
380 h = h % 360;
381 }
382 h /= 360; // normalise on [0, 1]
383
384 s = parseFloat(m[2]);
385 if (s < 0 || s > 100) {
386 return;
387 } // saturation is [0, 100]
388 s = s / 100; // normalise on [0, 1]
389
390 l = parseFloat(m[3]);
391 if (l < 0 || l > 100) {
392 return;
393 } // lightness is [0, 100]
394 l = l / 100; // normalise on [0, 1]
395
396 a = m[4];
397 if (a !== undefined) {
398 a = parseFloat(a);
399 if (a < 0 || a > 1) {
400 return;
401 } // alpha is [0, 1]
402 }
403
404 // now, convert to rgb
405 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
406 if (s === 0) {
407 r = g = b = Math.round(l * 255); // achromatic
408 } else {
409 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
410 var p = 2 * l - q;
411 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
412 g = Math.round(255 * hue2rgb(p, q, h));
413 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
414 }
415 ret = [r, g, b, a];
416 }
417 return ret;
418};
419
420// get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
421var rgb2tuple = function rgb2tuple(rgb) {
422 var ret;
423 var m = new RegExp('^' + rgba + '$').exec(rgb);
424 if (m) {
425 ret = [];
426 var isPct = [];
427 for (var i = 1; i <= 3; i++) {
428 var channel = m[i];
429 if (channel[channel.length - 1] === '%') {
430 isPct[i] = true;
431 }
432 channel = parseFloat(channel);
433 if (isPct[i]) {
434 channel = channel / 100 * 255; // normalise to [0, 255]
435 }
436
437 if (channel < 0 || channel > 255) {
438 return;
439 } // invalid channel value
440
441 ret.push(Math.floor(channel));
442 }
443 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
444 var allArePct = isPct[1] && isPct[2] && isPct[3];
445 if (atLeastOneIsPct && !allArePct) {
446 return;
447 } // must all be percent values if one is
448
449 var alpha = m[4];
450 if (alpha !== undefined) {
451 alpha = parseFloat(alpha);
452 if (alpha < 0 || alpha > 1) {
453 return;
454 } // invalid alpha value
455
456 ret.push(alpha);
457 }
458 }
459 return ret;
460};
461var colorname2tuple = function colorname2tuple(color) {
462 return colors[color.toLowerCase()];
463};
464var color2tuple = function color2tuple(color) {
465 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
466};
467var colors = {
468 // special colour names
469 transparent: [0, 0, 0, 0],
470 // NB alpha === 0
471
472 // regular colours
473 aliceblue: [240, 248, 255],
474 antiquewhite: [250, 235, 215],
475 aqua: [0, 255, 255],
476 aquamarine: [127, 255, 212],
477 azure: [240, 255, 255],
478 beige: [245, 245, 220],
479 bisque: [255, 228, 196],
480 black: [0, 0, 0],
481 blanchedalmond: [255, 235, 205],
482 blue: [0, 0, 255],
483 blueviolet: [138, 43, 226],
484 brown: [165, 42, 42],
485 burlywood: [222, 184, 135],
486 cadetblue: [95, 158, 160],
487 chartreuse: [127, 255, 0],
488 chocolate: [210, 105, 30],
489 coral: [255, 127, 80],
490 cornflowerblue: [100, 149, 237],
491 cornsilk: [255, 248, 220],
492 crimson: [220, 20, 60],
493 cyan: [0, 255, 255],
494 darkblue: [0, 0, 139],
495 darkcyan: [0, 139, 139],
496 darkgoldenrod: [184, 134, 11],
497 darkgray: [169, 169, 169],
498 darkgreen: [0, 100, 0],
499 darkgrey: [169, 169, 169],
500 darkkhaki: [189, 183, 107],
501 darkmagenta: [139, 0, 139],
502 darkolivegreen: [85, 107, 47],
503 darkorange: [255, 140, 0],
504 darkorchid: [153, 50, 204],
505 darkred: [139, 0, 0],
506 darksalmon: [233, 150, 122],
507 darkseagreen: [143, 188, 143],
508 darkslateblue: [72, 61, 139],
509 darkslategray: [47, 79, 79],
510 darkslategrey: [47, 79, 79],
511 darkturquoise: [0, 206, 209],
512 darkviolet: [148, 0, 211],
513 deeppink: [255, 20, 147],
514 deepskyblue: [0, 191, 255],
515 dimgray: [105, 105, 105],
516 dimgrey: [105, 105, 105],
517 dodgerblue: [30, 144, 255],
518 firebrick: [178, 34, 34],
519 floralwhite: [255, 250, 240],
520 forestgreen: [34, 139, 34],
521 fuchsia: [255, 0, 255],
522 gainsboro: [220, 220, 220],
523 ghostwhite: [248, 248, 255],
524 gold: [255, 215, 0],
525 goldenrod: [218, 165, 32],
526 gray: [128, 128, 128],
527 grey: [128, 128, 128],
528 green: [0, 128, 0],
529 greenyellow: [173, 255, 47],
530 honeydew: [240, 255, 240],
531 hotpink: [255, 105, 180],
532 indianred: [205, 92, 92],
533 indigo: [75, 0, 130],
534 ivory: [255, 255, 240],
535 khaki: [240, 230, 140],
536 lavender: [230, 230, 250],
537 lavenderblush: [255, 240, 245],
538 lawngreen: [124, 252, 0],
539 lemonchiffon: [255, 250, 205],
540 lightblue: [173, 216, 230],
541 lightcoral: [240, 128, 128],
542 lightcyan: [224, 255, 255],
543 lightgoldenrodyellow: [250, 250, 210],
544 lightgray: [211, 211, 211],
545 lightgreen: [144, 238, 144],
546 lightgrey: [211, 211, 211],
547 lightpink: [255, 182, 193],
548 lightsalmon: [255, 160, 122],
549 lightseagreen: [32, 178, 170],
550 lightskyblue: [135, 206, 250],
551 lightslategray: [119, 136, 153],
552 lightslategrey: [119, 136, 153],
553 lightsteelblue: [176, 196, 222],
554 lightyellow: [255, 255, 224],
555 lime: [0, 255, 0],
556 limegreen: [50, 205, 50],
557 linen: [250, 240, 230],
558 magenta: [255, 0, 255],
559 maroon: [128, 0, 0],
560 mediumaquamarine: [102, 205, 170],
561 mediumblue: [0, 0, 205],
562 mediumorchid: [186, 85, 211],
563 mediumpurple: [147, 112, 219],
564 mediumseagreen: [60, 179, 113],
565 mediumslateblue: [123, 104, 238],
566 mediumspringgreen: [0, 250, 154],
567 mediumturquoise: [72, 209, 204],
568 mediumvioletred: [199, 21, 133],
569 midnightblue: [25, 25, 112],
570 mintcream: [245, 255, 250],
571 mistyrose: [255, 228, 225],
572 moccasin: [255, 228, 181],
573 navajowhite: [255, 222, 173],
574 navy: [0, 0, 128],
575 oldlace: [253, 245, 230],
576 olive: [128, 128, 0],
577 olivedrab: [107, 142, 35],
578 orange: [255, 165, 0],
579 orangered: [255, 69, 0],
580 orchid: [218, 112, 214],
581 palegoldenrod: [238, 232, 170],
582 palegreen: [152, 251, 152],
583 paleturquoise: [175, 238, 238],
584 palevioletred: [219, 112, 147],
585 papayawhip: [255, 239, 213],
586 peachpuff: [255, 218, 185],
587 peru: [205, 133, 63],
588 pink: [255, 192, 203],
589 plum: [221, 160, 221],
590 powderblue: [176, 224, 230],
591 purple: [128, 0, 128],
592 red: [255, 0, 0],
593 rosybrown: [188, 143, 143],
594 royalblue: [65, 105, 225],
595 saddlebrown: [139, 69, 19],
596 salmon: [250, 128, 114],
597 sandybrown: [244, 164, 96],
598 seagreen: [46, 139, 87],
599 seashell: [255, 245, 238],
600 sienna: [160, 82, 45],
601 silver: [192, 192, 192],
602 skyblue: [135, 206, 235],
603 slateblue: [106, 90, 205],
604 slategray: [112, 128, 144],
605 slategrey: [112, 128, 144],
606 snow: [255, 250, 250],
607 springgreen: [0, 255, 127],
608 steelblue: [70, 130, 180],
609 tan: [210, 180, 140],
610 teal: [0, 128, 128],
611 thistle: [216, 191, 216],
612 tomato: [255, 99, 71],
613 turquoise: [64, 224, 208],
614 violet: [238, 130, 238],
615 wheat: [245, 222, 179],
616 white: [255, 255, 255],
617 whitesmoke: [245, 245, 245],
618 yellow: [255, 255, 0],
619 yellowgreen: [154, 205, 50]
620};
621
622// sets the value in a map (map may not be built)
623var setMap = function setMap(options) {
624 var obj = options.map;
625 var keys = options.keys;
626 var l = keys.length;
627 for (var i = 0; i < l; i++) {
628 var key = keys[i];
629 if (plainObject(key)) {
630 throw Error('Tried to set map with object key');
631 }
632 if (i < keys.length - 1) {
633 // extend the map if necessary
634 if (obj[key] == null) {
635 obj[key] = {};
636 }
637 obj = obj[key];
638 } else {
639 // set the value
640 obj[key] = options.value;
641 }
642 }
643};
644
645// gets the value in a map even if it's not built in places
646var getMap = function getMap(options) {
647 var obj = options.map;
648 var keys = options.keys;
649 var l = keys.length;
650 for (var i = 0; i < l; i++) {
651 var key = keys[i];
652 if (plainObject(key)) {
653 throw Error('Tried to get map with object key');
654 }
655 obj = obj[key];
656 if (obj == null) {
657 return obj;
658 }
659 }
660 return obj;
661};
662
663/**
664 * Checks if `value` is the
665 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
666 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
667 *
668 * @static
669 * @memberOf _
670 * @since 0.1.0
671 * @category Lang
672 * @param {*} value The value to check.
673 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
674 * @example
675 *
676 * _.isObject({});
677 * // => true
678 *
679 * _.isObject([1, 2, 3]);
680 * // => true
681 *
682 * _.isObject(_.noop);
683 * // => true
684 *
685 * _.isObject(null);
686 * // => false
687 */
688function isObject(value) {
689 var type = typeof value;
690 return value != null && (type == 'object' || type == 'function');
691}
692
693var isObject_1 = isObject;
694
695var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
696
697function createCommonjsModule(fn, module) {
698 return module = { exports: {} }, fn(module, module.exports), module.exports;
699}
700
701/** Detect free variable `global` from Node.js. */
702var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
703
704var _freeGlobal = freeGlobal;
705
706/** Detect free variable `self`. */
707var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
708
709/** Used as a reference to the global object. */
710var root = _freeGlobal || freeSelf || Function('return this')();
711
712var _root = root;
713
714/**
715 * Gets the timestamp of the number of milliseconds that have elapsed since
716 * the Unix epoch (1 January 1970 00:00:00 UTC).
717 *
718 * @static
719 * @memberOf _
720 * @since 2.4.0
721 * @category Date
722 * @returns {number} Returns the timestamp.
723 * @example
724 *
725 * _.defer(function(stamp) {
726 * console.log(_.now() - stamp);
727 * }, _.now());
728 * // => Logs the number of milliseconds it took for the deferred invocation.
729 */
730var now = function() {
731 return _root.Date.now();
732};
733
734var now_1 = now;
735
736/** Used to match a single whitespace character. */
737var reWhitespace = /\s/;
738
739/**
740 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
741 * character of `string`.
742 *
743 * @private
744 * @param {string} string The string to inspect.
745 * @returns {number} Returns the index of the last non-whitespace character.
746 */
747function trimmedEndIndex(string) {
748 var index = string.length;
749
750 while (index-- && reWhitespace.test(string.charAt(index))) {}
751 return index;
752}
753
754var _trimmedEndIndex = trimmedEndIndex;
755
756/** Used to match leading whitespace. */
757var reTrimStart = /^\s+/;
758
759/**
760 * The base implementation of `_.trim`.
761 *
762 * @private
763 * @param {string} string The string to trim.
764 * @returns {string} Returns the trimmed string.
765 */
766function baseTrim(string) {
767 return string
768 ? string.slice(0, _trimmedEndIndex(string) + 1).replace(reTrimStart, '')
769 : string;
770}
771
772var _baseTrim = baseTrim;
773
774/** Built-in value references. */
775var Symbol$1 = _root.Symbol;
776
777var _Symbol = Symbol$1;
778
779/** Used for built-in method references. */
780var objectProto$5 = Object.prototype;
781
782/** Used to check objects for own properties. */
783var hasOwnProperty$4 = objectProto$5.hasOwnProperty;
784
785/**
786 * Used to resolve the
787 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
788 * of values.
789 */
790var nativeObjectToString$1 = objectProto$5.toString;
791
792/** Built-in value references. */
793var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
794
795/**
796 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
797 *
798 * @private
799 * @param {*} value The value to query.
800 * @returns {string} Returns the raw `toStringTag`.
801 */
802function getRawTag(value) {
803 var isOwn = hasOwnProperty$4.call(value, symToStringTag$1),
804 tag = value[symToStringTag$1];
805
806 try {
807 value[symToStringTag$1] = undefined;
808 var unmasked = true;
809 } catch (e) {}
810
811 var result = nativeObjectToString$1.call(value);
812 if (unmasked) {
813 if (isOwn) {
814 value[symToStringTag$1] = tag;
815 } else {
816 delete value[symToStringTag$1];
817 }
818 }
819 return result;
820}
821
822var _getRawTag = getRawTag;
823
824/** Used for built-in method references. */
825var objectProto$4 = Object.prototype;
826
827/**
828 * Used to resolve the
829 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
830 * of values.
831 */
832var nativeObjectToString = objectProto$4.toString;
833
834/**
835 * Converts `value` to a string using `Object.prototype.toString`.
836 *
837 * @private
838 * @param {*} value The value to convert.
839 * @returns {string} Returns the converted string.
840 */
841function objectToString(value) {
842 return nativeObjectToString.call(value);
843}
844
845var _objectToString = objectToString;
846
847/** `Object#toString` result references. */
848var nullTag = '[object Null]',
849 undefinedTag = '[object Undefined]';
850
851/** Built-in value references. */
852var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
853
854/**
855 * The base implementation of `getTag` without fallbacks for buggy environments.
856 *
857 * @private
858 * @param {*} value The value to query.
859 * @returns {string} Returns the `toStringTag`.
860 */
861function baseGetTag(value) {
862 if (value == null) {
863 return value === undefined ? undefinedTag : nullTag;
864 }
865 return (symToStringTag && symToStringTag in Object(value))
866 ? _getRawTag(value)
867 : _objectToString(value);
868}
869
870var _baseGetTag = baseGetTag;
871
872/**
873 * Checks if `value` is object-like. A value is object-like if it's not `null`
874 * and has a `typeof` result of "object".
875 *
876 * @static
877 * @memberOf _
878 * @since 4.0.0
879 * @category Lang
880 * @param {*} value The value to check.
881 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
882 * @example
883 *
884 * _.isObjectLike({});
885 * // => true
886 *
887 * _.isObjectLike([1, 2, 3]);
888 * // => true
889 *
890 * _.isObjectLike(_.noop);
891 * // => false
892 *
893 * _.isObjectLike(null);
894 * // => false
895 */
896function isObjectLike(value) {
897 return value != null && typeof value == 'object';
898}
899
900var isObjectLike_1 = isObjectLike;
901
902/** `Object#toString` result references. */
903var symbolTag = '[object Symbol]';
904
905/**
906 * Checks if `value` is classified as a `Symbol` primitive or object.
907 *
908 * @static
909 * @memberOf _
910 * @since 4.0.0
911 * @category Lang
912 * @param {*} value The value to check.
913 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
914 * @example
915 *
916 * _.isSymbol(Symbol.iterator);
917 * // => true
918 *
919 * _.isSymbol('abc');
920 * // => false
921 */
922function isSymbol(value) {
923 return typeof value == 'symbol' ||
924 (isObjectLike_1(value) && _baseGetTag(value) == symbolTag);
925}
926
927var isSymbol_1 = isSymbol;
928
929/** Used as references for various `Number` constants. */
930var NAN = 0 / 0;
931
932/** Used to detect bad signed hexadecimal string values. */
933var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
934
935/** Used to detect binary string values. */
936var reIsBinary = /^0b[01]+$/i;
937
938/** Used to detect octal string values. */
939var reIsOctal = /^0o[0-7]+$/i;
940
941/** Built-in method references without a dependency on `root`. */
942var freeParseInt = parseInt;
943
944/**
945 * Converts `value` to a number.
946 *
947 * @static
948 * @memberOf _
949 * @since 4.0.0
950 * @category Lang
951 * @param {*} value The value to process.
952 * @returns {number} Returns the number.
953 * @example
954 *
955 * _.toNumber(3.2);
956 * // => 3.2
957 *
958 * _.toNumber(Number.MIN_VALUE);
959 * // => 5e-324
960 *
961 * _.toNumber(Infinity);
962 * // => Infinity
963 *
964 * _.toNumber('3.2');
965 * // => 3.2
966 */
967function toNumber(value) {
968 if (typeof value == 'number') {
969 return value;
970 }
971 if (isSymbol_1(value)) {
972 return NAN;
973 }
974 if (isObject_1(value)) {
975 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
976 value = isObject_1(other) ? (other + '') : other;
977 }
978 if (typeof value != 'string') {
979 return value === 0 ? value : +value;
980 }
981 value = _baseTrim(value);
982 var isBinary = reIsBinary.test(value);
983 return (isBinary || reIsOctal.test(value))
984 ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
985 : (reIsBadHex.test(value) ? NAN : +value);
986}
987
988var toNumber_1 = toNumber;
989
990/** Error message constants. */
991var FUNC_ERROR_TEXT$1 = 'Expected a function';
992
993/* Built-in method references for those with the same name as other `lodash` methods. */
994var nativeMax = Math.max,
995 nativeMin = Math.min;
996
997/**
998 * Creates a debounced function that delays invoking `func` until after `wait`
999 * milliseconds have elapsed since the last time the debounced function was
1000 * invoked. The debounced function comes with a `cancel` method to cancel
1001 * delayed `func` invocations and a `flush` method to immediately invoke them.
1002 * Provide `options` to indicate whether `func` should be invoked on the
1003 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
1004 * with the last arguments provided to the debounced function. Subsequent
1005 * calls to the debounced function return the result of the last `func`
1006 * invocation.
1007 *
1008 * **Note:** If `leading` and `trailing` options are `true`, `func` is
1009 * invoked on the trailing edge of the timeout only if the debounced function
1010 * is invoked more than once during the `wait` timeout.
1011 *
1012 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
1013 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
1014 *
1015 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
1016 * for details over the differences between `_.debounce` and `_.throttle`.
1017 *
1018 * @static
1019 * @memberOf _
1020 * @since 0.1.0
1021 * @category Function
1022 * @param {Function} func The function to debounce.
1023 * @param {number} [wait=0] The number of milliseconds to delay.
1024 * @param {Object} [options={}] The options object.
1025 * @param {boolean} [options.leading=false]
1026 * Specify invoking on the leading edge of the timeout.
1027 * @param {number} [options.maxWait]
1028 * The maximum time `func` is allowed to be delayed before it's invoked.
1029 * @param {boolean} [options.trailing=true]
1030 * Specify invoking on the trailing edge of the timeout.
1031 * @returns {Function} Returns the new debounced function.
1032 * @example
1033 *
1034 * // Avoid costly calculations while the window size is in flux.
1035 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
1036 *
1037 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
1038 * jQuery(element).on('click', _.debounce(sendMail, 300, {
1039 * 'leading': true,
1040 * 'trailing': false
1041 * }));
1042 *
1043 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
1044 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
1045 * var source = new EventSource('/stream');
1046 * jQuery(source).on('message', debounced);
1047 *
1048 * // Cancel the trailing debounced invocation.
1049 * jQuery(window).on('popstate', debounced.cancel);
1050 */
1051function debounce(func, wait, options) {
1052 var lastArgs,
1053 lastThis,
1054 maxWait,
1055 result,
1056 timerId,
1057 lastCallTime,
1058 lastInvokeTime = 0,
1059 leading = false,
1060 maxing = false,
1061 trailing = true;
1062
1063 if (typeof func != 'function') {
1064 throw new TypeError(FUNC_ERROR_TEXT$1);
1065 }
1066 wait = toNumber_1(wait) || 0;
1067 if (isObject_1(options)) {
1068 leading = !!options.leading;
1069 maxing = 'maxWait' in options;
1070 maxWait = maxing ? nativeMax(toNumber_1(options.maxWait) || 0, wait) : maxWait;
1071 trailing = 'trailing' in options ? !!options.trailing : trailing;
1072 }
1073
1074 function invokeFunc(time) {
1075 var args = lastArgs,
1076 thisArg = lastThis;
1077
1078 lastArgs = lastThis = undefined;
1079 lastInvokeTime = time;
1080 result = func.apply(thisArg, args);
1081 return result;
1082 }
1083
1084 function leadingEdge(time) {
1085 // Reset any `maxWait` timer.
1086 lastInvokeTime = time;
1087 // Start the timer for the trailing edge.
1088 timerId = setTimeout(timerExpired, wait);
1089 // Invoke the leading edge.
1090 return leading ? invokeFunc(time) : result;
1091 }
1092
1093 function remainingWait(time) {
1094 var timeSinceLastCall = time - lastCallTime,
1095 timeSinceLastInvoke = time - lastInvokeTime,
1096 timeWaiting = wait - timeSinceLastCall;
1097
1098 return maxing
1099 ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
1100 : timeWaiting;
1101 }
1102
1103 function shouldInvoke(time) {
1104 var timeSinceLastCall = time - lastCallTime,
1105 timeSinceLastInvoke = time - lastInvokeTime;
1106
1107 // Either this is the first call, activity has stopped and we're at the
1108 // trailing edge, the system time has gone backwards and we're treating
1109 // it as the trailing edge, or we've hit the `maxWait` limit.
1110 return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
1111 (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
1112 }
1113
1114 function timerExpired() {
1115 var time = now_1();
1116 if (shouldInvoke(time)) {
1117 return trailingEdge(time);
1118 }
1119 // Restart the timer.
1120 timerId = setTimeout(timerExpired, remainingWait(time));
1121 }
1122
1123 function trailingEdge(time) {
1124 timerId = undefined;
1125
1126 // Only invoke if we have `lastArgs` which means `func` has been
1127 // debounced at least once.
1128 if (trailing && lastArgs) {
1129 return invokeFunc(time);
1130 }
1131 lastArgs = lastThis = undefined;
1132 return result;
1133 }
1134
1135 function cancel() {
1136 if (timerId !== undefined) {
1137 clearTimeout(timerId);
1138 }
1139 lastInvokeTime = 0;
1140 lastArgs = lastCallTime = lastThis = timerId = undefined;
1141 }
1142
1143 function flush() {
1144 return timerId === undefined ? result : trailingEdge(now_1());
1145 }
1146
1147 function debounced() {
1148 var time = now_1(),
1149 isInvoking = shouldInvoke(time);
1150
1151 lastArgs = arguments;
1152 lastThis = this;
1153 lastCallTime = time;
1154
1155 if (isInvoking) {
1156 if (timerId === undefined) {
1157 return leadingEdge(lastCallTime);
1158 }
1159 if (maxing) {
1160 // Handle invocations in a tight loop.
1161 clearTimeout(timerId);
1162 timerId = setTimeout(timerExpired, wait);
1163 return invokeFunc(lastCallTime);
1164 }
1165 }
1166 if (timerId === undefined) {
1167 timerId = setTimeout(timerExpired, wait);
1168 }
1169 return result;
1170 }
1171 debounced.cancel = cancel;
1172 debounced.flush = flush;
1173 return debounced;
1174}
1175
1176var debounce_1 = debounce;
1177
1178var performance = _window ? _window.performance : null;
1179var pnow = performance && performance.now ? function () {
1180 return performance.now();
1181} : function () {
1182 return Date.now();
1183};
1184var raf = function () {
1185 if (_window) {
1186 if (_window.requestAnimationFrame) {
1187 return function (fn) {
1188 _window.requestAnimationFrame(fn);
1189 };
1190 } else if (_window.mozRequestAnimationFrame) {
1191 return function (fn) {
1192 _window.mozRequestAnimationFrame(fn);
1193 };
1194 } else if (_window.webkitRequestAnimationFrame) {
1195 return function (fn) {
1196 _window.webkitRequestAnimationFrame(fn);
1197 };
1198 } else if (_window.msRequestAnimationFrame) {
1199 return function (fn) {
1200 _window.msRequestAnimationFrame(fn);
1201 };
1202 }
1203 }
1204 return function (fn) {
1205 if (fn) {
1206 setTimeout(function () {
1207 fn(pnow());
1208 }, 1000 / 60);
1209 }
1210 };
1211}();
1212var requestAnimationFrame = function requestAnimationFrame(fn) {
1213 return raf(fn);
1214};
1215var performanceNow = pnow;
1216
1217var DEFAULT_HASH_SEED = 9261;
1218var K = 65599; // 37 also works pretty well
1219var DEFAULT_HASH_SEED_ALT = 5381;
1220var hashIterableInts = function hashIterableInts(iterator) {
1221 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1222 // sdbm/string-hash
1223 var hash = seed;
1224 var entry;
1225 for (;;) {
1226 entry = iterator.next();
1227 if (entry.done) {
1228 break;
1229 }
1230 hash = hash * K + entry.value | 0;
1231 }
1232 return hash;
1233};
1234var hashInt = function hashInt(num) {
1235 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1236 // sdbm/string-hash
1237 return seed * K + num | 0;
1238};
1239var hashIntAlt = function hashIntAlt(num) {
1240 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
1241 // djb2/string-hash
1242 return (seed << 5) + seed + num | 0;
1243};
1244var combineHashes = function combineHashes(hash1, hash2) {
1245 return hash1 * 0x200000 + hash2;
1246};
1247var combineHashesArray = function combineHashesArray(hashes) {
1248 return hashes[0] * 0x200000 + hashes[1];
1249};
1250var hashArrays = function hashArrays(hashes1, hashes2) {
1251 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
1252};
1253var hashIntsArray = function hashIntsArray(ints, seed) {
1254 var entry = {
1255 value: 0,
1256 done: false
1257 };
1258 var i = 0;
1259 var length = ints.length;
1260 var iterator = {
1261 next: function next() {
1262 if (i < length) {
1263 entry.value = ints[i++];
1264 } else {
1265 entry.done = true;
1266 }
1267 return entry;
1268 }
1269 };
1270 return hashIterableInts(iterator, seed);
1271};
1272var hashString = function hashString(str, seed) {
1273 var entry = {
1274 value: 0,
1275 done: false
1276 };
1277 var i = 0;
1278 var length = str.length;
1279 var iterator = {
1280 next: function next() {
1281 if (i < length) {
1282 entry.value = str.charCodeAt(i++);
1283 } else {
1284 entry.done = true;
1285 }
1286 return entry;
1287 }
1288 };
1289 return hashIterableInts(iterator, seed);
1290};
1291var hashStrings = function hashStrings() {
1292 return hashStringsArray(arguments);
1293};
1294var hashStringsArray = function hashStringsArray(strs) {
1295 var hash;
1296 for (var i = 0; i < strs.length; i++) {
1297 var str = strs[i];
1298 if (i === 0) {
1299 hash = hashString(str);
1300 } else {
1301 hash = hashString(str, hash);
1302 }
1303 }
1304 return hash;
1305};
1306
1307/*global console */
1308var warningsEnabled = true;
1309var warnSupported = console.warn != null; // eslint-disable-line no-console
1310var traceSupported = console.trace != null; // eslint-disable-line no-console
1311
1312var MAX_INT$1 = Number.MAX_SAFE_INTEGER || 9007199254740991;
1313var trueify = function trueify() {
1314 return true;
1315};
1316var falsify = function falsify() {
1317 return false;
1318};
1319var zeroify = function zeroify() {
1320 return 0;
1321};
1322var noop$1 = function noop() {};
1323var error = function error(msg) {
1324 throw new Error(msg);
1325};
1326var warnings = function warnings(enabled) {
1327 if (enabled !== undefined) {
1328 warningsEnabled = !!enabled;
1329 } else {
1330 return warningsEnabled;
1331 }
1332};
1333var warn = function warn(msg) {
1334 /* eslint-disable no-console */
1335 if (!warnings()) {
1336 return;
1337 }
1338 if (warnSupported) {
1339 console.warn(msg);
1340 } else {
1341 console.log(msg);
1342 if (traceSupported) {
1343 console.trace();
1344 }
1345 }
1346}; /* eslint-enable */
1347
1348var clone = function clone(obj) {
1349 return extend({}, obj);
1350};
1351
1352// gets a shallow copy of the argument
1353var copy = function copy(obj) {
1354 if (obj == null) {
1355 return obj;
1356 }
1357 if (array(obj)) {
1358 return obj.slice();
1359 } else if (plainObject(obj)) {
1360 return clone(obj);
1361 } else {
1362 return obj;
1363 }
1364};
1365var copyArray$1 = function copyArray(arr) {
1366 return arr.slice();
1367};
1368var uuid = function uuid(a, b /* placeholders */) {
1369 for (
1370 // loop :)
1371 b = a = '';
1372 // b - result , a - numeric letiable
1373 a++ < 36;
1374 //
1375 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
1376 ?
1377 // return a random number or 4
1378 (a ^ 15 // if "a" is not 15
1379 ?
1380 // generate a random number from 0 to 15
1381 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
1382 : 4 // otherwise 4
1383 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
1384 ) {
1385 }
1386 return b;
1387};
1388var _staticEmptyObject = {};
1389var staticEmptyObject = function staticEmptyObject() {
1390 return _staticEmptyObject;
1391};
1392var defaults$g = function defaults(_defaults) {
1393 var keys = Object.keys(_defaults);
1394 return function (opts) {
1395 var filledOpts = {};
1396 for (var i = 0; i < keys.length; i++) {
1397 var key = keys[i];
1398 var optVal = opts == null ? undefined : opts[key];
1399 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
1400 }
1401 return filledOpts;
1402 };
1403};
1404var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
1405 for (var i = arr.length - 1; i >= 0; i--) {
1406 if (arr[i] === ele) {
1407 arr.splice(i, 1);
1408 if (oneCopy) {
1409 break;
1410 }
1411 }
1412 }
1413};
1414var clearArray = function clearArray(arr) {
1415 arr.splice(0, arr.length);
1416};
1417var push = function push(arr, otherArr) {
1418 for (var i = 0; i < otherArr.length; i++) {
1419 var el = otherArr[i];
1420 arr.push(el);
1421 }
1422};
1423var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
1424 if (prefix) {
1425 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1426 }
1427
1428 return obj[propName];
1429};
1430var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
1431 if (prefix) {
1432 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1433 }
1434
1435 obj[propName] = value;
1436};
1437
1438/* global Map */
1439var ObjectMap = /*#__PURE__*/function () {
1440 function ObjectMap() {
1441 _classCallCheck(this, ObjectMap);
1442 this._obj = {};
1443 }
1444 _createClass(ObjectMap, [{
1445 key: "set",
1446 value: function set(key, val) {
1447 this._obj[key] = val;
1448 return this;
1449 }
1450 }, {
1451 key: "delete",
1452 value: function _delete(key) {
1453 this._obj[key] = undefined;
1454 return this;
1455 }
1456 }, {
1457 key: "clear",
1458 value: function clear() {
1459 this._obj = {};
1460 }
1461 }, {
1462 key: "has",
1463 value: function has(key) {
1464 return this._obj[key] !== undefined;
1465 }
1466 }, {
1467 key: "get",
1468 value: function get(key) {
1469 return this._obj[key];
1470 }
1471 }]);
1472 return ObjectMap;
1473}();
1474var Map$2 = typeof Map !== 'undefined' ? Map : ObjectMap;
1475
1476/* global Set */
1477
1478var undef = "undefined" ;
1479var ObjectSet = /*#__PURE__*/function () {
1480 function ObjectSet(arrayOrObjectSet) {
1481 _classCallCheck(this, ObjectSet);
1482 this._obj = Object.create(null);
1483 this.size = 0;
1484 if (arrayOrObjectSet != null) {
1485 var arr;
1486 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1487 arr = arrayOrObjectSet.toArray();
1488 } else {
1489 arr = arrayOrObjectSet;
1490 }
1491 for (var i = 0; i < arr.length; i++) {
1492 this.add(arr[i]);
1493 }
1494 }
1495 }
1496 _createClass(ObjectSet, [{
1497 key: "instanceString",
1498 value: function instanceString() {
1499 return 'set';
1500 }
1501 }, {
1502 key: "add",
1503 value: function add(val) {
1504 var o = this._obj;
1505 if (o[val] !== 1) {
1506 o[val] = 1;
1507 this.size++;
1508 }
1509 }
1510 }, {
1511 key: "delete",
1512 value: function _delete(val) {
1513 var o = this._obj;
1514 if (o[val] === 1) {
1515 o[val] = 0;
1516 this.size--;
1517 }
1518 }
1519 }, {
1520 key: "clear",
1521 value: function clear() {
1522 this._obj = Object.create(null);
1523 }
1524 }, {
1525 key: "has",
1526 value: function has(val) {
1527 return this._obj[val] === 1;
1528 }
1529 }, {
1530 key: "toArray",
1531 value: function toArray() {
1532 var _this = this;
1533 return Object.keys(this._obj).filter(function (key) {
1534 return _this.has(key);
1535 });
1536 }
1537 }, {
1538 key: "forEach",
1539 value: function forEach(callback, thisArg) {
1540 return this.toArray().forEach(callback, thisArg);
1541 }
1542 }]);
1543 return ObjectSet;
1544}();
1545var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1546
1547// represents a node or an edge
1548var Element = function Element(cy, params) {
1549 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1550 if (cy === undefined || params === undefined || !core(cy)) {
1551 error('An element must have a core reference and parameters set');
1552 return;
1553 }
1554 var group = params.group;
1555
1556 // try to automatically infer the group if unspecified
1557 if (group == null) {
1558 if (params.data && params.data.source != null && params.data.target != null) {
1559 group = 'edges';
1560 } else {
1561 group = 'nodes';
1562 }
1563 }
1564
1565 // validate group
1566 if (group !== 'nodes' && group !== 'edges') {
1567 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1568 return;
1569 }
1570
1571 // make the element array-like, just like a collection
1572 this.length = 1;
1573 this[0] = this;
1574
1575 // NOTE: when something is added here, add also to ele.json()
1576 var _p = this._private = {
1577 cy: cy,
1578 single: true,
1579 // indicates this is an element
1580 data: params.data || {},
1581 // data object
1582 position: params.position || {
1583 x: 0,
1584 y: 0
1585 },
1586 // (x, y) position pair
1587 autoWidth: undefined,
1588 // width and height of nodes calculated by the renderer when set to special 'auto' value
1589 autoHeight: undefined,
1590 autoPadding: undefined,
1591 compoundBoundsClean: false,
1592 // whether the compound dimensions need to be recalculated the next time dimensions are read
1593 listeners: [],
1594 // array of bound listeners
1595 group: group,
1596 // string; 'nodes' or 'edges'
1597 style: {},
1598 // properties as set by the style
1599 rstyle: {},
1600 // properties for style sent from the renderer to the core
1601 styleCxts: [],
1602 // applied style contexts from the styler
1603 styleKeys: {},
1604 // per-group keys of style property values
1605 removed: true,
1606 // whether it's inside the vis; true if removed (set true here since we call restore)
1607 selected: params.selected ? true : false,
1608 // whether it's selected
1609 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1610 // whether it's selectable
1611 locked: params.locked ? true : false,
1612 // whether the element is locked (cannot be moved)
1613 grabbed: false,
1614 // whether the element is grabbed by the mouse; renderer sets this privately
1615 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1616 // whether the element can be grabbed
1617 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1618 // whether the element has passthrough panning enabled
1619 active: false,
1620 // whether the element is active from user interaction
1621 classes: new Set$1(),
1622 // map ( className => true )
1623 animation: {
1624 // object for currently-running animations
1625 current: [],
1626 queue: []
1627 },
1628 rscratch: {},
1629 // object in which the renderer can store information
1630 scratch: params.scratch || {},
1631 // scratch objects
1632 edges: [],
1633 // array of connected edges
1634 children: [],
1635 // array of children
1636 parent: params.parent && params.parent.isNode() ? params.parent : null,
1637 // parent ref
1638 traversalCache: {},
1639 // cache of output of traversal functions
1640 backgrounding: false,
1641 // whether background images are loading
1642 bbCache: null,
1643 // cache of the current bounding box
1644 bbCacheShift: {
1645 x: 0,
1646 y: 0
1647 },
1648 // shift applied to cached bb to be applied on next get
1649 bodyBounds: null,
1650 // bounds cache of element body, w/o overlay
1651 overlayBounds: null,
1652 // bounds cache of element body, including overlay
1653 labelBounds: {
1654 // bounds cache of labels
1655 all: null,
1656 source: null,
1657 target: null,
1658 main: null
1659 },
1660 arrowBounds: {
1661 // bounds cache of edge arrows
1662 source: null,
1663 target: null,
1664 'mid-source': null,
1665 'mid-target': null
1666 }
1667 };
1668 if (_p.position.x == null) {
1669 _p.position.x = 0;
1670 }
1671 if (_p.position.y == null) {
1672 _p.position.y = 0;
1673 }
1674
1675 // renderedPosition overrides if specified
1676 if (params.renderedPosition) {
1677 var rpos = params.renderedPosition;
1678 var pan = cy.pan();
1679 var zoom = cy.zoom();
1680 _p.position = {
1681 x: (rpos.x - pan.x) / zoom,
1682 y: (rpos.y - pan.y) / zoom
1683 };
1684 }
1685 var classes = [];
1686 if (array(params.classes)) {
1687 classes = params.classes;
1688 } else if (string(params.classes)) {
1689 classes = params.classes.split(/\s+/);
1690 }
1691 for (var i = 0, l = classes.length; i < l; i++) {
1692 var cls = classes[i];
1693 if (!cls || cls === '') {
1694 continue;
1695 }
1696 _p.classes.add(cls);
1697 }
1698 this.createEmitter();
1699 var bypass = params.style || params.css;
1700 if (bypass) {
1701 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1702 this.style(bypass);
1703 }
1704 if (restore === undefined || restore) {
1705 this.restore();
1706 }
1707};
1708
1709var defineSearch = function defineSearch(params) {
1710 params = {
1711 bfs: params.bfs || !params.dfs,
1712 dfs: params.dfs || !params.bfs
1713 };
1714
1715 // from pseudocode on wikipedia
1716 return function searchFn(roots, fn, directed) {
1717 var options;
1718 if (plainObject(roots) && !elementOrCollection(roots)) {
1719 options = roots;
1720 roots = options.roots || options.root;
1721 fn = options.visit;
1722 directed = options.directed;
1723 }
1724 directed = arguments.length === 2 && !fn$6(fn) ? fn : directed;
1725 fn = fn$6(fn) ? fn : function () {};
1726 var cy = this._private.cy;
1727 var v = roots = string(roots) ? this.filter(roots) : roots;
1728 var Q = [];
1729 var connectedNodes = [];
1730 var connectedBy = {};
1731 var id2depth = {};
1732 var V = {};
1733 var j = 0;
1734 var found;
1735 var _this$byGroup = this.byGroup(),
1736 nodes = _this$byGroup.nodes,
1737 edges = _this$byGroup.edges;
1738
1739 // enqueue v
1740 for (var i = 0; i < v.length; i++) {
1741 var vi = v[i];
1742 var viId = vi.id();
1743 if (vi.isNode()) {
1744 Q.unshift(vi);
1745 if (params.bfs) {
1746 V[viId] = true;
1747 connectedNodes.push(vi);
1748 }
1749 id2depth[viId] = 0;
1750 }
1751 }
1752 var _loop = function _loop() {
1753 var v = params.bfs ? Q.shift() : Q.pop();
1754 var vId = v.id();
1755 if (params.dfs) {
1756 if (V[vId]) {
1757 return "continue";
1758 }
1759 V[vId] = true;
1760 connectedNodes.push(v);
1761 }
1762 var depth = id2depth[vId];
1763 var prevEdge = connectedBy[vId];
1764 var src = prevEdge != null ? prevEdge.source() : null;
1765 var tgt = prevEdge != null ? prevEdge.target() : null;
1766 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1767 var ret = void 0;
1768 ret = fn(v, prevEdge, prevNode, j++, depth);
1769 if (ret === true) {
1770 found = v;
1771 return "break";
1772 }
1773 if (ret === false) {
1774 return "break";
1775 }
1776 var vwEdges = v.connectedEdges().filter(function (e) {
1777 return (!directed || e.source().same(v)) && edges.has(e);
1778 });
1779 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1780 var e = vwEdges[_i2];
1781 var w = e.connectedNodes().filter(function (n) {
1782 return !n.same(v) && nodes.has(n);
1783 });
1784 var wId = w.id();
1785 if (w.length !== 0 && !V[wId]) {
1786 w = w[0];
1787 Q.push(w);
1788 if (params.bfs) {
1789 V[wId] = true;
1790 connectedNodes.push(w);
1791 }
1792 connectedBy[wId] = e;
1793 id2depth[wId] = id2depth[vId] + 1;
1794 }
1795 }
1796 };
1797 while (Q.length !== 0) {
1798 var _ret = _loop();
1799 if (_ret === "continue") continue;
1800 if (_ret === "break") break;
1801 }
1802 var connectedEles = cy.collection();
1803 for (var _i = 0; _i < connectedNodes.length; _i++) {
1804 var node = connectedNodes[_i];
1805 var edge = connectedBy[node.id()];
1806 if (edge != null) {
1807 connectedEles.push(edge);
1808 }
1809 connectedEles.push(node);
1810 }
1811 return {
1812 path: cy.collection(connectedEles),
1813 found: cy.collection(found)
1814 };
1815 };
1816};
1817
1818// search, spanning trees, etc
1819var elesfn$v = {
1820 breadthFirstSearch: defineSearch({
1821 bfs: true
1822 }),
1823 depthFirstSearch: defineSearch({
1824 dfs: true
1825 })
1826};
1827
1828// nice, short mathematical alias
1829elesfn$v.bfs = elesfn$v.breadthFirstSearch;
1830elesfn$v.dfs = elesfn$v.depthFirstSearch;
1831
1832var heap$1 = createCommonjsModule(function (module, exports) {
1833// Generated by CoffeeScript 1.8.0
1834(function() {
1835 var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
1836
1837 floor = Math.floor, min = Math.min;
1838
1839
1840 /*
1841 Default comparison function to be used
1842 */
1843
1844 defaultCmp = function(x, y) {
1845 if (x < y) {
1846 return -1;
1847 }
1848 if (x > y) {
1849 return 1;
1850 }
1851 return 0;
1852 };
1853
1854
1855 /*
1856 Insert item x in list a, and keep it sorted assuming a is sorted.
1857
1858 If x is already in a, insert it to the right of the rightmost x.
1859
1860 Optional args lo (default 0) and hi (default a.length) bound the slice
1861 of a to be searched.
1862 */
1863
1864 insort = function(a, x, lo, hi, cmp) {
1865 var mid;
1866 if (lo == null) {
1867 lo = 0;
1868 }
1869 if (cmp == null) {
1870 cmp = defaultCmp;
1871 }
1872 if (lo < 0) {
1873 throw new Error('lo must be non-negative');
1874 }
1875 if (hi == null) {
1876 hi = a.length;
1877 }
1878 while (lo < hi) {
1879 mid = floor((lo + hi) / 2);
1880 if (cmp(x, a[mid]) < 0) {
1881 hi = mid;
1882 } else {
1883 lo = mid + 1;
1884 }
1885 }
1886 return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
1887 };
1888
1889
1890 /*
1891 Push item onto heap, maintaining the heap invariant.
1892 */
1893
1894 heappush = function(array, item, cmp) {
1895 if (cmp == null) {
1896 cmp = defaultCmp;
1897 }
1898 array.push(item);
1899 return _siftdown(array, 0, array.length - 1, cmp);
1900 };
1901
1902
1903 /*
1904 Pop the smallest item off the heap, maintaining the heap invariant.
1905 */
1906
1907 heappop = function(array, cmp) {
1908 var lastelt, returnitem;
1909 if (cmp == null) {
1910 cmp = defaultCmp;
1911 }
1912 lastelt = array.pop();
1913 if (array.length) {
1914 returnitem = array[0];
1915 array[0] = lastelt;
1916 _siftup(array, 0, cmp);
1917 } else {
1918 returnitem = lastelt;
1919 }
1920 return returnitem;
1921 };
1922
1923
1924 /*
1925 Pop and return the current smallest value, and add the new item.
1926
1927 This is more efficient than heappop() followed by heappush(), and can be
1928 more appropriate when using a fixed size heap. Note that the value
1929 returned may be larger than item! That constrains reasonable use of
1930 this routine unless written as part of a conditional replacement:
1931 if item > array[0]
1932 item = heapreplace(array, item)
1933 */
1934
1935 heapreplace = function(array, item, cmp) {
1936 var returnitem;
1937 if (cmp == null) {
1938 cmp = defaultCmp;
1939 }
1940 returnitem = array[0];
1941 array[0] = item;
1942 _siftup(array, 0, cmp);
1943 return returnitem;
1944 };
1945
1946
1947 /*
1948 Fast version of a heappush followed by a heappop.
1949 */
1950
1951 heappushpop = function(array, item, cmp) {
1952 var _ref;
1953 if (cmp == null) {
1954 cmp = defaultCmp;
1955 }
1956 if (array.length && cmp(array[0], item) < 0) {
1957 _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
1958 _siftup(array, 0, cmp);
1959 }
1960 return item;
1961 };
1962
1963
1964 /*
1965 Transform list into a heap, in-place, in O(array.length) time.
1966 */
1967
1968 heapify = function(array, cmp) {
1969 var i, _i, _len, _ref1, _results, _results1;
1970 if (cmp == null) {
1971 cmp = defaultCmp;
1972 }
1973 _ref1 = (function() {
1974 _results1 = [];
1975 for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
1976 return _results1;
1977 }).apply(this).reverse();
1978 _results = [];
1979 for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
1980 i = _ref1[_i];
1981 _results.push(_siftup(array, i, cmp));
1982 }
1983 return _results;
1984 };
1985
1986
1987 /*
1988 Update the position of the given item in the heap.
1989 This function should be called every time the item is being modified.
1990 */
1991
1992 updateItem = function(array, item, cmp) {
1993 var pos;
1994 if (cmp == null) {
1995 cmp = defaultCmp;
1996 }
1997 pos = array.indexOf(item);
1998 if (pos === -1) {
1999 return;
2000 }
2001 _siftdown(array, 0, pos, cmp);
2002 return _siftup(array, pos, cmp);
2003 };
2004
2005
2006 /*
2007 Find the n largest elements in a dataset.
2008 */
2009
2010 nlargest = function(array, n, cmp) {
2011 var elem, result, _i, _len, _ref;
2012 if (cmp == null) {
2013 cmp = defaultCmp;
2014 }
2015 result = array.slice(0, n);
2016 if (!result.length) {
2017 return result;
2018 }
2019 heapify(result, cmp);
2020 _ref = array.slice(n);
2021 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2022 elem = _ref[_i];
2023 heappushpop(result, elem, cmp);
2024 }
2025 return result.sort(cmp).reverse();
2026 };
2027
2028
2029 /*
2030 Find the n smallest elements in a dataset.
2031 */
2032
2033 nsmallest = function(array, n, cmp) {
2034 var elem, los, result, _i, _j, _len, _ref, _ref1, _results;
2035 if (cmp == null) {
2036 cmp = defaultCmp;
2037 }
2038 if (n * 10 <= array.length) {
2039 result = array.slice(0, n).sort(cmp);
2040 if (!result.length) {
2041 return result;
2042 }
2043 los = result[result.length - 1];
2044 _ref = array.slice(n);
2045 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2046 elem = _ref[_i];
2047 if (cmp(elem, los) < 0) {
2048 insort(result, elem, 0, null, cmp);
2049 result.pop();
2050 los = result[result.length - 1];
2051 }
2052 }
2053 return result;
2054 }
2055 heapify(array, cmp);
2056 _results = [];
2057 for (_j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; 0 <= _ref1 ? ++_j : --_j) {
2058 _results.push(heappop(array, cmp));
2059 }
2060 return _results;
2061 };
2062
2063 _siftdown = function(array, startpos, pos, cmp) {
2064 var newitem, parent, parentpos;
2065 if (cmp == null) {
2066 cmp = defaultCmp;
2067 }
2068 newitem = array[pos];
2069 while (pos > startpos) {
2070 parentpos = (pos - 1) >> 1;
2071 parent = array[parentpos];
2072 if (cmp(newitem, parent) < 0) {
2073 array[pos] = parent;
2074 pos = parentpos;
2075 continue;
2076 }
2077 break;
2078 }
2079 return array[pos] = newitem;
2080 };
2081
2082 _siftup = function(array, pos, cmp) {
2083 var childpos, endpos, newitem, rightpos, startpos;
2084 if (cmp == null) {
2085 cmp = defaultCmp;
2086 }
2087 endpos = array.length;
2088 startpos = pos;
2089 newitem = array[pos];
2090 childpos = 2 * pos + 1;
2091 while (childpos < endpos) {
2092 rightpos = childpos + 1;
2093 if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
2094 childpos = rightpos;
2095 }
2096 array[pos] = array[childpos];
2097 pos = childpos;
2098 childpos = 2 * pos + 1;
2099 }
2100 array[pos] = newitem;
2101 return _siftdown(array, startpos, pos, cmp);
2102 };
2103
2104 Heap = (function() {
2105 Heap.push = heappush;
2106
2107 Heap.pop = heappop;
2108
2109 Heap.replace = heapreplace;
2110
2111 Heap.pushpop = heappushpop;
2112
2113 Heap.heapify = heapify;
2114
2115 Heap.updateItem = updateItem;
2116
2117 Heap.nlargest = nlargest;
2118
2119 Heap.nsmallest = nsmallest;
2120
2121 function Heap(cmp) {
2122 this.cmp = cmp != null ? cmp : defaultCmp;
2123 this.nodes = [];
2124 }
2125
2126 Heap.prototype.push = function(x) {
2127 return heappush(this.nodes, x, this.cmp);
2128 };
2129
2130 Heap.prototype.pop = function() {
2131 return heappop(this.nodes, this.cmp);
2132 };
2133
2134 Heap.prototype.peek = function() {
2135 return this.nodes[0];
2136 };
2137
2138 Heap.prototype.contains = function(x) {
2139 return this.nodes.indexOf(x) !== -1;
2140 };
2141
2142 Heap.prototype.replace = function(x) {
2143 return heapreplace(this.nodes, x, this.cmp);
2144 };
2145
2146 Heap.prototype.pushpop = function(x) {
2147 return heappushpop(this.nodes, x, this.cmp);
2148 };
2149
2150 Heap.prototype.heapify = function() {
2151 return heapify(this.nodes, this.cmp);
2152 };
2153
2154 Heap.prototype.updateItem = function(x) {
2155 return updateItem(this.nodes, x, this.cmp);
2156 };
2157
2158 Heap.prototype.clear = function() {
2159 return this.nodes = [];
2160 };
2161
2162 Heap.prototype.empty = function() {
2163 return this.nodes.length === 0;
2164 };
2165
2166 Heap.prototype.size = function() {
2167 return this.nodes.length;
2168 };
2169
2170 Heap.prototype.clone = function() {
2171 var heap;
2172 heap = new Heap();
2173 heap.nodes = this.nodes.slice(0);
2174 return heap;
2175 };
2176
2177 Heap.prototype.toArray = function() {
2178 return this.nodes.slice(0);
2179 };
2180
2181 Heap.prototype.insert = Heap.prototype.push;
2182
2183 Heap.prototype.top = Heap.prototype.peek;
2184
2185 Heap.prototype.front = Heap.prototype.peek;
2186
2187 Heap.prototype.has = Heap.prototype.contains;
2188
2189 Heap.prototype.copy = Heap.prototype.clone;
2190
2191 return Heap;
2192
2193 })();
2194
2195 (function(root, factory) {
2196 {
2197 return module.exports = factory();
2198 }
2199 })(this, function() {
2200 return Heap;
2201 });
2202
2203}).call(commonjsGlobal);
2204});
2205
2206var heap = heap$1;
2207
2208var dijkstraDefaults = defaults$g({
2209 root: null,
2210 weight: function weight(edge) {
2211 return 1;
2212 },
2213 directed: false
2214});
2215var elesfn$u = {
2216 dijkstra: function dijkstra(options) {
2217 if (!plainObject(options)) {
2218 var args = arguments;
2219 options = {
2220 root: args[0],
2221 weight: args[1],
2222 directed: args[2]
2223 };
2224 }
2225 var _dijkstraDefaults = dijkstraDefaults(options),
2226 root = _dijkstraDefaults.root,
2227 weight = _dijkstraDefaults.weight,
2228 directed = _dijkstraDefaults.directed;
2229 var eles = this;
2230 var weightFn = weight;
2231 var source = string(root) ? this.filter(root)[0] : root[0];
2232 var dist = {};
2233 var prev = {};
2234 var knownDist = {};
2235 var _this$byGroup = this.byGroup(),
2236 nodes = _this$byGroup.nodes,
2237 edges = _this$byGroup.edges;
2238 edges.unmergeBy(function (ele) {
2239 return ele.isLoop();
2240 });
2241 var getDist = function getDist(node) {
2242 return dist[node.id()];
2243 };
2244 var setDist = function setDist(node, d) {
2245 dist[node.id()] = d;
2246 Q.updateItem(node);
2247 };
2248 var Q = new heap(function (a, b) {
2249 return getDist(a) - getDist(b);
2250 });
2251 for (var i = 0; i < nodes.length; i++) {
2252 var node = nodes[i];
2253 dist[node.id()] = node.same(source) ? 0 : Infinity;
2254 Q.push(node);
2255 }
2256 var distBetween = function distBetween(u, v) {
2257 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
2258 var smallestDistance = Infinity;
2259 var smallestEdge;
2260 for (var _i = 0; _i < uvs.length; _i++) {
2261 var edge = uvs[_i];
2262 var _weight = weightFn(edge);
2263 if (_weight < smallestDistance || !smallestEdge) {
2264 smallestDistance = _weight;
2265 smallestEdge = edge;
2266 }
2267 }
2268 return {
2269 edge: smallestEdge,
2270 dist: smallestDistance
2271 };
2272 };
2273 while (Q.size() > 0) {
2274 var u = Q.pop();
2275 var smalletsDist = getDist(u);
2276 var uid = u.id();
2277 knownDist[uid] = smalletsDist;
2278 if (smalletsDist === Infinity) {
2279 continue;
2280 }
2281 var neighbors = u.neighborhood().intersect(nodes);
2282 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
2283 var v = neighbors[_i2];
2284 var vid = v.id();
2285 var vDist = distBetween(u, v);
2286 var alt = smalletsDist + vDist.dist;
2287 if (alt < getDist(v)) {
2288 setDist(v, alt);
2289 prev[vid] = {
2290 node: u,
2291 edge: vDist.edge
2292 };
2293 }
2294 } // for
2295 } // while
2296
2297 return {
2298 distanceTo: function distanceTo(node) {
2299 var target = string(node) ? nodes.filter(node)[0] : node[0];
2300 return knownDist[target.id()];
2301 },
2302 pathTo: function pathTo(node) {
2303 var target = string(node) ? nodes.filter(node)[0] : node[0];
2304 var S = [];
2305 var u = target;
2306 var uid = u.id();
2307 if (target.length > 0) {
2308 S.unshift(target);
2309 while (prev[uid]) {
2310 var p = prev[uid];
2311 S.unshift(p.edge);
2312 S.unshift(p.node);
2313 u = p.node;
2314 uid = u.id();
2315 }
2316 }
2317 return eles.spawn(S);
2318 }
2319 };
2320 }
2321};
2322
2323var elesfn$t = {
2324 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
2325 // implemented from pseudocode from wikipedia
2326 kruskal: function kruskal(weightFn) {
2327 weightFn = weightFn || function (edge) {
2328 return 1;
2329 };
2330 var _this$byGroup = this.byGroup(),
2331 nodes = _this$byGroup.nodes,
2332 edges = _this$byGroup.edges;
2333 var numNodes = nodes.length;
2334 var forest = new Array(numNodes);
2335 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
2336
2337 var findSetIndex = function findSetIndex(ele) {
2338 for (var i = 0; i < forest.length; i++) {
2339 var eles = forest[i];
2340 if (eles.has(ele)) {
2341 return i;
2342 }
2343 }
2344 };
2345
2346 // start with one forest per node
2347 for (var i = 0; i < numNodes; i++) {
2348 forest[i] = this.spawn(nodes[i]);
2349 }
2350 var S = edges.sort(function (a, b) {
2351 return weightFn(a) - weightFn(b);
2352 });
2353 for (var _i = 0; _i < S.length; _i++) {
2354 var edge = S[_i];
2355 var u = edge.source()[0];
2356 var v = edge.target()[0];
2357 var setUIndex = findSetIndex(u);
2358 var setVIndex = findSetIndex(v);
2359 var setU = forest[setUIndex];
2360 var setV = forest[setVIndex];
2361 if (setUIndex !== setVIndex) {
2362 A.merge(edge);
2363
2364 // combine forests for u and v
2365 setU.merge(setV);
2366 forest.splice(setVIndex, 1);
2367 }
2368 }
2369 return A;
2370 }
2371};
2372
2373var aStarDefaults = defaults$g({
2374 root: null,
2375 goal: null,
2376 weight: function weight(edge) {
2377 return 1;
2378 },
2379 heuristic: function heuristic(edge) {
2380 return 0;
2381 },
2382 directed: false
2383});
2384var elesfn$s = {
2385 // Implemented from pseudocode from wikipedia
2386 aStar: function aStar(options) {
2387 var cy = this.cy();
2388 var _aStarDefaults = aStarDefaults(options),
2389 root = _aStarDefaults.root,
2390 goal = _aStarDefaults.goal,
2391 heuristic = _aStarDefaults.heuristic,
2392 directed = _aStarDefaults.directed,
2393 weight = _aStarDefaults.weight;
2394 root = cy.collection(root)[0];
2395 goal = cy.collection(goal)[0];
2396 var sid = root.id();
2397 var tid = goal.id();
2398 var gScore = {};
2399 var fScore = {};
2400 var closedSetIds = {};
2401 var openSet = new heap(function (a, b) {
2402 return fScore[a.id()] - fScore[b.id()];
2403 });
2404 var openSetIds = new Set$1();
2405 var cameFrom = {};
2406 var cameFromEdge = {};
2407 var addToOpenSet = function addToOpenSet(ele, id) {
2408 openSet.push(ele);
2409 openSetIds.add(id);
2410 };
2411 var cMin, cMinId;
2412 var popFromOpenSet = function popFromOpenSet() {
2413 cMin = openSet.pop();
2414 cMinId = cMin.id();
2415 openSetIds["delete"](cMinId);
2416 };
2417 var isInOpenSet = function isInOpenSet(id) {
2418 return openSetIds.has(id);
2419 };
2420 addToOpenSet(root, sid);
2421 gScore[sid] = 0;
2422 fScore[sid] = heuristic(root);
2423
2424 // Counter
2425 var steps = 0;
2426
2427 // Main loop
2428 while (openSet.size() > 0) {
2429 popFromOpenSet();
2430 steps++;
2431
2432 // If we've found our goal, then we are done
2433 if (cMinId === tid) {
2434 var path = [];
2435 var pathNode = goal;
2436 var pathNodeId = tid;
2437 var pathEdge = cameFromEdge[pathNodeId];
2438 for (;;) {
2439 path.unshift(pathNode);
2440 if (pathEdge != null) {
2441 path.unshift(pathEdge);
2442 }
2443 pathNode = cameFrom[pathNodeId];
2444 if (pathNode == null) {
2445 break;
2446 }
2447 pathNodeId = pathNode.id();
2448 pathEdge = cameFromEdge[pathNodeId];
2449 }
2450 return {
2451 found: true,
2452 distance: gScore[cMinId],
2453 path: this.spawn(path),
2454 steps: steps
2455 };
2456 }
2457
2458 // Add cMin to processed nodes
2459 closedSetIds[cMinId] = true;
2460
2461 // Update scores for neighbors of cMin
2462 // Take into account if graph is directed or not
2463 var vwEdges = cMin._private.edges;
2464 for (var i = 0; i < vwEdges.length; i++) {
2465 var e = vwEdges[i];
2466
2467 // edge must be in set of calling eles
2468 if (!this.hasElementWithId(e.id())) {
2469 continue;
2470 }
2471
2472 // cMin must be the source of edge if directed
2473 if (directed && e.data('source') !== cMinId) {
2474 continue;
2475 }
2476 var wSrc = e.source();
2477 var wTgt = e.target();
2478 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
2479 var wid = w.id();
2480
2481 // node must be in set of calling eles
2482 if (!this.hasElementWithId(wid)) {
2483 continue;
2484 }
2485
2486 // if node is in closedSet, ignore it
2487 if (closedSetIds[wid]) {
2488 continue;
2489 }
2490
2491 // New tentative score for node w
2492 var tempScore = gScore[cMinId] + weight(e);
2493
2494 // Update gScore for node w if:
2495 // w not present in openSet
2496 // OR
2497 // tentative gScore is less than previous value
2498
2499 // w not in openSet
2500 if (!isInOpenSet(wid)) {
2501 gScore[wid] = tempScore;
2502 fScore[wid] = tempScore + heuristic(w);
2503 addToOpenSet(w, wid);
2504 cameFrom[wid] = cMin;
2505 cameFromEdge[wid] = e;
2506 continue;
2507 }
2508
2509 // w already in openSet, but with greater gScore
2510 if (tempScore < gScore[wid]) {
2511 gScore[wid] = tempScore;
2512 fScore[wid] = tempScore + heuristic(w);
2513 cameFrom[wid] = cMin;
2514 cameFromEdge[wid] = e;
2515 }
2516 } // End of neighbors update
2517 } // End of main loop
2518
2519 // If we've reached here, then we've not reached our goal
2520 return {
2521 found: false,
2522 distance: undefined,
2523 path: undefined,
2524 steps: steps
2525 };
2526 }
2527}; // elesfn
2528
2529var floydWarshallDefaults = defaults$g({
2530 weight: function weight(edge) {
2531 return 1;
2532 },
2533 directed: false
2534});
2535var elesfn$r = {
2536 // Implemented from pseudocode from wikipedia
2537 floydWarshall: function floydWarshall(options) {
2538 var cy = this.cy();
2539 var _floydWarshallDefault = floydWarshallDefaults(options),
2540 weight = _floydWarshallDefault.weight,
2541 directed = _floydWarshallDefault.directed;
2542 var weightFn = weight;
2543 var _this$byGroup = this.byGroup(),
2544 nodes = _this$byGroup.nodes,
2545 edges = _this$byGroup.edges;
2546 var N = nodes.length;
2547 var Nsq = N * N;
2548 var indexOf = function indexOf(node) {
2549 return nodes.indexOf(node);
2550 };
2551 var atIndex = function atIndex(i) {
2552 return nodes[i];
2553 };
2554
2555 // Initialize distance matrix
2556 var dist = new Array(Nsq);
2557 for (var n = 0; n < Nsq; n++) {
2558 var j = n % N;
2559 var i = (n - j) / N;
2560 if (i === j) {
2561 dist[n] = 0;
2562 } else {
2563 dist[n] = Infinity;
2564 }
2565 }
2566
2567 // Initialize matrix used for path reconstruction
2568 // Initialize distance matrix
2569 var next = new Array(Nsq);
2570 var edgeNext = new Array(Nsq);
2571
2572 // Process edges
2573 for (var _i = 0; _i < edges.length; _i++) {
2574 var edge = edges[_i];
2575 var src = edge.source()[0];
2576 var tgt = edge.target()[0];
2577 if (src === tgt) {
2578 continue;
2579 } // exclude loops
2580
2581 var s = indexOf(src);
2582 var t = indexOf(tgt);
2583 var st = s * N + t; // source to target index
2584 var _weight = weightFn(edge);
2585
2586 // Check if already process another edge between same 2 nodes
2587 if (dist[st] > _weight) {
2588 dist[st] = _weight;
2589 next[st] = t;
2590 edgeNext[st] = edge;
2591 }
2592
2593 // If undirected graph, process 'reversed' edge
2594 if (!directed) {
2595 var ts = t * N + s; // target to source index
2596
2597 if (!directed && dist[ts] > _weight) {
2598 dist[ts] = _weight;
2599 next[ts] = s;
2600 edgeNext[ts] = edge;
2601 }
2602 }
2603 }
2604
2605 // Main loop
2606 for (var k = 0; k < N; k++) {
2607 for (var _i2 = 0; _i2 < N; _i2++) {
2608 var ik = _i2 * N + k;
2609 for (var _j = 0; _j < N; _j++) {
2610 var ij = _i2 * N + _j;
2611 var kj = k * N + _j;
2612 if (dist[ik] + dist[kj] < dist[ij]) {
2613 dist[ij] = dist[ik] + dist[kj];
2614 next[ij] = next[ik];
2615 }
2616 }
2617 }
2618 }
2619 var getArgEle = function getArgEle(ele) {
2620 return (string(ele) ? cy.filter(ele) : ele)[0];
2621 };
2622 var indexOfArgEle = function indexOfArgEle(ele) {
2623 return indexOf(getArgEle(ele));
2624 };
2625 var res = {
2626 distance: function distance(from, to) {
2627 var i = indexOfArgEle(from);
2628 var j = indexOfArgEle(to);
2629 return dist[i * N + j];
2630 },
2631 path: function path(from, to) {
2632 var i = indexOfArgEle(from);
2633 var j = indexOfArgEle(to);
2634 var fromNode = atIndex(i);
2635 if (i === j) {
2636 return fromNode.collection();
2637 }
2638 if (next[i * N + j] == null) {
2639 return cy.collection();
2640 }
2641 var path = cy.collection();
2642 var prev = i;
2643 var edge;
2644 path.merge(fromNode);
2645 while (i !== j) {
2646 prev = i;
2647 i = next[i * N + j];
2648 edge = edgeNext[prev * N + i];
2649 path.merge(edge);
2650 path.merge(atIndex(i));
2651 }
2652 return path;
2653 }
2654 };
2655 return res;
2656 } // floydWarshall
2657}; // elesfn
2658
2659var bellmanFordDefaults = defaults$g({
2660 weight: function weight(edge) {
2661 return 1;
2662 },
2663 directed: false,
2664 root: null
2665});
2666var elesfn$q = {
2667 // Implemented from pseudocode from wikipedia
2668 bellmanFord: function bellmanFord(options) {
2669 var _this = this;
2670 var _bellmanFordDefaults = bellmanFordDefaults(options),
2671 weight = _bellmanFordDefaults.weight,
2672 directed = _bellmanFordDefaults.directed,
2673 root = _bellmanFordDefaults.root;
2674 var weightFn = weight;
2675 var eles = this;
2676 var cy = this.cy();
2677 var _this$byGroup = this.byGroup(),
2678 edges = _this$byGroup.edges,
2679 nodes = _this$byGroup.nodes;
2680 var numNodes = nodes.length;
2681 var infoMap = new Map$2();
2682 var hasNegativeWeightCycle = false;
2683 var negativeWeightCycles = [];
2684 root = cy.collection(root)[0]; // in case selector passed
2685
2686 edges.unmergeBy(function (edge) {
2687 return edge.isLoop();
2688 });
2689 var numEdges = edges.length;
2690 var getInfo = function getInfo(node) {
2691 var obj = infoMap.get(node.id());
2692 if (!obj) {
2693 obj = {};
2694 infoMap.set(node.id(), obj);
2695 }
2696 return obj;
2697 };
2698 var getNodeFromTo = function getNodeFromTo(to) {
2699 return (string(to) ? cy.$(to) : to)[0];
2700 };
2701 var distanceTo = function distanceTo(to) {
2702 return getInfo(getNodeFromTo(to)).dist;
2703 };
2704 var pathTo = function pathTo(to) {
2705 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
2706 var end = getNodeFromTo(to);
2707 var path = [];
2708 var node = end;
2709 for (;;) {
2710 if (node == null) {
2711 return _this.spawn();
2712 }
2713 var _getInfo = getInfo(node),
2714 edge = _getInfo.edge,
2715 pred = _getInfo.pred;
2716 path.unshift(node[0]);
2717 if (node.same(thisStart) && path.length > 0) {
2718 break;
2719 }
2720 if (edge != null) {
2721 path.unshift(edge);
2722 }
2723 node = pred;
2724 }
2725 return eles.spawn(path);
2726 };
2727
2728 // Initializations { dist, pred, edge }
2729 for (var i = 0; i < numNodes; i++) {
2730 var node = nodes[i];
2731 var info = getInfo(node);
2732 if (node.same(root)) {
2733 info.dist = 0;
2734 } else {
2735 info.dist = Infinity;
2736 }
2737 info.pred = null;
2738 info.edge = null;
2739 }
2740
2741 // Edges relaxation
2742 var replacedEdge = false;
2743 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2744 var dist = info1.dist + weight;
2745 if (dist < info2.dist && !edge.same(info1.edge)) {
2746 info2.dist = dist;
2747 info2.pred = node1;
2748 info2.edge = edge;
2749 replacedEdge = true;
2750 }
2751 };
2752 for (var _i = 1; _i < numNodes; _i++) {
2753 replacedEdge = false;
2754 for (var e = 0; e < numEdges; e++) {
2755 var edge = edges[e];
2756 var src = edge.source();
2757 var tgt = edge.target();
2758 var _weight = weightFn(edge);
2759 var srcInfo = getInfo(src);
2760 var tgtInfo = getInfo(tgt);
2761 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight);
2762
2763 // If undirected graph, we need to take into account the 'reverse' edge
2764 if (!directed) {
2765 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2766 }
2767 }
2768 if (!replacedEdge) {
2769 break;
2770 }
2771 }
2772 if (replacedEdge) {
2773 // Check for negative weight cycles
2774 var negativeWeightCycleIds = [];
2775 for (var _e = 0; _e < numEdges; _e++) {
2776 var _edge = edges[_e];
2777 var _src = _edge.source();
2778 var _tgt = _edge.target();
2779 var _weight2 = weightFn(_edge);
2780 var srcDist = getInfo(_src).dist;
2781 var tgtDist = getInfo(_tgt).dist;
2782 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2783 if (!hasNegativeWeightCycle) {
2784 warn('Graph contains a negative weight cycle for Bellman-Ford');
2785 hasNegativeWeightCycle = true;
2786 }
2787 if (options.findNegativeWeightCycles !== false) {
2788 var negativeNodes = [];
2789 if (srcDist + _weight2 < tgtDist) {
2790 negativeNodes.push(_src);
2791 }
2792 if (!directed && tgtDist + _weight2 < srcDist) {
2793 negativeNodes.push(_tgt);
2794 }
2795 var numNegativeNodes = negativeNodes.length;
2796 for (var n = 0; n < numNegativeNodes; n++) {
2797 var start = negativeNodes[n];
2798 var cycle = [start];
2799 cycle.push(getInfo(start).edge);
2800 var _node = getInfo(start).pred;
2801 while (cycle.indexOf(_node) === -1) {
2802 cycle.push(_node);
2803 cycle.push(getInfo(_node).edge);
2804 _node = getInfo(_node).pred;
2805 }
2806 cycle = cycle.slice(cycle.indexOf(_node));
2807 var smallestId = cycle[0].id();
2808 var smallestIndex = 0;
2809 for (var c = 2; c < cycle.length; c += 2) {
2810 if (cycle[c].id() < smallestId) {
2811 smallestId = cycle[c].id();
2812 smallestIndex = c;
2813 }
2814 }
2815 cycle = cycle.slice(smallestIndex).concat(cycle.slice(0, smallestIndex));
2816 cycle.push(cycle[0]);
2817 var cycleId = cycle.map(function (el) {
2818 return el.id();
2819 }).join(",");
2820 if (negativeWeightCycleIds.indexOf(cycleId) === -1) {
2821 negativeWeightCycles.push(eles.spawn(cycle));
2822 negativeWeightCycleIds.push(cycleId);
2823 }
2824 }
2825 } else {
2826 break;
2827 }
2828 }
2829 }
2830 }
2831 return {
2832 distanceTo: distanceTo,
2833 pathTo: pathTo,
2834 hasNegativeWeightCycle: hasNegativeWeightCycle,
2835 negativeWeightCycles: negativeWeightCycles
2836 };
2837 } // bellmanFord
2838}; // elesfn
2839
2840var sqrt2 = Math.sqrt(2);
2841
2842// Function which colapses 2 (meta) nodes into one
2843// Updates the remaining edge lists
2844// Receives as a paramater the edge which causes the collapse
2845var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2846 if (remainingEdges.length === 0) {
2847 error("Karger-Stein must be run on a connected (sub)graph");
2848 }
2849 var edgeInfo = remainingEdges[edgeIndex];
2850 var sourceIn = edgeInfo[1];
2851 var targetIn = edgeInfo[2];
2852 var partition1 = nodeMap[sourceIn];
2853 var partition2 = nodeMap[targetIn];
2854 var newEdges = remainingEdges; // re-use array
2855
2856 // Delete all edges between partition1 and partition2
2857 for (var i = newEdges.length - 1; i >= 0; i--) {
2858 var edge = newEdges[i];
2859 var src = edge[1];
2860 var tgt = edge[2];
2861 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2862 newEdges.splice(i, 1);
2863 }
2864 }
2865
2866 // All edges pointing to partition2 should now point to partition1
2867 for (var _i = 0; _i < newEdges.length; _i++) {
2868 var _edge = newEdges[_i];
2869 if (_edge[1] === partition2) {
2870 // Check source
2871 newEdges[_i] = _edge.slice(); // copy
2872 newEdges[_i][1] = partition1;
2873 } else if (_edge[2] === partition2) {
2874 // Check target
2875 newEdges[_i] = _edge.slice(); // copy
2876 newEdges[_i][2] = partition1;
2877 }
2878 }
2879
2880 // Move all nodes from partition2 to partition1
2881 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2882 if (nodeMap[_i2] === partition2) {
2883 nodeMap[_i2] = partition1;
2884 }
2885 }
2886 return newEdges;
2887};
2888
2889// Contracts a graph until we reach a certain number of meta nodes
2890var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2891 while (size > sizeLimit) {
2892 // Choose an edge randomly
2893 var edgeIndex = Math.floor(Math.random() * remainingEdges.length);
2894
2895 // Collapse graph based on edge
2896 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2897 size--;
2898 }
2899 return remainingEdges;
2900};
2901var elesfn$p = {
2902 // Computes the minimum cut of an undirected graph
2903 // Returns the correct answer with high probability
2904 kargerStein: function kargerStein() {
2905 var _this = this;
2906 var _this$byGroup = this.byGroup(),
2907 nodes = _this$byGroup.nodes,
2908 edges = _this$byGroup.edges;
2909 edges.unmergeBy(function (edge) {
2910 return edge.isLoop();
2911 });
2912 var numNodes = nodes.length;
2913 var numEdges = edges.length;
2914 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2915 var stopSize = Math.floor(numNodes / sqrt2);
2916 if (numNodes < 2) {
2917 error('At least 2 nodes are required for Karger-Stein algorithm');
2918 return undefined;
2919 }
2920
2921 // Now store edge destination as indexes
2922 // Format for each edge (edge index, source node index, target node index)
2923 var edgeIndexes = [];
2924 for (var i = 0; i < numEdges; i++) {
2925 var e = edges[i];
2926 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2927 }
2928
2929 // We will store the best cut found here
2930 var minCutSize = Infinity;
2931 var minCutEdgeIndexes = [];
2932 var minCutNodeMap = new Array(numNodes);
2933
2934 // Initial meta node partition
2935 var metaNodeMap = new Array(numNodes);
2936 var metaNodeMap2 = new Array(numNodes);
2937 var copyNodesMap = function copyNodesMap(from, to) {
2938 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2939 to[_i3] = from[_i3];
2940 }
2941 };
2942
2943 // Main loop
2944 for (var iter = 0; iter <= numIter; iter++) {
2945 // Reset meta node partition
2946 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2947 metaNodeMap[_i4] = _i4;
2948 }
2949
2950 // Contract until stop point (stopSize nodes)
2951 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2952 var edgesState2 = edgesState.slice(); // copy
2953
2954 // Create a copy of the colapsed nodes state
2955 copyNodesMap(metaNodeMap, metaNodeMap2);
2956
2957 // Run 2 iterations starting in the stop state
2958 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2959 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2);
2960
2961 // Is any of the 2 results the best cut so far?
2962 if (res1.length <= res2.length && res1.length < minCutSize) {
2963 minCutSize = res1.length;
2964 minCutEdgeIndexes = res1;
2965 copyNodesMap(metaNodeMap, minCutNodeMap);
2966 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2967 minCutSize = res2.length;
2968 minCutEdgeIndexes = res2;
2969 copyNodesMap(metaNodeMap2, minCutNodeMap);
2970 }
2971 } // end of main loop
2972
2973 // Construct result
2974 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2975 return edges[e[0]];
2976 }));
2977 var partition1 = this.spawn();
2978 var partition2 = this.spawn();
2979
2980 // traverse metaNodeMap for best cut
2981 var witnessNodePartition = minCutNodeMap[0];
2982 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2983 var partitionId = minCutNodeMap[_i5];
2984 var node = nodes[_i5];
2985 if (partitionId === witnessNodePartition) {
2986 partition1.merge(node);
2987 } else {
2988 partition2.merge(node);
2989 }
2990 }
2991
2992 // construct components corresponding to each disjoint subset of nodes
2993 var constructComponent = function constructComponent(subset) {
2994 var component = _this.spawn();
2995 subset.forEach(function (node) {
2996 component.merge(node);
2997 node.connectedEdges().forEach(function (edge) {
2998 // ensure edge is within calling collection and edge is not in cut
2999 if (_this.contains(edge) && !cut.contains(edge)) {
3000 component.merge(edge);
3001 }
3002 });
3003 });
3004 return component;
3005 };
3006 var components = [constructComponent(partition1), constructComponent(partition2)];
3007 var ret = {
3008 cut: cut,
3009 components: components,
3010 // n.b. partitions are included to be compatible with the old api spec
3011 // (could be removed in a future major version)
3012 partition1: partition1,
3013 partition2: partition2
3014 };
3015 return ret;
3016 }
3017}; // elesfn
3018
3019var copyPosition = function copyPosition(p) {
3020 return {
3021 x: p.x,
3022 y: p.y
3023 };
3024};
3025var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
3026 return {
3027 x: p.x * zoom + pan.x,
3028 y: p.y * zoom + pan.y
3029 };
3030};
3031var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
3032 return {
3033 x: (p.x - pan.x) / zoom,
3034 y: (p.y - pan.y) / zoom
3035 };
3036};
3037var array2point = function array2point(arr) {
3038 return {
3039 x: arr[0],
3040 y: arr[1]
3041 };
3042};
3043var min = function min(arr) {
3044 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3045 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3046 var min = Infinity;
3047 for (var i = begin; i < end; i++) {
3048 var val = arr[i];
3049 if (isFinite(val)) {
3050 min = Math.min(val, min);
3051 }
3052 }
3053 return min;
3054};
3055var max = function max(arr) {
3056 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3057 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3058 var max = -Infinity;
3059 for (var i = begin; i < end; i++) {
3060 var val = arr[i];
3061 if (isFinite(val)) {
3062 max = Math.max(val, max);
3063 }
3064 }
3065 return max;
3066};
3067var mean = function mean(arr) {
3068 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3069 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3070 var total = 0;
3071 var n = 0;
3072 for (var i = begin; i < end; i++) {
3073 var val = arr[i];
3074 if (isFinite(val)) {
3075 total += val;
3076 n++;
3077 }
3078 }
3079 return total / n;
3080};
3081var median = function median(arr) {
3082 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3083 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3084 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
3085 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
3086 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
3087 if (copy) {
3088 arr = arr.slice(begin, end);
3089 } else {
3090 if (end < arr.length) {
3091 arr.splice(end, arr.length - end);
3092 }
3093 if (begin > 0) {
3094 arr.splice(0, begin);
3095 }
3096 }
3097
3098 // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
3099 var off = 0; // offset from non-finite values
3100 for (var i = arr.length - 1; i >= 0; i--) {
3101 var v = arr[i];
3102 if (includeHoles) {
3103 if (!isFinite(v)) {
3104 arr[i] = -Infinity;
3105 off++;
3106 }
3107 } else {
3108 // just remove it if we don't want to consider holes
3109 arr.splice(i, 1);
3110 }
3111 }
3112 if (sort) {
3113 arr.sort(function (a, b) {
3114 return a - b;
3115 }); // requires copy = true if you don't want to change the orig
3116 }
3117
3118 var len = arr.length;
3119 var mid = Math.floor(len / 2);
3120 if (len % 2 !== 0) {
3121 return arr[mid + 1 + off];
3122 } else {
3123 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
3124 }
3125};
3126var deg2rad = function deg2rad(deg) {
3127 return Math.PI * deg / 180;
3128};
3129var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
3130 return Math.atan2(dispY, dispX) - Math.PI / 2;
3131};
3132var log2 = Math.log2 || function (n) {
3133 return Math.log(n) / Math.log(2);
3134};
3135var signum = function signum(x) {
3136 if (x > 0) {
3137 return 1;
3138 } else if (x < 0) {
3139 return -1;
3140 } else {
3141 return 0;
3142 }
3143};
3144var dist = function dist(p1, p2) {
3145 return Math.sqrt(sqdist(p1, p2));
3146};
3147var sqdist = function sqdist(p1, p2) {
3148 var dx = p2.x - p1.x;
3149 var dy = p2.y - p1.y;
3150 return dx * dx + dy * dy;
3151};
3152var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
3153 var length = v.length;
3154
3155 // First, get sum of all elements
3156 var total = 0;
3157 for (var i = 0; i < length; i++) {
3158 total += v[i];
3159 }
3160
3161 // Now, divide each by the sum of all elements
3162 for (var _i = 0; _i < length; _i++) {
3163 v[_i] = v[_i] / total;
3164 }
3165 return v;
3166};
3167
3168// from http://en.wikipedia.org/wiki/Bézier_curve#Quadratic_curves
3169var qbezierAt = function qbezierAt(p0, p1, p2, t) {
3170 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
3171};
3172var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
3173 return {
3174 x: qbezierAt(p0.x, p1.x, p2.x, t),
3175 y: qbezierAt(p0.y, p1.y, p2.y, t)
3176 };
3177};
3178var lineAt = function lineAt(p0, p1, t, d) {
3179 var vec = {
3180 x: p1.x - p0.x,
3181 y: p1.y - p0.y
3182 };
3183 var vecDist = dist(p0, p1);
3184 var normVec = {
3185 x: vec.x / vecDist,
3186 y: vec.y / vecDist
3187 };
3188 t = t == null ? 0 : t;
3189 d = d != null ? d : t * vecDist;
3190 return {
3191 x: p0.x + normVec.x * d,
3192 y: p0.y + normVec.y * d
3193 };
3194};
3195var bound = function bound(min, val, max) {
3196 return Math.max(min, Math.min(max, val));
3197};
3198
3199// makes a full bb (x1, y1, x2, y2, w, h) from implicit params
3200var makeBoundingBox = function makeBoundingBox(bb) {
3201 if (bb == null) {
3202 return {
3203 x1: Infinity,
3204 y1: Infinity,
3205 x2: -Infinity,
3206 y2: -Infinity,
3207 w: 0,
3208 h: 0
3209 };
3210 } else if (bb.x1 != null && bb.y1 != null) {
3211 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
3212 return {
3213 x1: bb.x1,
3214 y1: bb.y1,
3215 x2: bb.x2,
3216 y2: bb.y2,
3217 w: bb.x2 - bb.x1,
3218 h: bb.y2 - bb.y1
3219 };
3220 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
3221 return {
3222 x1: bb.x1,
3223 y1: bb.y1,
3224 x2: bb.x1 + bb.w,
3225 y2: bb.y1 + bb.h,
3226 w: bb.w,
3227 h: bb.h
3228 };
3229 }
3230 }
3231};
3232var copyBoundingBox = function copyBoundingBox(bb) {
3233 return {
3234 x1: bb.x1,
3235 x2: bb.x2,
3236 w: bb.w,
3237 y1: bb.y1,
3238 y2: bb.y2,
3239 h: bb.h
3240 };
3241};
3242var clearBoundingBox = function clearBoundingBox(bb) {
3243 bb.x1 = Infinity;
3244 bb.y1 = Infinity;
3245 bb.x2 = -Infinity;
3246 bb.y2 = -Infinity;
3247 bb.w = 0;
3248 bb.h = 0;
3249};
3250var shiftBoundingBox = function shiftBoundingBox(bb, dx, dy) {
3251 return {
3252 x1: bb.x1 + dx,
3253 x2: bb.x2 + dx,
3254 y1: bb.y1 + dy,
3255 y2: bb.y2 + dy,
3256 w: bb.w,
3257 h: bb.h
3258 };
3259};
3260var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
3261 // update bb1 with bb2 bounds
3262
3263 bb1.x1 = Math.min(bb1.x1, bb2.x1);
3264 bb1.x2 = Math.max(bb1.x2, bb2.x2);
3265 bb1.w = bb1.x2 - bb1.x1;
3266 bb1.y1 = Math.min(bb1.y1, bb2.y1);
3267 bb1.y2 = Math.max(bb1.y2, bb2.y2);
3268 bb1.h = bb1.y2 - bb1.y1;
3269};
3270var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
3271 bb.x1 = Math.min(bb.x1, x);
3272 bb.x2 = Math.max(bb.x2, x);
3273 bb.w = bb.x2 - bb.x1;
3274 bb.y1 = Math.min(bb.y1, y);
3275 bb.y2 = Math.max(bb.y2, y);
3276 bb.h = bb.y2 - bb.y1;
3277};
3278var expandBoundingBox = function expandBoundingBox(bb) {
3279 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3280 bb.x1 -= padding;
3281 bb.x2 += padding;
3282 bb.y1 -= padding;
3283 bb.y2 += padding;
3284 bb.w = bb.x2 - bb.x1;
3285 bb.h = bb.y2 - bb.y1;
3286 return bb;
3287};
3288var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
3289 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
3290 var top, right, bottom, left;
3291 if (padding.length === 1) {
3292 top = right = bottom = left = padding[0];
3293 } else if (padding.length === 2) {
3294 top = bottom = padding[0];
3295 left = right = padding[1];
3296 } else if (padding.length === 4) {
3297 var _padding = _slicedToArray(padding, 4);
3298 top = _padding[0];
3299 right = _padding[1];
3300 bottom = _padding[2];
3301 left = _padding[3];
3302 }
3303 bb.x1 -= left;
3304 bb.x2 += right;
3305 bb.y1 -= top;
3306 bb.y2 += bottom;
3307 bb.w = bb.x2 - bb.x1;
3308 bb.h = bb.y2 - bb.y1;
3309 return bb;
3310};
3311
3312// assign the values of bb2 into bb1
3313var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
3314 bb1.x1 = bb2.x1;
3315 bb1.y1 = bb2.y1;
3316 bb1.x2 = bb2.x2;
3317 bb1.y2 = bb2.y2;
3318 bb1.w = bb1.x2 - bb1.x1;
3319 bb1.h = bb1.y2 - bb1.y1;
3320};
3321var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
3322 // case: one bb to right of other
3323 if (bb1.x1 > bb2.x2) {
3324 return false;
3325 }
3326 if (bb2.x1 > bb1.x2) {
3327 return false;
3328 }
3329
3330 // case: one bb to left of other
3331 if (bb1.x2 < bb2.x1) {
3332 return false;
3333 }
3334 if (bb2.x2 < bb1.x1) {
3335 return false;
3336 }
3337
3338 // case: one bb above other
3339 if (bb1.y2 < bb2.y1) {
3340 return false;
3341 }
3342 if (bb2.y2 < bb1.y1) {
3343 return false;
3344 }
3345
3346 // case: one bb below other
3347 if (bb1.y1 > bb2.y2) {
3348 return false;
3349 }
3350 if (bb2.y1 > bb1.y2) {
3351 return false;
3352 }
3353
3354 // otherwise, must have some overlap
3355 return true;
3356};
3357var inBoundingBox = function inBoundingBox(bb, x, y) {
3358 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
3359};
3360var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
3361 return inBoundingBox(bb, pt.x, pt.y);
3362};
3363var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
3364 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
3365};
3366var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
3367 var radius = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 'auto';
3368 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : radius;
3369 var halfWidth = width / 2;
3370 var halfHeight = height / 2;
3371 cornerRadius = Math.min(cornerRadius, halfWidth, halfHeight);
3372 var doWidth = cornerRadius !== halfWidth,
3373 doHeight = cornerRadius !== halfHeight;
3374
3375 // Check intersections with straight line segments
3376 var straightLineIntersections;
3377
3378 // Top segment, left to right
3379 if (doWidth) {
3380 var topStartX = nodeX - halfWidth + cornerRadius - padding;
3381 var topStartY = nodeY - halfHeight - padding;
3382 var topEndX = nodeX + halfWidth - cornerRadius + padding;
3383 var topEndY = topStartY;
3384 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
3385 if (straightLineIntersections.length > 0) {
3386 return straightLineIntersections;
3387 }
3388 }
3389
3390 // Right segment, top to bottom
3391 if (doHeight) {
3392 var rightStartX = nodeX + halfWidth + padding;
3393 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
3394 var rightEndX = rightStartX;
3395 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
3396 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
3397 if (straightLineIntersections.length > 0) {
3398 return straightLineIntersections;
3399 }
3400 }
3401
3402 // Bottom segment, left to right
3403 if (doWidth) {
3404 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
3405 var bottomStartY = nodeY + halfHeight + padding;
3406 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
3407 var bottomEndY = bottomStartY;
3408 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
3409 if (straightLineIntersections.length > 0) {
3410 return straightLineIntersections;
3411 }
3412 }
3413
3414 // Left segment, top to bottom
3415 if (doHeight) {
3416 var leftStartX = nodeX - halfWidth - padding;
3417 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
3418 var leftEndX = leftStartX;
3419 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
3420 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
3421 if (straightLineIntersections.length > 0) {
3422 return straightLineIntersections;
3423 }
3424 }
3425
3426 // Check intersections with arc segments
3427 var arcIntersections;
3428
3429 // Top Left
3430 {
3431 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
3432 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
3433 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding);
3434
3435 // Ensure the intersection is on the desired quarter of the circle
3436 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
3437 return [arcIntersections[0], arcIntersections[1]];
3438 }
3439 }
3440
3441 // Top Right
3442 {
3443 var topRightCenterX = nodeX + halfWidth - cornerRadius;
3444 var topRightCenterY = nodeY - halfHeight + cornerRadius;
3445 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding);
3446
3447 // Ensure the intersection is on the desired quarter of the circle
3448 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
3449 return [arcIntersections[0], arcIntersections[1]];
3450 }
3451 }
3452
3453 // Bottom Right
3454 {
3455 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
3456 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
3457 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding);
3458
3459 // Ensure the intersection is on the desired quarter of the circle
3460 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
3461 return [arcIntersections[0], arcIntersections[1]];
3462 }
3463 }
3464
3465 // Bottom Left
3466 {
3467 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
3468 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
3469 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding);
3470
3471 // Ensure the intersection is on the desired quarter of the circle
3472 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
3473 return [arcIntersections[0], arcIntersections[1]];
3474 }
3475 }
3476 return []; // if nothing
3477};
3478
3479var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
3480 var t = tolerance;
3481 var x1 = Math.min(lx1, lx2);
3482 var x2 = Math.max(lx1, lx2);
3483 var y1 = Math.min(ly1, ly2);
3484 var y2 = Math.max(ly1, ly2);
3485 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
3486};
3487var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
3488 var bb = {
3489 x1: Math.min(x1, x3, x2) - tolerance,
3490 x2: Math.max(x1, x3, x2) + tolerance,
3491 y1: Math.min(y1, y3, y2) - tolerance,
3492 y2: Math.max(y1, y3, y2) + tolerance
3493 };
3494
3495 // if outside the rough bounding box for the bezier, then it can't be a hit
3496 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
3497 // console.log('bezier out of rough bb')
3498 return false;
3499 } else {
3500 // console.log('do more expensive check');
3501 return true;
3502 }
3503};
3504var solveQuadratic = function solveQuadratic(a, b, c, val) {
3505 c -= val;
3506 var r = b * b - 4 * a * c;
3507 if (r < 0) {
3508 return [];
3509 }
3510 var sqrtR = Math.sqrt(r);
3511 var denom = 2 * a;
3512 var root1 = (-b + sqrtR) / denom;
3513 var root2 = (-b - sqrtR) / denom;
3514 return [root1, root2];
3515};
3516var solveCubic = function solveCubic(a, b, c, d, result) {
3517 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
3518 // r is the real component, i is the imaginary component
3519
3520 // An implementation of the Cardano method from the year 1545
3521 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
3522
3523 var epsilon = 0.00001;
3524
3525 // avoid division by zero while keeping the overall expression close in value
3526 if (a === 0) {
3527 a = epsilon;
3528 }
3529 b /= a;
3530 c /= a;
3531 d /= a;
3532 var discriminant, q, r, dum1, s, t, term1, r13;
3533 q = (3.0 * c - b * b) / 9.0;
3534 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
3535 r /= 54.0;
3536 discriminant = q * q * q + r * r;
3537 result[1] = 0;
3538 term1 = b / 3.0;
3539 if (discriminant > 0) {
3540 s = r + Math.sqrt(discriminant);
3541 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
3542 t = r - Math.sqrt(discriminant);
3543 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
3544 result[0] = -term1 + s + t;
3545 term1 += (s + t) / 2.0;
3546 result[4] = result[2] = -term1;
3547 term1 = Math.sqrt(3.0) * (-t + s) / 2;
3548 result[3] = term1;
3549 result[5] = -term1;
3550 return;
3551 }
3552 result[5] = result[3] = 0;
3553 if (discriminant === 0) {
3554 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
3555 result[0] = -term1 + 2.0 * r13;
3556 result[4] = result[2] = -(r13 + term1);
3557 return;
3558 }
3559 q = -q;
3560 dum1 = q * q * q;
3561 dum1 = Math.acos(r / Math.sqrt(dum1));
3562 r13 = 2.0 * Math.sqrt(q);
3563 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
3564 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
3565 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
3566 return;
3567};
3568var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
3569 // Find minimum distance by using the minimum of the distance
3570 // function between the given point and the curve
3571
3572 // This gives the coefficients of the resulting cubic equation
3573 // whose roots tell us where a possible minimum is
3574 // (Coefficients are divided by 4)
3575
3576 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;
3577 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;
3578 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;
3579 var d = 1.0 * x1 * x2 - x1 * x1 + x1 * x - x2 * x + y1 * y2 - y1 * y1 + y1 * y - y2 * y;
3580
3581 // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a);
3582
3583 var roots = [];
3584
3585 // Use the cubic solving algorithm
3586 solveCubic(a, b, c, d, roots);
3587 var zeroThreshold = 0.0000001;
3588 var params = [];
3589 for (var index = 0; index < 6; index += 2) {
3590 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
3591 params.push(roots[index]);
3592 }
3593 }
3594 params.push(1.0);
3595 params.push(0.0);
3596 var minDistanceSquared = -1;
3597 var curX, curY, distSquared;
3598 for (var i = 0; i < params.length; i++) {
3599 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
3600 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
3601 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2);
3602 // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
3603 if (minDistanceSquared >= 0) {
3604 if (distSquared < minDistanceSquared) {
3605 minDistanceSquared = distSquared;
3606 }
3607 } else {
3608 minDistanceSquared = distSquared;
3609 }
3610 }
3611 return minDistanceSquared;
3612};
3613var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
3614 var offset = [x - x1, y - y1];
3615 var line = [x2 - x1, y2 - y1];
3616 var lineSq = line[0] * line[0] + line[1] * line[1];
3617 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
3618 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
3619 var adjSq = dotProduct * dotProduct / lineSq;
3620 if (dotProduct < 0) {
3621 return hypSq;
3622 }
3623 if (adjSq > lineSq) {
3624 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
3625 }
3626 return hypSq - adjSq;
3627};
3628var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
3629 var x1, y1, x2, y2;
3630 var y3;
3631
3632 // Intersect with vertical line through (x, y)
3633 var up = 0;
3634 // let down = 0;
3635 for (var i = 0; i < points.length / 2; i++) {
3636 x1 = points[i * 2];
3637 y1 = points[i * 2 + 1];
3638 if (i + 1 < points.length / 2) {
3639 x2 = points[(i + 1) * 2];
3640 y2 = points[(i + 1) * 2 + 1];
3641 } else {
3642 x2 = points[(i + 1 - points.length / 2) * 2];
3643 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
3644 }
3645 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
3646 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
3647 if (y3 > y) {
3648 up++;
3649 }
3650
3651 // if( y3 < y ){
3652 // down++;
3653 // }
3654 } else {
3655 continue;
3656 }
3657 }
3658 if (up % 2 === 0) {
3659 return false;
3660 } else {
3661 return true;
3662 }
3663};
3664var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
3665 var transformedPoints = new Array(basePoints.length);
3666
3667 // Gives negative angle
3668 var angle;
3669 if (direction[0] != null) {
3670 angle = Math.atan(direction[1] / direction[0]);
3671 if (direction[0] < 0) {
3672 angle = angle + Math.PI / 2;
3673 } else {
3674 angle = -angle - Math.PI / 2;
3675 }
3676 } else {
3677 angle = direction;
3678 }
3679 var cos = Math.cos(-angle);
3680 var sin = Math.sin(-angle);
3681
3682 // console.log("base: " + basePoints);
3683 for (var i = 0; i < transformedPoints.length / 2; i++) {
3684 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
3685 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
3686 transformedPoints[i * 2] += centerX;
3687 transformedPoints[i * 2 + 1] += centerY;
3688 }
3689 var points;
3690 if (padding > 0) {
3691 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3692 points = joinLines(expandedLineSet);
3693 } else {
3694 points = transformedPoints;
3695 }
3696 return pointInsidePolygonPoints(x, y, points);
3697};
3698var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height, corners) {
3699 var cutPolygonPoints = new Array(basePoints.length * 2);
3700 for (var i = 0; i < corners.length; i++) {
3701 var corner = corners[i];
3702 cutPolygonPoints[i * 4 + 0] = corner.startX;
3703 cutPolygonPoints[i * 4 + 1] = corner.startY;
3704 cutPolygonPoints[i * 4 + 2] = corner.stopX;
3705 cutPolygonPoints[i * 4 + 3] = corner.stopY;
3706 var squaredDistance = Math.pow(corner.cx - x, 2) + Math.pow(corner.cy - y, 2);
3707 if (squaredDistance <= Math.pow(corner.radius, 2)) {
3708 return true;
3709 }
3710 }
3711 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3712};
3713var joinLines = function joinLines(lineSet) {
3714 var vertices = new Array(lineSet.length / 2);
3715 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3716 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3717 for (var i = 0; i < lineSet.length / 4; i++) {
3718 currentLineStartX = lineSet[i * 4];
3719 currentLineStartY = lineSet[i * 4 + 1];
3720 currentLineEndX = lineSet[i * 4 + 2];
3721 currentLineEndY = lineSet[i * 4 + 3];
3722 if (i < lineSet.length / 4 - 1) {
3723 nextLineStartX = lineSet[(i + 1) * 4];
3724 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3725 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3726 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3727 } else {
3728 nextLineStartX = lineSet[0];
3729 nextLineStartY = lineSet[1];
3730 nextLineEndX = lineSet[2];
3731 nextLineEndY = lineSet[3];
3732 }
3733 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3734 vertices[i * 2] = intersection[0];
3735 vertices[i * 2 + 1] = intersection[1];
3736 }
3737 return vertices;
3738};
3739var expandPolygon = function expandPolygon(points, pad) {
3740 var expandedLineSet = new Array(points.length * 2);
3741 var currentPointX, currentPointY, nextPointX, nextPointY;
3742 for (var i = 0; i < points.length / 2; i++) {
3743 currentPointX = points[i * 2];
3744 currentPointY = points[i * 2 + 1];
3745 if (i < points.length / 2 - 1) {
3746 nextPointX = points[(i + 1) * 2];
3747 nextPointY = points[(i + 1) * 2 + 1];
3748 } else {
3749 nextPointX = points[0];
3750 nextPointY = points[1];
3751 }
3752
3753 // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3754
3755 // Assume CCW polygon winding
3756
3757 var offsetX = nextPointY - currentPointY;
3758 var offsetY = -(nextPointX - currentPointX);
3759
3760 // Normalize
3761 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3762 var normalizedOffsetX = offsetX / offsetLength;
3763 var normalizedOffsetY = offsetY / offsetLength;
3764 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3765 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3766 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3767 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3768 }
3769 return expandedLineSet;
3770};
3771var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3772 var dispX = centerX - x;
3773 var dispY = centerY - y;
3774 dispX /= ellipseWradius;
3775 dispY /= ellipseHradius;
3776 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3777 var newLength = len - 1;
3778 if (newLength < 0) {
3779 return [];
3780 }
3781 var lenProportion = newLength / len;
3782 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3783};
3784var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3785 x -= centerX;
3786 y -= centerY;
3787 x /= width / 2 + padding;
3788 y /= height / 2 + padding;
3789 return x * x + y * y <= 1;
3790};
3791
3792// Returns intersections of increasing distance from line's start point
3793var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3794 // Calculate d, direction vector of line
3795 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3796 var f = [x1 - centerX, y1 - centerY];
3797 var a = d[0] * d[0] + d[1] * d[1];
3798 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3799 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3800 var discriminant = b * b - 4 * a * c;
3801 if (discriminant < 0) {
3802 return [];
3803 }
3804 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3805 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3806 var tMin = Math.min(t1, t2);
3807 var tMax = Math.max(t1, t2);
3808 var inRangeParams = [];
3809 if (tMin >= 0 && tMin <= 1) {
3810 inRangeParams.push(tMin);
3811 }
3812 if (tMax >= 0 && tMax <= 1) {
3813 inRangeParams.push(tMax);
3814 }
3815 if (inRangeParams.length === 0) {
3816 return [];
3817 }
3818 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3819 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3820 if (inRangeParams.length > 1) {
3821 if (inRangeParams[0] == inRangeParams[1]) {
3822 return [nearIntersectionX, nearIntersectionY];
3823 } else {
3824 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3825 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3826 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3827 }
3828 } else {
3829 return [nearIntersectionX, nearIntersectionY];
3830 }
3831};
3832var midOfThree = function midOfThree(a, b, c) {
3833 if (b <= a && a <= c || c <= a && a <= b) {
3834 return a;
3835 } else if (a <= b && b <= c || c <= b && b <= a) {
3836 return b;
3837 } else {
3838 return c;
3839 }
3840};
3841
3842// (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3843var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3844 var dx13 = x1 - x3;
3845 var dx21 = x2 - x1;
3846 var dx43 = x4 - x3;
3847 var dy13 = y1 - y3;
3848 var dy21 = y2 - y1;
3849 var dy43 = y4 - y3;
3850 var ua_t = dx43 * dy13 - dy43 * dx13;
3851 var ub_t = dx21 * dy13 - dy21 * dx13;
3852 var u_b = dy43 * dx21 - dx43 * dy21;
3853 if (u_b !== 0) {
3854 var ua = ua_t / u_b;
3855 var ub = ub_t / u_b;
3856 var flptThreshold = 0.001;
3857 var _min = 0 - flptThreshold;
3858 var _max = 1 + flptThreshold;
3859 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3860 return [x1 + ua * dx21, y1 + ua * dy21];
3861 } else {
3862 if (!infiniteLines) {
3863 return [];
3864 } else {
3865 return [x1 + ua * dx21, y1 + ua * dy21];
3866 }
3867 }
3868 } else {
3869 if (ua_t === 0 || ub_t === 0) {
3870 // Parallel, coincident lines. Check if overlap
3871
3872 // Check endpoint of second line
3873 if (midOfThree(x1, x2, x4) === x4) {
3874 return [x4, y4];
3875 }
3876
3877 // Check start point of second line
3878 if (midOfThree(x1, x2, x3) === x3) {
3879 return [x3, y3];
3880 }
3881
3882 // Endpoint of first line
3883 if (midOfThree(x3, x4, x2) === x2) {
3884 return [x2, y2];
3885 }
3886 return [];
3887 } else {
3888 // Parallel, non-coincident
3889 return [];
3890 }
3891 }
3892};
3893
3894// math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3895// intersect a node polygon (pts transformed)
3896//
3897// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3898// intersect the points (no transform)
3899var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3900 var intersections = [];
3901 var intersection;
3902 var transformedPoints = new Array(basePoints.length);
3903 var doTransform = true;
3904 if (width == null) {
3905 doTransform = false;
3906 }
3907 var points;
3908 if (doTransform) {
3909 for (var i = 0; i < transformedPoints.length / 2; i++) {
3910 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3911 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3912 }
3913 if (padding > 0) {
3914 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3915 points = joinLines(expandedLineSet);
3916 } else {
3917 points = transformedPoints;
3918 }
3919 } else {
3920 points = basePoints;
3921 }
3922 var currentX, currentY, nextX, nextY;
3923 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3924 currentX = points[_i2 * 2];
3925 currentY = points[_i2 * 2 + 1];
3926 if (_i2 < points.length / 2 - 1) {
3927 nextX = points[(_i2 + 1) * 2];
3928 nextY = points[(_i2 + 1) * 2 + 1];
3929 } else {
3930 nextX = points[0];
3931 nextY = points[1];
3932 }
3933 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3934 if (intersection.length !== 0) {
3935 intersections.push(intersection[0], intersection[1]);
3936 }
3937 }
3938 return intersections;
3939};
3940var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding, corners) {
3941 var intersections = [];
3942 var intersection;
3943 var lines = new Array(basePoints.length * 2);
3944 corners.forEach(function (corner, i) {
3945 if (i === 0) {
3946 lines[lines.length - 2] = corner.startX;
3947 lines[lines.length - 1] = corner.startY;
3948 } else {
3949 lines[i * 4 - 2] = corner.startX;
3950 lines[i * 4 - 1] = corner.startY;
3951 }
3952 lines[i * 4] = corner.stopX;
3953 lines[i * 4 + 1] = corner.stopY;
3954 intersection = intersectLineCircle(x, y, centerX, centerY, corner.cx, corner.cy, corner.radius);
3955 if (intersection.length !== 0) {
3956 intersections.push(intersection[0], intersection[1]);
3957 }
3958 });
3959 for (var i = 0; i < lines.length / 4; i++) {
3960 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[i * 4], lines[i * 4 + 1], lines[i * 4 + 2], lines[i * 4 + 3], false);
3961 if (intersection.length !== 0) {
3962 intersections.push(intersection[0], intersection[1]);
3963 }
3964 }
3965 if (intersections.length > 2) {
3966 var lowestIntersection = [intersections[0], intersections[1]];
3967 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3968 for (var _i3 = 1; _i3 < intersections.length / 2; _i3++) {
3969 var squaredDistance = Math.pow(intersections[_i3 * 2] - x, 2) + Math.pow(intersections[_i3 * 2 + 1] - y, 2);
3970 if (squaredDistance <= lowestSquaredDistance) {
3971 lowestIntersection[0] = intersections[_i3 * 2];
3972 lowestIntersection[1] = intersections[_i3 * 2 + 1];
3973 lowestSquaredDistance = squaredDistance;
3974 }
3975 }
3976 return lowestIntersection;
3977 }
3978 return intersections;
3979};
3980var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3981 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3982 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3983 var lenRatio = (length - amount) / length;
3984 if (lenRatio < 0) {
3985 lenRatio = 0.00001;
3986 }
3987 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3988};
3989var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3990 var points = generateUnitNgonPoints(sides, rotationRadians);
3991 points = fitPolygonToSquare(points);
3992 return points;
3993};
3994var fitPolygonToSquare = function fitPolygonToSquare(points) {
3995 var x, y;
3996 var sides = points.length / 2;
3997 var minX = Infinity,
3998 minY = Infinity,
3999 maxX = -Infinity,
4000 maxY = -Infinity;
4001 for (var i = 0; i < sides; i++) {
4002 x = points[2 * i];
4003 y = points[2 * i + 1];
4004 minX = Math.min(minX, x);
4005 maxX = Math.max(maxX, x);
4006 minY = Math.min(minY, y);
4007 maxY = Math.max(maxY, y);
4008 }
4009
4010 // stretch factors
4011 var sx = 2 / (maxX - minX);
4012 var sy = 2 / (maxY - minY);
4013 for (var _i4 = 0; _i4 < sides; _i4++) {
4014 x = points[2 * _i4] = points[2 * _i4] * sx;
4015 y = points[2 * _i4 + 1] = points[2 * _i4 + 1] * sy;
4016 minX = Math.min(minX, x);
4017 maxX = Math.max(maxX, x);
4018 minY = Math.min(minY, y);
4019 maxY = Math.max(maxY, y);
4020 }
4021 if (minY < -1) {
4022 for (var _i5 = 0; _i5 < sides; _i5++) {
4023 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] + (-1 - minY);
4024 }
4025 }
4026 return points;
4027};
4028var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
4029 var increment = 1.0 / sides * 2 * Math.PI;
4030 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
4031 startAngle += rotationRadians;
4032 var points = new Array(sides * 2);
4033 var currentAngle;
4034 for (var i = 0; i < sides; i++) {
4035 currentAngle = i * increment + startAngle;
4036 points[2 * i] = Math.cos(currentAngle); // x
4037 points[2 * i + 1] = Math.sin(-currentAngle); // y
4038 }
4039
4040 return points;
4041};
4042
4043// Set the default radius, unless half of width or height is smaller than default
4044var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
4045 return Math.min(width / 4, height / 4, 8);
4046};
4047
4048// Set the default radius
4049var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
4050 return Math.min(width / 10, height / 10, 8);
4051};
4052var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
4053 return 8;
4054};
4055var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
4056 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
4057};
4058
4059// get curve width, height, and control point position offsets as a percentage of node height / width
4060var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
4061 return {
4062 heightOffset: Math.min(15, 0.05 * height),
4063 widthOffset: Math.min(100, 0.25 * width),
4064 ctrlPtOffsetPct: 0.05
4065 };
4066};
4067
4068var pageRankDefaults = defaults$g({
4069 dampingFactor: 0.8,
4070 precision: 0.000001,
4071 iterations: 200,
4072 weight: function weight(edge) {
4073 return 1;
4074 }
4075});
4076var elesfn$o = {
4077 pageRank: function pageRank(options) {
4078 var _pageRankDefaults = pageRankDefaults(options),
4079 dampingFactor = _pageRankDefaults.dampingFactor,
4080 precision = _pageRankDefaults.precision,
4081 iterations = _pageRankDefaults.iterations,
4082 weight = _pageRankDefaults.weight;
4083 var cy = this._private.cy;
4084 var _this$byGroup = this.byGroup(),
4085 nodes = _this$byGroup.nodes,
4086 edges = _this$byGroup.edges;
4087 var numNodes = nodes.length;
4088 var numNodesSqd = numNodes * numNodes;
4089 var numEdges = edges.length;
4090
4091 // Construct transposed adjacency matrix
4092 // First lets have a zeroed matrix of the right size
4093 // We'll also keep track of the sum of each column
4094 var matrix = new Array(numNodesSqd);
4095 var columnSum = new Array(numNodes);
4096 var additionalProb = (1 - dampingFactor) / numNodes;
4097
4098 // Create null matrix
4099 for (var i = 0; i < numNodes; i++) {
4100 for (var j = 0; j < numNodes; j++) {
4101 var n = i * numNodes + j;
4102 matrix[n] = 0;
4103 }
4104 columnSum[i] = 0;
4105 }
4106
4107 // Now, process edges
4108 for (var _i = 0; _i < numEdges; _i++) {
4109 var edge = edges[_i];
4110 var srcId = edge.data('source');
4111 var tgtId = edge.data('target');
4112
4113 // Don't include loops in the matrix
4114 if (srcId === tgtId) {
4115 continue;
4116 }
4117 var s = nodes.indexOfId(srcId);
4118 var t = nodes.indexOfId(tgtId);
4119 var w = weight(edge);
4120 var _n = t * numNodes + s;
4121
4122 // Update matrix
4123 matrix[_n] += w;
4124
4125 // Update column sum
4126 columnSum[s] += w;
4127 }
4128
4129 // Add additional probability based on damping factor
4130 // Also, take into account columns that have sum = 0
4131 var p = 1.0 / numNodes + additionalProb; // Shorthand
4132
4133 // Traverse matrix, column by column
4134 for (var _j = 0; _j < numNodes; _j++) {
4135 if (columnSum[_j] === 0) {
4136 // No 'links' out from node jth, assume equal probability for each possible node
4137 for (var _i2 = 0; _i2 < numNodes; _i2++) {
4138 var _n2 = _i2 * numNodes + _j;
4139 matrix[_n2] = p;
4140 }
4141 } else {
4142 // Node jth has outgoing link, compute normalized probabilities
4143 for (var _i3 = 0; _i3 < numNodes; _i3++) {
4144 var _n3 = _i3 * numNodes + _j;
4145 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
4146 }
4147 }
4148 }
4149
4150 // Compute dominant eigenvector using power method
4151 var eigenvector = new Array(numNodes);
4152 var temp = new Array(numNodes);
4153 var previous;
4154
4155 // Start with a vector of all 1's
4156 // Also, initialize a null vector which will be used as shorthand
4157 for (var _i4 = 0; _i4 < numNodes; _i4++) {
4158 eigenvector[_i4] = 1;
4159 }
4160 for (var iter = 0; iter < iterations; iter++) {
4161 // Temp array with all 0's
4162 for (var _i5 = 0; _i5 < numNodes; _i5++) {
4163 temp[_i5] = 0;
4164 }
4165
4166 // Multiply matrix with previous result
4167 for (var _i6 = 0; _i6 < numNodes; _i6++) {
4168 for (var _j2 = 0; _j2 < numNodes; _j2++) {
4169 var _n4 = _i6 * numNodes + _j2;
4170 temp[_i6] += matrix[_n4] * eigenvector[_j2];
4171 }
4172 }
4173 inPlaceSumNormalize(temp);
4174 previous = eigenvector;
4175 eigenvector = temp;
4176 temp = previous;
4177 var diff = 0;
4178 // Compute difference (squared module) of both vectors
4179 for (var _i7 = 0; _i7 < numNodes; _i7++) {
4180 var delta = previous[_i7] - eigenvector[_i7];
4181 diff += delta * delta;
4182 }
4183
4184 // If difference is less than the desired threshold, stop iterating
4185 if (diff < precision) {
4186 break;
4187 }
4188 }
4189
4190 // Construct result
4191 var res = {
4192 rank: function rank(node) {
4193 node = cy.collection(node)[0];
4194 return eigenvector[nodes.indexOf(node)];
4195 }
4196 };
4197 return res;
4198 } // pageRank
4199}; // elesfn
4200
4201var defaults$f = defaults$g({
4202 root: null,
4203 weight: function weight(edge) {
4204 return 1;
4205 },
4206 directed: false,
4207 alpha: 0
4208});
4209var elesfn$n = {
4210 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
4211 options = defaults$f(options);
4212 var cy = this.cy();
4213 var nodes = this.nodes();
4214 var numNodes = nodes.length;
4215 if (!options.directed) {
4216 var degrees = {};
4217 var maxDegree = 0;
4218 for (var i = 0; i < numNodes; i++) {
4219 var node = nodes[i];
4220
4221 // add current node to the current options object and call degreeCentrality
4222 options.root = node;
4223 var currDegree = this.degreeCentrality(options);
4224 if (maxDegree < currDegree.degree) {
4225 maxDegree = currDegree.degree;
4226 }
4227 degrees[node.id()] = currDegree.degree;
4228 }
4229 return {
4230 degree: function degree(node) {
4231 if (maxDegree === 0) {
4232 return 0;
4233 }
4234 if (string(node)) {
4235 // from is a selector string
4236 node = cy.filter(node);
4237 }
4238 return degrees[node.id()] / maxDegree;
4239 }
4240 };
4241 } else {
4242 var indegrees = {};
4243 var outdegrees = {};
4244 var maxIndegree = 0;
4245 var maxOutdegree = 0;
4246 for (var _i = 0; _i < numNodes; _i++) {
4247 var _node = nodes[_i];
4248 var id = _node.id();
4249
4250 // add current node to the current options object and call degreeCentrality
4251 options.root = _node;
4252 var _currDegree = this.degreeCentrality(options);
4253 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
4254 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
4255 indegrees[id] = _currDegree.indegree;
4256 outdegrees[id] = _currDegree.outdegree;
4257 }
4258 return {
4259 indegree: function indegree(node) {
4260 if (maxIndegree == 0) {
4261 return 0;
4262 }
4263 if (string(node)) {
4264 // from is a selector string
4265 node = cy.filter(node);
4266 }
4267 return indegrees[node.id()] / maxIndegree;
4268 },
4269 outdegree: function outdegree(node) {
4270 if (maxOutdegree === 0) {
4271 return 0;
4272 }
4273 if (string(node)) {
4274 // from is a selector string
4275 node = cy.filter(node);
4276 }
4277 return outdegrees[node.id()] / maxOutdegree;
4278 }
4279 };
4280 }
4281 },
4282 // degreeCentralityNormalized
4283
4284 // Implemented from the algorithm in Opsahl's paper
4285 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
4286 // check the heading 2 "Degree"
4287 degreeCentrality: function degreeCentrality(options) {
4288 options = defaults$f(options);
4289 var cy = this.cy();
4290 var callingEles = this;
4291 var _options = options,
4292 root = _options.root,
4293 weight = _options.weight,
4294 directed = _options.directed,
4295 alpha = _options.alpha;
4296 root = cy.collection(root)[0];
4297 if (!directed) {
4298 var connEdges = root.connectedEdges().intersection(callingEles);
4299 var k = connEdges.length;
4300 var s = 0;
4301
4302 // Now, sum edge weights
4303 for (var i = 0; i < connEdges.length; i++) {
4304 s += weight(connEdges[i]);
4305 }
4306 return {
4307 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
4308 };
4309 } else {
4310 var edges = root.connectedEdges();
4311 var incoming = edges.filter(function (edge) {
4312 return edge.target().same(root) && callingEles.has(edge);
4313 });
4314 var outgoing = edges.filter(function (edge) {
4315 return edge.source().same(root) && callingEles.has(edge);
4316 });
4317 var k_in = incoming.length;
4318 var k_out = outgoing.length;
4319 var s_in = 0;
4320 var s_out = 0;
4321
4322 // Now, sum incoming edge weights
4323 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
4324 s_in += weight(incoming[_i2]);
4325 }
4326
4327 // Now, sum outgoing edge weights
4328 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
4329 s_out += weight(outgoing[_i3]);
4330 }
4331 return {
4332 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
4333 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
4334 };
4335 }
4336 } // degreeCentrality
4337}; // elesfn
4338
4339// nice, short mathematical alias
4340elesfn$n.dc = elesfn$n.degreeCentrality;
4341elesfn$n.dcn = elesfn$n.degreeCentralityNormalised = elesfn$n.degreeCentralityNormalized;
4342
4343var defaults$e = defaults$g({
4344 harmonic: true,
4345 weight: function weight() {
4346 return 1;
4347 },
4348 directed: false,
4349 root: null
4350});
4351var elesfn$m = {
4352 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
4353 var _defaults = defaults$e(options),
4354 harmonic = _defaults.harmonic,
4355 weight = _defaults.weight,
4356 directed = _defaults.directed;
4357 var cy = this.cy();
4358 var closenesses = {};
4359 var maxCloseness = 0;
4360 var nodes = this.nodes();
4361 var fw = this.floydWarshall({
4362 weight: weight,
4363 directed: directed
4364 });
4365
4366 // Compute closeness for every node and find the maximum closeness
4367 for (var i = 0; i < nodes.length; i++) {
4368 var currCloseness = 0;
4369 var node_i = nodes[i];
4370 for (var j = 0; j < nodes.length; j++) {
4371 if (i !== j) {
4372 var d = fw.distance(node_i, nodes[j]);
4373 if (harmonic) {
4374 currCloseness += 1 / d;
4375 } else {
4376 currCloseness += d;
4377 }
4378 }
4379 }
4380 if (!harmonic) {
4381 currCloseness = 1 / currCloseness;
4382 }
4383 if (maxCloseness < currCloseness) {
4384 maxCloseness = currCloseness;
4385 }
4386 closenesses[node_i.id()] = currCloseness;
4387 }
4388 return {
4389 closeness: function closeness(node) {
4390 if (maxCloseness == 0) {
4391 return 0;
4392 }
4393 if (string(node)) {
4394 // from is a selector string
4395 node = cy.filter(node)[0].id();
4396 } else {
4397 // from is a node
4398 node = node.id();
4399 }
4400 return closenesses[node] / maxCloseness;
4401 }
4402 };
4403 },
4404 // Implemented from pseudocode from wikipedia
4405 closenessCentrality: function closenessCentrality(options) {
4406 var _defaults2 = defaults$e(options),
4407 root = _defaults2.root,
4408 weight = _defaults2.weight,
4409 directed = _defaults2.directed,
4410 harmonic = _defaults2.harmonic;
4411 root = this.filter(root)[0];
4412
4413 // we need distance from this node to every other node
4414 var dijkstra = this.dijkstra({
4415 root: root,
4416 weight: weight,
4417 directed: directed
4418 });
4419 var totalDistance = 0;
4420 var nodes = this.nodes();
4421 for (var i = 0; i < nodes.length; i++) {
4422 var n = nodes[i];
4423 if (!n.same(root)) {
4424 var d = dijkstra.distanceTo(n);
4425 if (harmonic) {
4426 totalDistance += 1 / d;
4427 } else {
4428 totalDistance += d;
4429 }
4430 }
4431 }
4432 return harmonic ? totalDistance : 1 / totalDistance;
4433 } // closenessCentrality
4434}; // elesfn
4435
4436// nice, short mathematical alias
4437elesfn$m.cc = elesfn$m.closenessCentrality;
4438elesfn$m.ccn = elesfn$m.closenessCentralityNormalised = elesfn$m.closenessCentralityNormalized;
4439
4440var defaults$d = defaults$g({
4441 weight: null,
4442 directed: false
4443});
4444var elesfn$l = {
4445 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
4446 betweennessCentrality: function betweennessCentrality(options) {
4447 var _defaults = defaults$d(options),
4448 directed = _defaults.directed,
4449 weight = _defaults.weight;
4450 var weighted = weight != null;
4451 var cy = this.cy();
4452
4453 // starting
4454 var V = this.nodes();
4455 var A = {};
4456 var _C = {};
4457 var max = 0;
4458 var C = {
4459 set: function set(key, val) {
4460 _C[key] = val;
4461 if (val > max) {
4462 max = val;
4463 }
4464 },
4465 get: function get(key) {
4466 return _C[key];
4467 }
4468 };
4469
4470 // A contains the neighborhoods of every node
4471 for (var i = 0; i < V.length; i++) {
4472 var v = V[i];
4473 var vid = v.id();
4474 if (directed) {
4475 A[vid] = v.outgoers().nodes(); // get outgoers of every node
4476 } else {
4477 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
4478 }
4479
4480 C.set(vid, 0);
4481 }
4482 var _loop = function _loop(s) {
4483 var sid = V[s].id();
4484 var S = []; // stack
4485 var P = {};
4486 var g = {};
4487 var d = {};
4488 var Q = new heap(function (a, b) {
4489 return d[a] - d[b];
4490 }); // queue
4491
4492 // init dictionaries
4493 for (var _i = 0; _i < V.length; _i++) {
4494 var _vid = V[_i].id();
4495 P[_vid] = [];
4496 g[_vid] = 0;
4497 d[_vid] = Infinity;
4498 }
4499 g[sid] = 1; // sigma
4500 d[sid] = 0; // distance to s
4501
4502 Q.push(sid);
4503 while (!Q.empty()) {
4504 var _v = Q.pop();
4505 S.push(_v);
4506 if (weighted) {
4507 for (var j = 0; j < A[_v].length; j++) {
4508 var w = A[_v][j];
4509 var vEle = cy.getElementById(_v);
4510 var edge = void 0;
4511 if (vEle.edgesTo(w).length > 0) {
4512 edge = vEle.edgesTo(w)[0];
4513 } else {
4514 edge = w.edgesTo(vEle)[0];
4515 }
4516 var edgeWeight = weight(edge);
4517 w = w.id();
4518 if (d[w] > d[_v] + edgeWeight) {
4519 d[w] = d[_v] + edgeWeight;
4520 if (Q.nodes.indexOf(w) < 0) {
4521 //if w is not in Q
4522 Q.push(w);
4523 } else {
4524 // update position if w is in Q
4525 Q.updateItem(w);
4526 }
4527 g[w] = 0;
4528 P[w] = [];
4529 }
4530 if (d[w] == d[_v] + edgeWeight) {
4531 g[w] = g[w] + g[_v];
4532 P[w].push(_v);
4533 }
4534 }
4535 } else {
4536 for (var _j = 0; _j < A[_v].length; _j++) {
4537 var _w = A[_v][_j].id();
4538 if (d[_w] == Infinity) {
4539 Q.push(_w);
4540 d[_w] = d[_v] + 1;
4541 }
4542 if (d[_w] == d[_v] + 1) {
4543 g[_w] = g[_w] + g[_v];
4544 P[_w].push(_v);
4545 }
4546 }
4547 }
4548 }
4549 var e = {};
4550 for (var _i2 = 0; _i2 < V.length; _i2++) {
4551 e[V[_i2].id()] = 0;
4552 }
4553 while (S.length > 0) {
4554 var _w2 = S.pop();
4555 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4556 var _v2 = P[_w2][_j2];
4557 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4558 }
4559 if (_w2 != V[s].id()) {
4560 C.set(_w2, C.get(_w2) + e[_w2]);
4561 }
4562 }
4563 };
4564 for (var s = 0; s < V.length; s++) {
4565 _loop(s);
4566 }
4567 var ret = {
4568 betweenness: function betweenness(node) {
4569 var id = cy.collection(node).id();
4570 return C.get(id);
4571 },
4572 betweennessNormalized: function betweennessNormalized(node) {
4573 if (max == 0) {
4574 return 0;
4575 }
4576 var id = cy.collection(node).id();
4577 return C.get(id) / max;
4578 }
4579 };
4580
4581 // alias
4582 ret.betweennessNormalised = ret.betweennessNormalized;
4583 return ret;
4584 } // betweennessCentrality
4585}; // elesfn
4586
4587// nice, short mathematical alias
4588elesfn$l.bc = elesfn$l.betweennessCentrality;
4589
4590// Implemented by Zoe Xi @zoexi for GSOC 2016
4591
4592/* eslint-disable no-unused-vars */
4593var defaults$c = defaults$g({
4594 expandFactor: 2,
4595 // affects time of computation and cluster granularity to some extent: M * M
4596 inflateFactor: 2,
4597 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4598 multFactor: 1,
4599 // optional self loops for each node. Use a neutral value to improve cluster computations.
4600 maxIterations: 20,
4601 // maximum number of iterations of the MCL algorithm in a single run
4602 attributes: [
4603 // attributes/features used to group nodes, ie. similarity values between nodes
4604 function (edge) {
4605 return 1;
4606 }]
4607});
4608/* eslint-enable */
4609
4610var setOptions$3 = function setOptions(options) {
4611 return defaults$c(options);
4612};
4613/* eslint-enable */
4614
4615var getSimilarity$1 = function getSimilarity(edge, attributes) {
4616 var total = 0;
4617 for (var i = 0; i < attributes.length; i++) {
4618 total += attributes[i](edge);
4619 }
4620 return total;
4621};
4622var addLoops = function addLoops(M, n, val) {
4623 for (var i = 0; i < n; i++) {
4624 M[i * n + i] = val;
4625 }
4626};
4627var normalize = function normalize(M, n) {
4628 var sum;
4629 for (var col = 0; col < n; col++) {
4630 sum = 0;
4631 for (var row = 0; row < n; row++) {
4632 sum += M[row * n + col];
4633 }
4634 for (var _row = 0; _row < n; _row++) {
4635 M[_row * n + col] = M[_row * n + col] / sum;
4636 }
4637 }
4638};
4639
4640// TODO: blocked matrix multiplication?
4641var mmult = function mmult(A, B, n) {
4642 var C = new Array(n * n);
4643 for (var i = 0; i < n; i++) {
4644 for (var j = 0; j < n; j++) {
4645 C[i * n + j] = 0;
4646 }
4647 for (var k = 0; k < n; k++) {
4648 for (var _j = 0; _j < n; _j++) {
4649 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4650 }
4651 }
4652 }
4653 return C;
4654};
4655var expand = function expand(M, n, expandFactor /** power **/) {
4656 var _M = M.slice(0);
4657 for (var p = 1; p < expandFactor; p++) {
4658 M = mmult(M, _M, n);
4659 }
4660 return M;
4661};
4662var inflate = function inflate(M, n, inflateFactor /** r **/) {
4663 var _M = new Array(n * n);
4664
4665 // M(i,j) ^ inflatePower
4666 for (var i = 0; i < n * n; i++) {
4667 _M[i] = Math.pow(M[i], inflateFactor);
4668 }
4669 normalize(_M, n);
4670 return _M;
4671};
4672var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4673 // Check that both matrices have the same elements (i,j)
4674 for (var i = 0; i < n2; i++) {
4675 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4676 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4677 if (v1 !== v2) {
4678 return false;
4679 }
4680 }
4681 return true;
4682};
4683var assign$2 = function assign(M, n, nodes, cy) {
4684 var clusters = [];
4685 for (var i = 0; i < n; i++) {
4686 var cluster = [];
4687 for (var j = 0; j < n; j++) {
4688 // Row-wise attractors and elements that they attract belong in same cluster
4689 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4690 cluster.push(nodes[j]);
4691 }
4692 }
4693 if (cluster.length !== 0) {
4694 clusters.push(cy.collection(cluster));
4695 }
4696 }
4697 return clusters;
4698};
4699var isDuplicate = function isDuplicate(c1, c2) {
4700 for (var i = 0; i < c1.length; i++) {
4701 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4702 return false;
4703 }
4704 }
4705 return true;
4706};
4707var removeDuplicates = function removeDuplicates(clusters) {
4708 for (var i = 0; i < clusters.length; i++) {
4709 for (var j = 0; j < clusters.length; j++) {
4710 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4711 clusters.splice(j, 1);
4712 }
4713 }
4714 }
4715 return clusters;
4716};
4717var markovClustering = function markovClustering(options) {
4718 var nodes = this.nodes();
4719 var edges = this.edges();
4720 var cy = this.cy();
4721
4722 // Set parameters of algorithm:
4723 var opts = setOptions$3(options);
4724
4725 // Map each node to its position in node array
4726 var id2position = {};
4727 for (var i = 0; i < nodes.length; i++) {
4728 id2position[nodes[i].id()] = i;
4729 }
4730
4731 // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4732 var n = nodes.length,
4733 n2 = n * n;
4734 var M = new Array(n2),
4735 _M;
4736 for (var _i = 0; _i < n2; _i++) {
4737 M[_i] = 0;
4738 }
4739 for (var e = 0; e < edges.length; e++) {
4740 var edge = edges[e];
4741 var _i2 = id2position[edge.source().id()];
4742 var j = id2position[edge.target().id()];
4743 var sim = getSimilarity$1(edge, opts.attributes);
4744 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4745 M[j * n + _i2] += sim;
4746 }
4747
4748 // Begin Markov cluster algorithm
4749
4750 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4751 addLoops(M, n, opts.multFactor);
4752
4753 // Step 2: M = normalize( M );
4754 normalize(M, n);
4755 var isStillMoving = true;
4756 var iterations = 0;
4757 while (isStillMoving && iterations < opts.maxIterations) {
4758 isStillMoving = false;
4759
4760 // Step 3:
4761 _M = expand(M, n, opts.expandFactor);
4762
4763 // Step 4:
4764 M = inflate(_M, n, opts.inflateFactor);
4765
4766 // Step 5: check to see if ~steady state has been reached
4767 if (!hasConverged(M, _M, n2, 4)) {
4768 isStillMoving = true;
4769 }
4770 iterations++;
4771 }
4772
4773 // Build clusters from matrix
4774 var clusters = assign$2(M, n, nodes, cy);
4775
4776 // Remove duplicate clusters due to symmetry of graph and M matrix
4777 clusters = removeDuplicates(clusters);
4778 return clusters;
4779};
4780var markovClustering$1 = {
4781 markovClustering: markovClustering,
4782 mcl: markovClustering
4783};
4784
4785// Common distance metrics for clustering algorithms
4786var identity = function identity(x) {
4787 return x;
4788};
4789var absDiff = function absDiff(p, q) {
4790 return Math.abs(q - p);
4791};
4792var addAbsDiff = function addAbsDiff(total, p, q) {
4793 return total + absDiff(p, q);
4794};
4795var addSquaredDiff = function addSquaredDiff(total, p, q) {
4796 return total + Math.pow(q - p, 2);
4797};
4798var sqrt = function sqrt(x) {
4799 return Math.sqrt(x);
4800};
4801var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4802 return Math.max(currentMax, absDiff(p, q));
4803};
4804var getDistance = function getDistance(length, getP, getQ, init, visit) {
4805 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4806 var ret = init;
4807 var p, q;
4808 for (var dim = 0; dim < length; dim++) {
4809 p = getP(dim);
4810 q = getQ(dim);
4811 ret = visit(ret, p, q);
4812 }
4813 return post(ret);
4814};
4815var distances = {
4816 euclidean: function euclidean(length, getP, getQ) {
4817 if (length >= 2) {
4818 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4819 } else {
4820 // for single attr case, more efficient to avoid sqrt
4821 return getDistance(length, getP, getQ, 0, addAbsDiff);
4822 }
4823 },
4824 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4825 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4826 },
4827 manhattan: function manhattan(length, getP, getQ) {
4828 return getDistance(length, getP, getQ, 0, addAbsDiff);
4829 },
4830 max: function max(length, getP, getQ) {
4831 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4832 }
4833};
4834
4835// in case the user accidentally doesn't use camel case
4836distances['squared-euclidean'] = distances['squaredEuclidean'];
4837distances['squaredeuclidean'] = distances['squaredEuclidean'];
4838function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4839 var impl;
4840 if (fn$6(method)) {
4841 impl = method;
4842 } else {
4843 impl = distances[method] || distances.euclidean;
4844 }
4845 if (length === 0 && fn$6(method)) {
4846 return impl(nodeP, nodeQ);
4847 } else {
4848 return impl(length, getP, getQ, nodeP, nodeQ);
4849 }
4850}
4851
4852var defaults$b = defaults$g({
4853 k: 2,
4854 m: 2,
4855 sensitivityThreshold: 0.0001,
4856 distance: 'euclidean',
4857 maxIterations: 10,
4858 attributes: [],
4859 testMode: false,
4860 testCentroids: null
4861});
4862var setOptions$2 = function setOptions(options) {
4863 return defaults$b(options);
4864};
4865
4866var getDist = function getDist(type, node, centroid, attributes, mode) {
4867 var noNodeP = mode !== 'kMedoids';
4868 var getP = noNodeP ? function (i) {
4869 return centroid[i];
4870 } : function (i) {
4871 return attributes[i](centroid);
4872 };
4873 var getQ = function getQ(i) {
4874 return attributes[i](node);
4875 };
4876 var nodeP = centroid;
4877 var nodeQ = node;
4878 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4879};
4880var randomCentroids = function randomCentroids(nodes, k, attributes) {
4881 var ndim = attributes.length;
4882 var min = new Array(ndim);
4883 var max = new Array(ndim);
4884 var centroids = new Array(k);
4885 var centroid = null;
4886
4887 // Find min, max values for each attribute dimension
4888 for (var i = 0; i < ndim; i++) {
4889 min[i] = nodes.min(attributes[i]).value;
4890 max[i] = nodes.max(attributes[i]).value;
4891 }
4892
4893 // Build k centroids, each represented as an n-dim feature vector
4894 for (var c = 0; c < k; c++) {
4895 centroid = [];
4896 for (var _i = 0; _i < ndim; _i++) {
4897 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4898 }
4899
4900 centroids[c] = centroid;
4901 }
4902 return centroids;
4903};
4904var classify = function classify(node, centroids, distance, attributes, type) {
4905 var min = Infinity;
4906 var index = 0;
4907 for (var i = 0; i < centroids.length; i++) {
4908 var dist = getDist(distance, node, centroids[i], attributes, type);
4909 if (dist < min) {
4910 min = dist;
4911 index = i;
4912 }
4913 }
4914 return index;
4915};
4916var buildCluster = function buildCluster(centroid, nodes, assignment) {
4917 var cluster = [];
4918 var node = null;
4919 for (var n = 0; n < nodes.length; n++) {
4920 node = nodes[n];
4921 if (assignment[node.id()] === centroid) {
4922 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4923 cluster.push(node);
4924 }
4925 }
4926 return cluster;
4927};
4928var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4929 return Math.abs(v2 - v1) <= sensitivityThreshold;
4930};
4931var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4932 for (var i = 0; i < v1.length; i++) {
4933 for (var j = 0; j < v1[i].length; j++) {
4934 var diff = Math.abs(v1[i][j] - v2[i][j]);
4935 if (diff > sensitivityThreshold) {
4936 return false;
4937 }
4938 }
4939 }
4940 return true;
4941};
4942var seenBefore = function seenBefore(node, medoids, n) {
4943 for (var i = 0; i < n; i++) {
4944 if (node === medoids[i]) return true;
4945 }
4946 return false;
4947};
4948var randomMedoids = function randomMedoids(nodes, k) {
4949 var medoids = new Array(k);
4950
4951 // For small data sets, the probability of medoid conflict is greater,
4952 // so we need to check to see if we've already seen or chose this node before.
4953 if (nodes.length < 50) {
4954 // Randomly select k medoids from the n nodes
4955 for (var i = 0; i < k; i++) {
4956 var node = nodes[Math.floor(Math.random() * nodes.length)];
4957
4958 // If we've already chosen this node to be a medoid, don't choose it again (for small data sets).
4959 // Instead choose a different random node.
4960 while (seenBefore(node, medoids, i)) {
4961 node = nodes[Math.floor(Math.random() * nodes.length)];
4962 }
4963 medoids[i] = node;
4964 }
4965 } else {
4966 // Relatively large data set, so pretty safe to not check and just select random nodes
4967 for (var _i2 = 0; _i2 < k; _i2++) {
4968 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4969 }
4970 }
4971 return medoids;
4972};
4973var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4974 var cost = 0;
4975 for (var n = 0; n < cluster.length; n++) {
4976 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4977 }
4978 return cost;
4979};
4980var kMeans = function kMeans(options) {
4981 var cy = this.cy();
4982 var nodes = this.nodes();
4983 var node = null;
4984
4985 // Set parameters of algorithm: # of clusters, distance metric, etc.
4986 var opts = setOptions$2(options);
4987
4988 // Begin k-means algorithm
4989 var clusters = new Array(opts.k);
4990 var assignment = {};
4991 var centroids;
4992
4993 // Step 1: Initialize centroid positions
4994 if (opts.testMode) {
4995 if (typeof opts.testCentroids === 'number') {
4996 // TODO: implement a seeded random number generator.
4997 opts.testCentroids;
4998 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4999 } else if (_typeof(opts.testCentroids) === 'object') {
5000 centroids = opts.testCentroids;
5001 } else {
5002 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5003 }
5004 } else {
5005 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5006 }
5007 var isStillMoving = true;
5008 var iterations = 0;
5009 while (isStillMoving && iterations < opts.maxIterations) {
5010 // Step 2: Assign nodes to the nearest centroid
5011 for (var n = 0; n < nodes.length; n++) {
5012 node = nodes[n];
5013 // Determine which cluster this node belongs to: node id => cluster #
5014 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
5015 }
5016
5017 // Step 3: For each of the k clusters, update its centroid
5018 isStillMoving = false;
5019 for (var c = 0; c < opts.k; c++) {
5020 // Get all nodes that belong to this cluster
5021 var cluster = buildCluster(c, nodes, assignment);
5022 if (cluster.length === 0) {
5023 // If cluster is empty, break out early & move to next cluster
5024 continue;
5025 }
5026
5027 // Update centroids by calculating avg of all nodes within the cluster.
5028 var ndim = opts.attributes.length;
5029 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
5030 var newCentroid = new Array(ndim);
5031 var sum = new Array(ndim);
5032 for (var d = 0; d < ndim; d++) {
5033 sum[d] = 0.0;
5034 for (var i = 0; i < cluster.length; i++) {
5035 node = cluster[i];
5036 sum[d] += opts.attributes[d](node);
5037 }
5038 newCentroid[d] = sum[d] / cluster.length;
5039
5040 // Check to see if algorithm has converged, i.e. when centroids no longer change
5041 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
5042 isStillMoving = true;
5043 }
5044 }
5045 centroids[c] = newCentroid;
5046 clusters[c] = cy.collection(cluster);
5047 }
5048 iterations++;
5049 }
5050 return clusters;
5051};
5052var kMedoids = function kMedoids(options) {
5053 var cy = this.cy();
5054 var nodes = this.nodes();
5055 var node = null;
5056 var opts = setOptions$2(options);
5057
5058 // Begin k-medoids algorithm
5059 var clusters = new Array(opts.k);
5060 var medoids;
5061 var assignment = {};
5062 var curCost;
5063 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
5064
5065 // Step 1: Initialize k medoids
5066 if (opts.testMode) {
5067 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
5068 medoids = opts.testCentroids;
5069 } else {
5070 medoids = randomMedoids(nodes, opts.k);
5071 }
5072 } else {
5073 medoids = randomMedoids(nodes, opts.k);
5074 }
5075 var isStillMoving = true;
5076 var iterations = 0;
5077 while (isStillMoving && iterations < opts.maxIterations) {
5078 // Step 2: Assign nodes to the nearest medoid
5079 for (var n = 0; n < nodes.length; n++) {
5080 node = nodes[n];
5081 // Determine which cluster this node belongs to: node id => cluster #
5082 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
5083 }
5084 isStillMoving = false;
5085 // Step 3: For each medoid m, and for each node associated with mediod m,
5086 // select the node with the lowest configuration cost as new medoid.
5087 for (var m = 0; m < medoids.length; m++) {
5088 // Get all nodes that belong to this medoid
5089 var cluster = buildCluster(m, nodes, assignment);
5090 if (cluster.length === 0) {
5091 // If cluster is empty, break out early & move to next cluster
5092 continue;
5093 }
5094 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
5095
5096 // Select different medoid if its configuration has the lowest cost
5097 for (var _n = 0; _n < cluster.length; _n++) {
5098 curCost = findCost(cluster[_n], cluster, opts.attributes);
5099 if (curCost < minCosts[m]) {
5100 minCosts[m] = curCost;
5101 medoids[m] = cluster[_n];
5102 isStillMoving = true;
5103 }
5104 }
5105 clusters[m] = cy.collection(cluster);
5106 }
5107 iterations++;
5108 }
5109 return clusters;
5110};
5111var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
5112 var numerator, denominator;
5113 for (var n = 0; n < nodes.length; n++) {
5114 for (var c = 0; c < centroids.length; c++) {
5115 weight[n][c] = Math.pow(U[n][c], opts.m);
5116 }
5117 }
5118 for (var _c = 0; _c < centroids.length; _c++) {
5119 for (var dim = 0; dim < opts.attributes.length; dim++) {
5120 numerator = 0;
5121 denominator = 0;
5122 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
5123 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
5124 denominator += weight[_n2][_c];
5125 }
5126 centroids[_c][dim] = numerator / denominator;
5127 }
5128 }
5129};
5130var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
5131 // Save previous step
5132 for (var i = 0; i < U.length; i++) {
5133 _U[i] = U[i].slice();
5134 }
5135 var sum, numerator, denominator;
5136 var pow = 2 / (opts.m - 1);
5137 for (var c = 0; c < centroids.length; c++) {
5138 for (var n = 0; n < nodes.length; n++) {
5139 sum = 0;
5140 for (var k = 0; k < centroids.length; k++) {
5141 // against all other centroids
5142 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
5143 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
5144 sum += Math.pow(numerator / denominator, pow);
5145 }
5146 U[n][c] = 1 / sum;
5147 }
5148 }
5149};
5150var assign$1 = function assign(nodes, U, opts, cy) {
5151 var clusters = new Array(opts.k);
5152 for (var c = 0; c < clusters.length; c++) {
5153 clusters[c] = [];
5154 }
5155 var max;
5156 var index;
5157 for (var n = 0; n < U.length; n++) {
5158 // for each node (U is N x C matrix)
5159 max = -Infinity;
5160 index = -1;
5161 // Determine which cluster the node is most likely to belong in
5162 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
5163 if (U[n][_c2] > max) {
5164 max = U[n][_c2];
5165 index = _c2;
5166 }
5167 }
5168 clusters[index].push(nodes[n]);
5169 }
5170
5171 // Turn every array into a collection of nodes
5172 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
5173 clusters[_c3] = cy.collection(clusters[_c3]);
5174 }
5175 return clusters;
5176};
5177var fuzzyCMeans = function fuzzyCMeans(options) {
5178 var cy = this.cy();
5179 var nodes = this.nodes();
5180 var opts = setOptions$2(options);
5181
5182 // Begin fuzzy c-means algorithm
5183 var clusters;
5184 var centroids;
5185 var U;
5186 var _U;
5187 var weight;
5188
5189 // Step 1: Initialize letiables.
5190 _U = new Array(nodes.length);
5191 for (var i = 0; i < nodes.length; i++) {
5192 // N x C matrix
5193 _U[i] = new Array(opts.k);
5194 }
5195 U = new Array(nodes.length);
5196 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
5197 // N x C matrix
5198 U[_i3] = new Array(opts.k);
5199 }
5200 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
5201 var total = 0;
5202 for (var j = 0; j < opts.k; j++) {
5203 U[_i4][j] = Math.random();
5204 total += U[_i4][j];
5205 }
5206 for (var _j = 0; _j < opts.k; _j++) {
5207 U[_i4][_j] = U[_i4][_j] / total;
5208 }
5209 }
5210 centroids = new Array(opts.k);
5211 for (var _i5 = 0; _i5 < opts.k; _i5++) {
5212 centroids[_i5] = new Array(opts.attributes.length);
5213 }
5214 weight = new Array(nodes.length);
5215 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
5216 // N x C matrix
5217 weight[_i6] = new Array(opts.k);
5218 }
5219 // end init FCM
5220
5221 var isStillMoving = true;
5222 var iterations = 0;
5223 while (isStillMoving && iterations < opts.maxIterations) {
5224 isStillMoving = false;
5225
5226 // Step 2: Calculate the centroids for each step.
5227 updateCentroids(centroids, nodes, U, weight, opts);
5228
5229 // Step 3: Update the partition matrix U.
5230 updateMembership(U, _U, centroids, nodes, opts);
5231
5232 // Step 4: Check for convergence.
5233 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
5234 isStillMoving = true;
5235 }
5236 iterations++;
5237 }
5238
5239 // Assign nodes to clusters with highest probability.
5240 clusters = assign$1(nodes, U, opts, cy);
5241 return {
5242 clusters: clusters,
5243 degreeOfMembership: U
5244 };
5245};
5246var kClustering = {
5247 kMeans: kMeans,
5248 kMedoids: kMedoids,
5249 fuzzyCMeans: fuzzyCMeans,
5250 fcm: fuzzyCMeans
5251};
5252
5253// Implemented by Zoe Xi @zoexi for GSOC 2016
5254var defaults$a = defaults$g({
5255 distance: 'euclidean',
5256 // distance metric to compare nodes
5257 linkage: 'min',
5258 // linkage criterion : how to determine the distance between clusters of nodes
5259 mode: 'threshold',
5260 // mode:'threshold' => clusters must be threshold distance apart
5261 threshold: Infinity,
5262 // the distance threshold
5263 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
5264 addDendrogram: false,
5265 // whether to add the dendrogram to the graph for viz
5266 dendrogramDepth: 0,
5267 // depth at which dendrogram branches are merged into the returned clusters
5268 attributes: [] // array of attr functions
5269});
5270
5271var linkageAliases = {
5272 'single': 'min',
5273 'complete': 'max'
5274};
5275var setOptions$1 = function setOptions(options) {
5276 var opts = defaults$a(options);
5277 var preferredAlias = linkageAliases[opts.linkage];
5278 if (preferredAlias != null) {
5279 opts.linkage = preferredAlias;
5280 }
5281 return opts;
5282};
5283var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
5284 // Find two closest clusters from cached mins
5285 var minKey = 0;
5286 var min = Infinity;
5287 var dist;
5288 var attrs = opts.attributes;
5289 var getDist = function getDist(n1, n2) {
5290 return clusteringDistance(opts.distance, attrs.length, function (i) {
5291 return attrs[i](n1);
5292 }, function (i) {
5293 return attrs[i](n2);
5294 }, n1, n2);
5295 };
5296 for (var i = 0; i < clusters.length; i++) {
5297 var key = clusters[i].key;
5298 var _dist = dists[key][mins[key]];
5299 if (_dist < min) {
5300 minKey = key;
5301 min = _dist;
5302 }
5303 }
5304 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
5305 return false;
5306 }
5307 var c1 = index[minKey];
5308 var c2 = index[mins[minKey]];
5309 var merged;
5310
5311 // Merge two closest clusters
5312 if (opts.mode === 'dendrogram') {
5313 merged = {
5314 left: c1,
5315 right: c2,
5316 key: c1.key
5317 };
5318 } else {
5319 merged = {
5320 value: c1.value.concat(c2.value),
5321 key: c1.key
5322 };
5323 }
5324 clusters[c1.index] = merged;
5325 clusters.splice(c2.index, 1);
5326 index[c1.key] = merged;
5327
5328 // Update distances with new merged cluster
5329 for (var _i = 0; _i < clusters.length; _i++) {
5330 var cur = clusters[_i];
5331 if (c1.key === cur.key) {
5332 dist = Infinity;
5333 } else if (opts.linkage === 'min') {
5334 dist = dists[c1.key][cur.key];
5335 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
5336 dist = dists[c2.key][cur.key];
5337 }
5338 } else if (opts.linkage === 'max') {
5339 dist = dists[c1.key][cur.key];
5340 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
5341 dist = dists[c2.key][cur.key];
5342 }
5343 } else if (opts.linkage === 'mean') {
5344 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
5345 } else {
5346 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
5347 }
5348 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
5349 }
5350
5351 // Update cached mins
5352 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
5353 var key1 = clusters[_i2].key;
5354 if (mins[key1] === c1.key || mins[key1] === c2.key) {
5355 var _min = key1;
5356 for (var j = 0; j < clusters.length; j++) {
5357 var key2 = clusters[j].key;
5358 if (dists[key1][key2] < dists[key1][_min]) {
5359 _min = key2;
5360 }
5361 }
5362 mins[key1] = _min;
5363 }
5364 clusters[_i2].index = _i2;
5365 }
5366
5367 // Clean up meta data used for clustering
5368 c1.key = c2.key = c1.index = c2.index = null;
5369 return true;
5370};
5371var getAllChildren = function getAllChildren(root, arr, cy) {
5372 if (!root) return;
5373 if (root.value) {
5374 arr.push(root.value);
5375 } else {
5376 if (root.left) getAllChildren(root.left, arr);
5377 if (root.right) getAllChildren(root.right, arr);
5378 }
5379};
5380var buildDendrogram = function buildDendrogram(root, cy) {
5381 if (!root) return '';
5382 if (root.left && root.right) {
5383 var leftStr = buildDendrogram(root.left, cy);
5384 var rightStr = buildDendrogram(root.right, cy);
5385 var node = cy.add({
5386 group: 'nodes',
5387 data: {
5388 id: leftStr + ',' + rightStr
5389 }
5390 });
5391 cy.add({
5392 group: 'edges',
5393 data: {
5394 source: leftStr,
5395 target: node.id()
5396 }
5397 });
5398 cy.add({
5399 group: 'edges',
5400 data: {
5401 source: rightStr,
5402 target: node.id()
5403 }
5404 });
5405 return node.id();
5406 } else if (root.value) {
5407 return root.value.id();
5408 }
5409};
5410var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5411 if (!root) return [];
5412 var left = [],
5413 right = [],
5414 leaves = [];
5415 if (k === 0) {
5416 // don't cut tree, simply return all nodes as 1 single cluster
5417 if (root.left) getAllChildren(root.left, left);
5418 if (root.right) getAllChildren(root.right, right);
5419 leaves = left.concat(right);
5420 return [cy.collection(leaves)];
5421 } else if (k === 1) {
5422 // cut at root
5423
5424 if (root.value) {
5425 // leaf node
5426 return [cy.collection(root.value)];
5427 } else {
5428 if (root.left) getAllChildren(root.left, left);
5429 if (root.right) getAllChildren(root.right, right);
5430 return [cy.collection(left), cy.collection(right)];
5431 }
5432 } else {
5433 if (root.value) {
5434 return [cy.collection(root.value)];
5435 } else {
5436 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5437 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5438 return left.concat(right);
5439 }
5440 }
5441};
5442
5443var hierarchicalClustering = function hierarchicalClustering(options) {
5444 var cy = this.cy();
5445 var nodes = this.nodes();
5446
5447 // Set parameters of algorithm: linkage type, distance metric, etc.
5448 var opts = setOptions$1(options);
5449 var attrs = opts.attributes;
5450 var getDist = function getDist(n1, n2) {
5451 return clusteringDistance(opts.distance, attrs.length, function (i) {
5452 return attrs[i](n1);
5453 }, function (i) {
5454 return attrs[i](n2);
5455 }, n1, n2);
5456 };
5457
5458 // Begin hierarchical algorithm
5459 var clusters = [];
5460 var dists = []; // distances between each pair of clusters
5461 var mins = []; // closest cluster for each cluster
5462 var index = []; // hash of all clusters by key
5463
5464 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5465 for (var n = 0; n < nodes.length; n++) {
5466 var cluster = {
5467 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5468 key: n,
5469 index: n
5470 };
5471 clusters[n] = cluster;
5472 index[n] = cluster;
5473 dists[n] = [];
5474 mins[n] = 0;
5475 }
5476
5477 // Calculate the distance between each pair of clusters
5478 for (var i = 0; i < clusters.length; i++) {
5479 for (var j = 0; j <= i; j++) {
5480 var dist = void 0;
5481 if (opts.mode === 'dendrogram') {
5482 // modes store cluster values differently
5483 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5484 } else {
5485 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5486 }
5487 dists[i][j] = dist;
5488 dists[j][i] = dist;
5489 if (dist < dists[i][mins[i]]) {
5490 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5491 }
5492 }
5493 }
5494
5495 // Find the closest pair of clusters and merge them into a single cluster.
5496 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5497 var merged = mergeClosest(clusters, index, dists, mins, opts);
5498 while (merged) {
5499 merged = mergeClosest(clusters, index, dists, mins, opts);
5500 }
5501 var retClusters;
5502
5503 // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5504 // in addition to returning the clusters.
5505 if (opts.mode === 'dendrogram') {
5506 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5507 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5508 } else {
5509 // Regular mode simply returns the clusters
5510
5511 retClusters = new Array(clusters.length);
5512 clusters.forEach(function (cluster, i) {
5513 // Clean up meta data used for clustering
5514 cluster.key = cluster.index = null;
5515 retClusters[i] = cy.collection(cluster.value);
5516 });
5517 }
5518 return retClusters;
5519};
5520var hierarchicalClustering$1 = {
5521 hierarchicalClustering: hierarchicalClustering,
5522 hca: hierarchicalClustering
5523};
5524
5525// Implemented by Zoe Xi @zoexi for GSOC 2016
5526var defaults$9 = defaults$g({
5527 distance: 'euclidean',
5528 // distance metric to compare attributes between two nodes
5529 preference: 'median',
5530 // suitability of a data point to serve as an exemplar
5531 damping: 0.8,
5532 // damping factor between [0.5, 1)
5533 maxIterations: 1000,
5534 // max number of iterations to run
5535 minIterations: 100,
5536 // min number of iterations to run in order for clustering to stop
5537 attributes: [// functions to quantify the similarity between any two points
5538 // e.g. node => node.data('weight')
5539 ]
5540});
5541var setOptions = function setOptions(options) {
5542 var dmp = options.damping;
5543 var pref = options.preference;
5544 if (!(0.5 <= dmp && dmp < 1)) {
5545 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5546 }
5547 var validPrefs = ['median', 'mean', 'min', 'max'];
5548 if (!(validPrefs.some(function (v) {
5549 return v === pref;
5550 }) || number$1(pref))) {
5551 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5552 return "'".concat(p, "'");
5553 }).join(', '), "] or a number. Got: ").concat(pref));
5554 }
5555 return defaults$9(options);
5556};
5557
5558var getSimilarity = function getSimilarity(type, n1, n2, attributes) {
5559 var attr = function attr(n, i) {
5560 return attributes[i](n);
5561 };
5562
5563 // nb negative because similarity should have an inverse relationship to distance
5564 return -clusteringDistance(type, attributes.length, function (i) {
5565 return attr(n1, i);
5566 }, function (i) {
5567 return attr(n2, i);
5568 }, n1, n2);
5569};
5570var getPreference = function getPreference(S, preference) {
5571 // larger preference = greater # of clusters
5572 var p = null;
5573 if (preference === 'median') {
5574 p = median(S);
5575 } else if (preference === 'mean') {
5576 p = mean(S);
5577 } else if (preference === 'min') {
5578 p = min(S);
5579 } else if (preference === 'max') {
5580 p = max(S);
5581 } else {
5582 // Custom preference number, as set by user
5583 p = preference;
5584 }
5585 return p;
5586};
5587var findExemplars = function findExemplars(n, R, A) {
5588 var indices = [];
5589 for (var i = 0; i < n; i++) {
5590 if (R[i * n + i] + A[i * n + i] > 0) {
5591 indices.push(i);
5592 }
5593 }
5594 return indices;
5595};
5596var assignClusters = function assignClusters(n, S, exemplars) {
5597 var clusters = [];
5598 for (var i = 0; i < n; i++) {
5599 var index = -1;
5600 var max = -Infinity;
5601 for (var ei = 0; ei < exemplars.length; ei++) {
5602 var e = exemplars[ei];
5603 if (S[i * n + e] > max) {
5604 index = e;
5605 max = S[i * n + e];
5606 }
5607 }
5608 if (index > 0) {
5609 clusters.push(index);
5610 }
5611 }
5612 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5613 clusters[exemplars[_ei]] = exemplars[_ei];
5614 }
5615 return clusters;
5616};
5617var assign = function assign(n, S, exemplars) {
5618 var clusters = assignClusters(n, S, exemplars);
5619 for (var ei = 0; ei < exemplars.length; ei++) {
5620 var ii = [];
5621 for (var c = 0; c < clusters.length; c++) {
5622 if (clusters[c] === exemplars[ei]) {
5623 ii.push(c);
5624 }
5625 }
5626 var maxI = -1;
5627 var maxSum = -Infinity;
5628 for (var i = 0; i < ii.length; i++) {
5629 var sum = 0;
5630 for (var j = 0; j < ii.length; j++) {
5631 sum += S[ii[j] * n + ii[i]];
5632 }
5633 if (sum > maxSum) {
5634 maxI = i;
5635 maxSum = sum;
5636 }
5637 }
5638 exemplars[ei] = ii[maxI];
5639 }
5640 clusters = assignClusters(n, S, exemplars);
5641 return clusters;
5642};
5643var affinityPropagation = function affinityPropagation(options) {
5644 var cy = this.cy();
5645 var nodes = this.nodes();
5646 var opts = setOptions(options);
5647
5648 // Map each node to its position in node array
5649 var id2position = {};
5650 for (var i = 0; i < nodes.length; i++) {
5651 id2position[nodes[i].id()] = i;
5652 }
5653
5654 // Begin affinity propagation algorithm
5655
5656 var n; // number of data points
5657 var n2; // size of matrices
5658 var S; // similarity matrix (1D array)
5659 var p; // preference/suitability of a data point to serve as an exemplar
5660 var R; // responsibility matrix (1D array)
5661 var A; // availability matrix (1D array)
5662
5663 n = nodes.length;
5664 n2 = n * n;
5665
5666 // Initialize and build S similarity matrix
5667 S = new Array(n2);
5668 for (var _i = 0; _i < n2; _i++) {
5669 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5670 }
5671
5672 for (var _i2 = 0; _i2 < n; _i2++) {
5673 for (var j = 0; j < n; j++) {
5674 if (_i2 !== j) {
5675 S[_i2 * n + j] = getSimilarity(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5676 }
5677 }
5678 }
5679
5680 // Place preferences on the diagonal of S
5681 p = getPreference(S, opts.preference);
5682 for (var _i3 = 0; _i3 < n; _i3++) {
5683 S[_i3 * n + _i3] = p;
5684 }
5685
5686 // Initialize R responsibility matrix
5687 R = new Array(n2);
5688 for (var _i4 = 0; _i4 < n2; _i4++) {
5689 R[_i4] = 0.0;
5690 }
5691
5692 // Initialize A availability matrix
5693 A = new Array(n2);
5694 for (var _i5 = 0; _i5 < n2; _i5++) {
5695 A[_i5] = 0.0;
5696 }
5697 var old = new Array(n);
5698 var Rp = new Array(n);
5699 var se = new Array(n);
5700 for (var _i6 = 0; _i6 < n; _i6++) {
5701 old[_i6] = 0.0;
5702 Rp[_i6] = 0.0;
5703 se[_i6] = 0;
5704 }
5705 var e = new Array(n * opts.minIterations);
5706 for (var _i7 = 0; _i7 < e.length; _i7++) {
5707 e[_i7] = 0;
5708 }
5709 var iter;
5710 for (iter = 0; iter < opts.maxIterations; iter++) {
5711 // main algorithmic loop
5712
5713 // Update R responsibility matrix
5714 for (var _i8 = 0; _i8 < n; _i8++) {
5715 var max = -Infinity,
5716 max2 = -Infinity,
5717 maxI = -1,
5718 AS = 0.0;
5719 for (var _j = 0; _j < n; _j++) {
5720 old[_j] = R[_i8 * n + _j];
5721 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5722 if (AS >= max) {
5723 max2 = max;
5724 max = AS;
5725 maxI = _j;
5726 } else if (AS > max2) {
5727 max2 = AS;
5728 }
5729 }
5730 for (var _j2 = 0; _j2 < n; _j2++) {
5731 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5732 }
5733 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5734 }
5735
5736 // Update A availability matrix
5737 for (var _i9 = 0; _i9 < n; _i9++) {
5738 var sum = 0;
5739 for (var _j3 = 0; _j3 < n; _j3++) {
5740 old[_j3] = A[_j3 * n + _i9];
5741 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5742 sum += Rp[_j3];
5743 }
5744 sum -= Rp[_i9];
5745 Rp[_i9] = R[_i9 * n + _i9];
5746 sum += Rp[_i9];
5747 for (var _j4 = 0; _j4 < n; _j4++) {
5748 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5749 }
5750 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5751 }
5752
5753 // Check for convergence
5754 var K = 0;
5755 for (var _i10 = 0; _i10 < n; _i10++) {
5756 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5757 e[iter % opts.minIterations * n + _i10] = E;
5758 K += E;
5759 }
5760 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5761 var _sum = 0;
5762 for (var _i11 = 0; _i11 < n; _i11++) {
5763 se[_i11] = 0;
5764 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5765 se[_i11] += e[_j5 * n + _i11];
5766 }
5767 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5768 _sum++;
5769 }
5770 }
5771 if (_sum === n) {
5772 // then we have convergence
5773 break;
5774 }
5775 }
5776 }
5777
5778 // Identify exemplars (cluster centers)
5779 var exemplarsIndices = findExemplars(n, R, A);
5780
5781 // Assign nodes to clusters
5782 var clusterIndices = assign(n, S, exemplarsIndices);
5783 var clusters = {};
5784 for (var c = 0; c < exemplarsIndices.length; c++) {
5785 clusters[exemplarsIndices[c]] = [];
5786 }
5787 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5788 var pos = id2position[nodes[_i12].id()];
5789 var clusterIndex = clusterIndices[pos];
5790 if (clusterIndex != null) {
5791 // the node may have not been assigned a cluster if no valid attributes were specified
5792 clusters[clusterIndex].push(nodes[_i12]);
5793 }
5794 }
5795 var retClusters = new Array(exemplarsIndices.length);
5796 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5797 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5798 }
5799 return retClusters;
5800};
5801var affinityPropagation$1 = {
5802 affinityPropagation: affinityPropagation,
5803 ap: affinityPropagation
5804};
5805
5806var hierholzerDefaults = defaults$g({
5807 root: undefined,
5808 directed: false
5809});
5810var elesfn$k = {
5811 hierholzer: function hierholzer(options) {
5812 if (!plainObject(options)) {
5813 var args = arguments;
5814 options = {
5815 root: args[0],
5816 directed: args[1]
5817 };
5818 }
5819 var _hierholzerDefaults = hierholzerDefaults(options),
5820 root = _hierholzerDefaults.root,
5821 directed = _hierholzerDefaults.directed;
5822 var eles = this;
5823 var dflag = false;
5824 var oddIn;
5825 var oddOut;
5826 var startVertex;
5827 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5828 var nodes = {};
5829 var edges = {};
5830 if (directed) {
5831 eles.forEach(function (ele) {
5832 var id = ele.id();
5833 if (ele.isNode()) {
5834 var ind = ele.indegree(true);
5835 var outd = ele.outdegree(true);
5836 var d1 = ind - outd;
5837 var d2 = outd - ind;
5838 if (d1 == 1) {
5839 if (oddIn) dflag = true;else oddIn = id;
5840 } else if (d2 == 1) {
5841 if (oddOut) dflag = true;else oddOut = id;
5842 } else if (d2 > 1 || d1 > 1) {
5843 dflag = true;
5844 }
5845 nodes[id] = [];
5846 ele.outgoers().forEach(function (e) {
5847 if (e.isEdge()) nodes[id].push(e.id());
5848 });
5849 } else {
5850 edges[id] = [undefined, ele.target().id()];
5851 }
5852 });
5853 } else {
5854 eles.forEach(function (ele) {
5855 var id = ele.id();
5856 if (ele.isNode()) {
5857 var d = ele.degree(true);
5858 if (d % 2) {
5859 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5860 }
5861 nodes[id] = [];
5862 ele.connectedEdges().forEach(function (e) {
5863 return nodes[id].push(e.id());
5864 });
5865 } else {
5866 edges[id] = [ele.source().id(), ele.target().id()];
5867 }
5868 });
5869 }
5870 var result = {
5871 found: false,
5872 trail: undefined
5873 };
5874 if (dflag) return result;else if (oddOut && oddIn) {
5875 if (directed) {
5876 if (startVertex && oddOut != startVertex) {
5877 return result;
5878 }
5879 startVertex = oddOut;
5880 } else {
5881 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5882 return result;
5883 } else if (!startVertex) {
5884 startVertex = oddOut;
5885 }
5886 }
5887 } else {
5888 if (!startVertex) startVertex = eles[0].id();
5889 }
5890 var walk = function walk(v) {
5891 var currentNode = v;
5892 var subtour = [v];
5893 var adj, adjTail, adjHead;
5894 while (nodes[currentNode].length) {
5895 adj = nodes[currentNode].shift();
5896 adjTail = edges[adj][0];
5897 adjHead = edges[adj][1];
5898 if (currentNode != adjHead) {
5899 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5900 return e != adj;
5901 });
5902 currentNode = adjHead;
5903 } else if (!directed && currentNode != adjTail) {
5904 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5905 return e != adj;
5906 });
5907 currentNode = adjTail;
5908 }
5909 subtour.unshift(adj);
5910 subtour.unshift(currentNode);
5911 }
5912 return subtour;
5913 };
5914 var trail = [];
5915 var subtour = [];
5916 subtour = walk(startVertex);
5917 while (subtour.length != 1) {
5918 if (nodes[subtour[0]].length == 0) {
5919 trail.unshift(eles.getElementById(subtour.shift()));
5920 trail.unshift(eles.getElementById(subtour.shift()));
5921 } else {
5922 subtour = walk(subtour.shift()).concat(subtour);
5923 }
5924 }
5925 trail.unshift(eles.getElementById(subtour.shift())); // final node
5926
5927 for (var d in nodes) {
5928 if (nodes[d].length) {
5929 return result;
5930 }
5931 }
5932 result.found = true;
5933 result.trail = this.spawn(trail, true);
5934 return result;
5935 }
5936};
5937
5938var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5939 var eles = this;
5940 var nodes = {};
5941 var id = 0;
5942 var edgeCount = 0;
5943 var components = [];
5944 var stack = [];
5945 var visitedEdges = {};
5946 var buildComponent = function buildComponent(x, y) {
5947 var i = stack.length - 1;
5948 var cutset = [];
5949 var component = eles.spawn();
5950 while (stack[i].x != x || stack[i].y != y) {
5951 cutset.push(stack.pop().edge);
5952 i--;
5953 }
5954 cutset.push(stack.pop().edge);
5955 cutset.forEach(function (edge) {
5956 var connectedNodes = edge.connectedNodes().intersection(eles);
5957 component.merge(edge);
5958 connectedNodes.forEach(function (node) {
5959 var nodeId = node.id();
5960 var connectedEdges = node.connectedEdges().intersection(eles);
5961 component.merge(node);
5962 if (!nodes[nodeId].cutVertex) {
5963 component.merge(connectedEdges);
5964 } else {
5965 component.merge(connectedEdges.filter(function (edge) {
5966 return edge.isLoop();
5967 }));
5968 }
5969 });
5970 });
5971 components.push(component);
5972 };
5973 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5974 if (root === parent) edgeCount += 1;
5975 nodes[currentNode] = {
5976 id: id,
5977 low: id++,
5978 cutVertex: false
5979 };
5980 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5981 if (edges.size() === 0) {
5982 components.push(eles.spawn(eles.getElementById(currentNode)));
5983 } else {
5984 var sourceId, targetId, otherNodeId, edgeId;
5985 edges.forEach(function (edge) {
5986 sourceId = edge.source().id();
5987 targetId = edge.target().id();
5988 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5989 if (otherNodeId !== parent) {
5990 edgeId = edge.id();
5991 if (!visitedEdges[edgeId]) {
5992 visitedEdges[edgeId] = true;
5993 stack.push({
5994 x: currentNode,
5995 y: otherNodeId,
5996 edge: edge
5997 });
5998 }
5999 if (!(otherNodeId in nodes)) {
6000 biconnectedSearch(root, otherNodeId, currentNode);
6001 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
6002 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
6003 nodes[currentNode].cutVertex = true;
6004 buildComponent(currentNode, otherNodeId);
6005 }
6006 } else {
6007 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
6008 }
6009 }
6010 });
6011 }
6012 };
6013 eles.forEach(function (ele) {
6014 if (ele.isNode()) {
6015 var nodeId = ele.id();
6016 if (!(nodeId in nodes)) {
6017 edgeCount = 0;
6018 biconnectedSearch(nodeId, nodeId);
6019 nodes[nodeId].cutVertex = edgeCount > 1;
6020 }
6021 }
6022 });
6023 var cutVertices = Object.keys(nodes).filter(function (id) {
6024 return nodes[id].cutVertex;
6025 }).map(function (id) {
6026 return eles.getElementById(id);
6027 });
6028 return {
6029 cut: eles.spawn(cutVertices),
6030 components: components
6031 };
6032};
6033var hopcroftTarjanBiconnected$1 = {
6034 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
6035 htbc: hopcroftTarjanBiconnected,
6036 htb: hopcroftTarjanBiconnected,
6037 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
6038};
6039
6040var tarjanStronglyConnected = function tarjanStronglyConnected() {
6041 var eles = this;
6042 var nodes = {};
6043 var index = 0;
6044 var components = [];
6045 var stack = [];
6046 var cut = eles.spawn(eles);
6047 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
6048 stack.push(sourceNodeId);
6049 nodes[sourceNodeId] = {
6050 index: index,
6051 low: index++,
6052 explored: false
6053 };
6054 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
6055 connectedEdges.forEach(function (edge) {
6056 var targetNodeId = edge.target().id();
6057 if (targetNodeId !== sourceNodeId) {
6058 if (!(targetNodeId in nodes)) {
6059 stronglyConnectedSearch(targetNodeId);
6060 }
6061 if (!nodes[targetNodeId].explored) {
6062 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
6063 }
6064 }
6065 });
6066 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
6067 var componentNodes = eles.spawn();
6068 for (;;) {
6069 var nodeId = stack.pop();
6070 componentNodes.merge(eles.getElementById(nodeId));
6071 nodes[nodeId].low = nodes[sourceNodeId].index;
6072 nodes[nodeId].explored = true;
6073 if (nodeId === sourceNodeId) {
6074 break;
6075 }
6076 }
6077 var componentEdges = componentNodes.edgesWith(componentNodes);
6078 var component = componentNodes.merge(componentEdges);
6079 components.push(component);
6080 cut = cut.difference(component);
6081 }
6082 };
6083 eles.forEach(function (ele) {
6084 if (ele.isNode()) {
6085 var nodeId = ele.id();
6086 if (!(nodeId in nodes)) {
6087 stronglyConnectedSearch(nodeId);
6088 }
6089 }
6090 });
6091 return {
6092 cut: cut,
6093 components: components
6094 };
6095};
6096var tarjanStronglyConnected$1 = {
6097 tarjanStronglyConnected: tarjanStronglyConnected,
6098 tsc: tarjanStronglyConnected,
6099 tscc: tarjanStronglyConnected,
6100 tarjanStronglyConnectedComponents: tarjanStronglyConnected
6101};
6102
6103var elesfn$j = {};
6104[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) {
6105 extend(elesfn$j, props);
6106});
6107
6108/*!
6109Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
6110Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
6111Licensed under The MIT License (http://opensource.org/licenses/MIT)
6112*/
6113
6114/* promise states [Promises/A+ 2.1] */
6115var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
6116var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
6117var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
6118
6119/* promise object constructor */
6120var api = function api(executor) {
6121 /* optionally support non-constructor/plain-function call */
6122 if (!(this instanceof api)) return new api(executor);
6123
6124 /* initialize object */
6125 this.id = 'Thenable/1.0.7';
6126 this.state = STATE_PENDING; /* initial state */
6127 this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
6128 this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
6129 this.onFulfilled = []; /* initial handlers */
6130 this.onRejected = []; /* initial handlers */
6131
6132 /* provide optional information-hiding proxy */
6133 this.proxy = {
6134 then: this.then.bind(this)
6135 };
6136
6137 /* support optional executor function */
6138 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
6139};
6140
6141/* promise API methods */
6142api.prototype = {
6143 /* promise resolving methods */
6144 fulfill: function fulfill(value) {
6145 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
6146 },
6147 reject: function reject(value) {
6148 return deliver(this, STATE_REJECTED, 'rejectReason', value);
6149 },
6150 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
6151 then: function then(onFulfilled, onRejected) {
6152 var curr = this;
6153 var next = new api(); /* [Promises/A+ 2.2.7] */
6154 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill')); /* [Promises/A+ 2.2.2/2.2.6] */
6155 curr.onRejected.push(resolver(onRejected, next, 'reject')); /* [Promises/A+ 2.2.3/2.2.6] */
6156 execute(curr);
6157 return next.proxy; /* [Promises/A+ 2.2.7, 3.3] */
6158 }
6159};
6160
6161/* deliver an action */
6162var deliver = function deliver(curr, state, name, value) {
6163 if (curr.state === STATE_PENDING) {
6164 curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
6165 curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
6166 execute(curr);
6167 }
6168 return curr;
6169};
6170
6171/* execute all handlers */
6172var execute = function execute(curr) {
6173 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
6174};
6175
6176/* execute particular set of handlers */
6177var execute_handlers = function execute_handlers(curr, name, value) {
6178 /* global setImmediate: true */
6179 /* global setTimeout: true */
6180
6181 /* short-circuit processing */
6182 if (curr[name].length === 0) return;
6183
6184 /* iterate over all handlers, exactly once */
6185 var handlers = curr[name];
6186 curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
6187 var func = function func() {
6188 for (var i = 0; i < handlers.length; i++) {
6189 handlers[i](value);
6190 } /* [Promises/A+ 2.2.5] */
6191 };
6192
6193 /* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
6194 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
6195};
6196
6197/* generate a resolver function */
6198var resolver = function resolver(cb, next, method) {
6199 return function (value) {
6200 if (typeof cb !== 'function') /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
6201 next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */else {
6202 var result;
6203 try {
6204 result = cb(value);
6205 } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */ catch (e) {
6206 next.reject(e); /* [Promises/A+ 2.2.7.2] */
6207 return;
6208 }
6209 resolve(next, result); /* [Promises/A+ 2.2.7.1] */
6210 }
6211 };
6212};
6213
6214/* "Promise Resolution Procedure" */ /* [Promises/A+ 2.3] */
6215var resolve = function resolve(promise, x) {
6216 /* sanity check arguments */ /* [Promises/A+ 2.3.1] */
6217 if (promise === x || promise.proxy === x) {
6218 promise.reject(new TypeError('cannot resolve promise with itself'));
6219 return;
6220 }
6221
6222 /* surgically check for a "then" method
6223 (mainly to just call the "getter" of "then" only once) */
6224 var then;
6225 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6226 try {
6227 then = x.then;
6228 } /* [Promises/A+ 2.3.3.1, 3.5] */ catch (e) {
6229 promise.reject(e); /* [Promises/A+ 2.3.3.2] */
6230 return;
6231 }
6232 }
6233
6234 /* handle own Thenables [Promises/A+ 2.3.2]
6235 and similar "thenables" [Promises/A+ 2.3.3] */
6236 if (typeof then === 'function') {
6237 var resolved = false;
6238 try {
6239 /* call retrieved "then" method */ /* [Promises/A+ 2.3.3.3] */
6240 then.call(x, /* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
6241 function (y) {
6242 if (resolved) return;
6243 resolved = true; /* [Promises/A+ 2.3.3.3.3] */
6244 if (y === x) /* [Promises/A+ 3.6] */
6245 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6246 }, /* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
6247 function (r) {
6248 if (resolved) return;
6249 resolved = true; /* [Promises/A+ 2.3.3.3.3] */
6250 promise.reject(r);
6251 });
6252 } catch (e) {
6253 if (!resolved) /* [Promises/A+ 2.3.3.3.3] */
6254 promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */
6255 }
6256
6257 return;
6258 }
6259
6260 /* handle other values */
6261 promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */
6262};
6263
6264// so we always have Promise.all()
6265api.all = function (ps) {
6266 return new api(function (resolveAll, rejectAll) {
6267 var vals = new Array(ps.length);
6268 var doneCount = 0;
6269 var fulfill = function fulfill(i, val) {
6270 vals[i] = val;
6271 doneCount++;
6272 if (doneCount === ps.length) {
6273 resolveAll(vals);
6274 }
6275 };
6276 for (var i = 0; i < ps.length; i++) {
6277 (function (i) {
6278 var p = ps[i];
6279 var isPromise = p != null && p.then != null;
6280 if (isPromise) {
6281 p.then(function (val) {
6282 fulfill(i, val);
6283 }, function (err) {
6284 rejectAll(err);
6285 });
6286 } else {
6287 var val = p;
6288 fulfill(i, val);
6289 }
6290 })(i);
6291 }
6292 });
6293};
6294api.resolve = function (val) {
6295 return new api(function (resolve, reject) {
6296 resolve(val);
6297 });
6298};
6299api.reject = function (val) {
6300 return new api(function (resolve, reject) {
6301 reject(val);
6302 });
6303};
6304var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6305
6306var Animation = function Animation(target, opts, opts2) {
6307 var isCore = core(target);
6308 var isEle = !isCore;
6309 var _p = this._private = extend({
6310 duration: 1000
6311 }, opts, opts2);
6312 _p.target = target;
6313 _p.style = _p.style || _p.css;
6314 _p.started = false;
6315 _p.playing = false;
6316 _p.hooked = false;
6317 _p.applying = false;
6318 _p.progress = 0;
6319 _p.completes = [];
6320 _p.frames = [];
6321 if (_p.complete && fn$6(_p.complete)) {
6322 _p.completes.push(_p.complete);
6323 }
6324 if (isEle) {
6325 var pos = target.position();
6326 _p.startPosition = _p.startPosition || {
6327 x: pos.x,
6328 y: pos.y
6329 };
6330 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6331 }
6332 if (isCore) {
6333 var pan = target.pan();
6334 _p.startPan = {
6335 x: pan.x,
6336 y: pan.y
6337 };
6338 _p.startZoom = target.zoom();
6339 }
6340
6341 // for future timeline/animations impl
6342 this.length = 1;
6343 this[0] = this;
6344};
6345var anifn = Animation.prototype;
6346extend(anifn, {
6347 instanceString: function instanceString() {
6348 return 'animation';
6349 },
6350 hook: function hook() {
6351 var _p = this._private;
6352 if (!_p.hooked) {
6353 // add to target's animation queue
6354 var q;
6355 var tAni = _p.target._private.animation;
6356 if (_p.queue) {
6357 q = tAni.queue;
6358 } else {
6359 q = tAni.current;
6360 }
6361 q.push(this);
6362
6363 // add to the animation loop pool
6364 if (elementOrCollection(_p.target)) {
6365 _p.target.cy().addToAnimationPool(_p.target);
6366 }
6367 _p.hooked = true;
6368 }
6369 return this;
6370 },
6371 play: function play() {
6372 var _p = this._private;
6373
6374 // autorewind
6375 if (_p.progress === 1) {
6376 _p.progress = 0;
6377 }
6378 _p.playing = true;
6379 _p.started = false; // needs to be started by animation loop
6380 _p.stopped = false;
6381 this.hook();
6382
6383 // the animation loop will start the animation...
6384
6385 return this;
6386 },
6387 playing: function playing() {
6388 return this._private.playing;
6389 },
6390 apply: function apply() {
6391 var _p = this._private;
6392 _p.applying = true;
6393 _p.started = false; // needs to be started by animation loop
6394 _p.stopped = false;
6395 this.hook();
6396
6397 // the animation loop will apply the animation at this progress
6398
6399 return this;
6400 },
6401 applying: function applying() {
6402 return this._private.applying;
6403 },
6404 pause: function pause() {
6405 var _p = this._private;
6406 _p.playing = false;
6407 _p.started = false;
6408 return this;
6409 },
6410 stop: function stop() {
6411 var _p = this._private;
6412 _p.playing = false;
6413 _p.started = false;
6414 _p.stopped = true; // to be removed from animation queues
6415
6416 return this;
6417 },
6418 rewind: function rewind() {
6419 return this.progress(0);
6420 },
6421 fastforward: function fastforward() {
6422 return this.progress(1);
6423 },
6424 time: function time(t) {
6425 var _p = this._private;
6426 if (t === undefined) {
6427 return _p.progress * _p.duration;
6428 } else {
6429 return this.progress(t / _p.duration);
6430 }
6431 },
6432 progress: function progress(p) {
6433 var _p = this._private;
6434 var wasPlaying = _p.playing;
6435 if (p === undefined) {
6436 return _p.progress;
6437 } else {
6438 if (wasPlaying) {
6439 this.pause();
6440 }
6441 _p.progress = p;
6442 _p.started = false;
6443 if (wasPlaying) {
6444 this.play();
6445 }
6446 }
6447 return this;
6448 },
6449 completed: function completed() {
6450 return this._private.progress === 1;
6451 },
6452 reverse: function reverse() {
6453 var _p = this._private;
6454 var wasPlaying = _p.playing;
6455 if (wasPlaying) {
6456 this.pause();
6457 }
6458 _p.progress = 1 - _p.progress;
6459 _p.started = false;
6460 var swap = function swap(a, b) {
6461 var _pa = _p[a];
6462 if (_pa == null) {
6463 return;
6464 }
6465 _p[a] = _p[b];
6466 _p[b] = _pa;
6467 };
6468 swap('zoom', 'startZoom');
6469 swap('pan', 'startPan');
6470 swap('position', 'startPosition');
6471
6472 // swap styles
6473 if (_p.style) {
6474 for (var i = 0; i < _p.style.length; i++) {
6475 var prop = _p.style[i];
6476 var name = prop.name;
6477 var startStyleProp = _p.startStyle[name];
6478 _p.startStyle[name] = prop;
6479 _p.style[i] = startStyleProp;
6480 }
6481 }
6482 if (wasPlaying) {
6483 this.play();
6484 }
6485 return this;
6486 },
6487 promise: function promise(type) {
6488 var _p = this._private;
6489 var arr;
6490 switch (type) {
6491 case 'frame':
6492 arr = _p.frames;
6493 break;
6494 default:
6495 case 'complete':
6496 case 'completed':
6497 arr = _p.completes;
6498 }
6499 return new Promise$1(function (resolve, reject) {
6500 arr.push(function () {
6501 resolve();
6502 });
6503 });
6504 }
6505});
6506anifn.complete = anifn.completed;
6507anifn.run = anifn.play;
6508anifn.running = anifn.playing;
6509
6510var define$3 = {
6511 animated: function animated() {
6512 return function animatedImpl() {
6513 var self = this;
6514 var selfIsArrayLike = self.length !== undefined;
6515 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6516 var cy = this._private.cy || this;
6517 if (!cy.styleEnabled()) {
6518 return false;
6519 }
6520 var ele = all[0];
6521 if (ele) {
6522 return ele._private.animation.current.length > 0;
6523 }
6524 };
6525 },
6526 // animated
6527
6528 clearQueue: function clearQueue() {
6529 return function clearQueueImpl() {
6530 var self = this;
6531 var selfIsArrayLike = self.length !== undefined;
6532 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6533 var cy = this._private.cy || this;
6534 if (!cy.styleEnabled()) {
6535 return this;
6536 }
6537 for (var i = 0; i < all.length; i++) {
6538 var ele = all[i];
6539 ele._private.animation.queue = [];
6540 }
6541 return this;
6542 };
6543 },
6544 // clearQueue
6545
6546 delay: function delay() {
6547 return function delayImpl(time, complete) {
6548 var cy = this._private.cy || this;
6549 if (!cy.styleEnabled()) {
6550 return this;
6551 }
6552 return this.animate({
6553 delay: time,
6554 duration: time,
6555 complete: complete
6556 });
6557 };
6558 },
6559 // delay
6560
6561 delayAnimation: function delayAnimation() {
6562 return function delayAnimationImpl(time, complete) {
6563 var cy = this._private.cy || this;
6564 if (!cy.styleEnabled()) {
6565 return this;
6566 }
6567 return this.animation({
6568 delay: time,
6569 duration: time,
6570 complete: complete
6571 });
6572 };
6573 },
6574 // delay
6575
6576 animation: function animation() {
6577 return function animationImpl(properties, params) {
6578 var self = this;
6579 var selfIsArrayLike = self.length !== undefined;
6580 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6581 var cy = this._private.cy || this;
6582 var isCore = !selfIsArrayLike;
6583 var isEles = !isCore;
6584 if (!cy.styleEnabled()) {
6585 return this;
6586 }
6587 var style = cy.style();
6588 properties = extend({}, properties, params);
6589 var propertiesEmpty = Object.keys(properties).length === 0;
6590 if (propertiesEmpty) {
6591 return new Animation(all[0], properties); // nothing to animate
6592 }
6593
6594 if (properties.duration === undefined) {
6595 properties.duration = 400;
6596 }
6597 switch (properties.duration) {
6598 case 'slow':
6599 properties.duration = 600;
6600 break;
6601 case 'fast':
6602 properties.duration = 200;
6603 break;
6604 }
6605 if (isEles) {
6606 properties.style = style.getPropsList(properties.style || properties.css);
6607 properties.css = undefined;
6608 }
6609 if (isEles && properties.renderedPosition != null) {
6610 var rpos = properties.renderedPosition;
6611 var pan = cy.pan();
6612 var zoom = cy.zoom();
6613 properties.position = renderedToModelPosition(rpos, zoom, pan);
6614 }
6615
6616 // override pan w/ panBy if set
6617 if (isCore && properties.panBy != null) {
6618 var panBy = properties.panBy;
6619 var cyPan = cy.pan();
6620 properties.pan = {
6621 x: cyPan.x + panBy.x,
6622 y: cyPan.y + panBy.y
6623 };
6624 }
6625
6626 // override pan w/ center if set
6627 var center = properties.center || properties.centre;
6628 if (isCore && center != null) {
6629 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6630 if (centerPan != null) {
6631 properties.pan = centerPan;
6632 }
6633 }
6634
6635 // override pan & zoom w/ fit if set
6636 if (isCore && properties.fit != null) {
6637 var fit = properties.fit;
6638 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6639 if (fitVp != null) {
6640 properties.pan = fitVp.pan;
6641 properties.zoom = fitVp.zoom;
6642 }
6643 }
6644
6645 // override zoom (& potentially pan) w/ zoom obj if set
6646 if (isCore && plainObject(properties.zoom)) {
6647 var vp = cy.getZoomedViewport(properties.zoom);
6648 if (vp != null) {
6649 if (vp.zoomed) {
6650 properties.zoom = vp.zoom;
6651 }
6652 if (vp.panned) {
6653 properties.pan = vp.pan;
6654 }
6655 } else {
6656 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6657 }
6658 }
6659
6660 return new Animation(all[0], properties);
6661 };
6662 },
6663 // animate
6664
6665 animate: function animate() {
6666 return function animateImpl(properties, params) {
6667 var self = this;
6668 var selfIsArrayLike = self.length !== undefined;
6669 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6670 var cy = this._private.cy || this;
6671 if (!cy.styleEnabled()) {
6672 return this;
6673 }
6674 if (params) {
6675 properties = extend({}, properties, params);
6676 }
6677
6678 // manually hook and run the animation
6679 for (var i = 0; i < all.length; i++) {
6680 var ele = all[i];
6681 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6682 var ani = ele.animation(properties, queue ? {
6683 queue: true
6684 } : undefined);
6685 ani.play();
6686 }
6687 return this; // chaining
6688 };
6689 },
6690
6691 // animate
6692
6693 stop: function stop() {
6694 return function stopImpl(clearQueue, jumpToEnd) {
6695 var self = this;
6696 var selfIsArrayLike = self.length !== undefined;
6697 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6698 var cy = this._private.cy || this;
6699 if (!cy.styleEnabled()) {
6700 return this;
6701 }
6702 for (var i = 0; i < all.length; i++) {
6703 var ele = all[i];
6704 var _p = ele._private;
6705 var anis = _p.animation.current;
6706 for (var j = 0; j < anis.length; j++) {
6707 var ani = anis[j];
6708 var ani_p = ani._private;
6709 if (jumpToEnd) {
6710 // next iteration of the animation loop, the animation
6711 // will go straight to the end and be removed
6712 ani_p.duration = 0;
6713 }
6714 }
6715
6716 // clear the queue of future animations
6717 if (clearQueue) {
6718 _p.animation.queue = [];
6719 }
6720 if (!jumpToEnd) {
6721 _p.animation.current = [];
6722 }
6723 }
6724
6725 // we have to notify (the animation loop doesn't do it for us on `stop`)
6726 cy.notify('draw');
6727 return this;
6728 };
6729 } // stop
6730}; // define
6731
6732/**
6733 * Checks if `value` is classified as an `Array` object.
6734 *
6735 * @static
6736 * @memberOf _
6737 * @since 0.1.0
6738 * @category Lang
6739 * @param {*} value The value to check.
6740 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
6741 * @example
6742 *
6743 * _.isArray([1, 2, 3]);
6744 * // => true
6745 *
6746 * _.isArray(document.body.children);
6747 * // => false
6748 *
6749 * _.isArray('abc');
6750 * // => false
6751 *
6752 * _.isArray(_.noop);
6753 * // => false
6754 */
6755var isArray = Array.isArray;
6756
6757var isArray_1 = isArray;
6758
6759/** Used to match property names within property paths. */
6760var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
6761 reIsPlainProp = /^\w*$/;
6762
6763/**
6764 * Checks if `value` is a property name and not a property path.
6765 *
6766 * @private
6767 * @param {*} value The value to check.
6768 * @param {Object} [object] The object to query keys on.
6769 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
6770 */
6771function isKey(value, object) {
6772 if (isArray_1(value)) {
6773 return false;
6774 }
6775 var type = typeof value;
6776 if (type == 'number' || type == 'symbol' || type == 'boolean' ||
6777 value == null || isSymbol_1(value)) {
6778 return true;
6779 }
6780 return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
6781 (object != null && value in Object(object));
6782}
6783
6784var _isKey = isKey;
6785
6786/** `Object#toString` result references. */
6787var asyncTag = '[object AsyncFunction]',
6788 funcTag = '[object Function]',
6789 genTag = '[object GeneratorFunction]',
6790 proxyTag = '[object Proxy]';
6791
6792/**
6793 * Checks if `value` is classified as a `Function` object.
6794 *
6795 * @static
6796 * @memberOf _
6797 * @since 0.1.0
6798 * @category Lang
6799 * @param {*} value The value to check.
6800 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
6801 * @example
6802 *
6803 * _.isFunction(_);
6804 * // => true
6805 *
6806 * _.isFunction(/abc/);
6807 * // => false
6808 */
6809function isFunction(value) {
6810 if (!isObject_1(value)) {
6811 return false;
6812 }
6813 // The use of `Object#toString` avoids issues with the `typeof` operator
6814 // in Safari 9 which returns 'object' for typed arrays and other constructors.
6815 var tag = _baseGetTag(value);
6816 return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
6817}
6818
6819var isFunction_1 = isFunction;
6820
6821/** Used to detect overreaching core-js shims. */
6822var coreJsData = _root['__core-js_shared__'];
6823
6824var _coreJsData = coreJsData;
6825
6826/** Used to detect methods masquerading as native. */
6827var maskSrcKey = (function() {
6828 var uid = /[^.]+$/.exec(_coreJsData && _coreJsData.keys && _coreJsData.keys.IE_PROTO || '');
6829 return uid ? ('Symbol(src)_1.' + uid) : '';
6830}());
6831
6832/**
6833 * Checks if `func` has its source masked.
6834 *
6835 * @private
6836 * @param {Function} func The function to check.
6837 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
6838 */
6839function isMasked(func) {
6840 return !!maskSrcKey && (maskSrcKey in func);
6841}
6842
6843var _isMasked = isMasked;
6844
6845/** Used for built-in method references. */
6846var funcProto$1 = Function.prototype;
6847
6848/** Used to resolve the decompiled source of functions. */
6849var funcToString$1 = funcProto$1.toString;
6850
6851/**
6852 * Converts `func` to its source code.
6853 *
6854 * @private
6855 * @param {Function} func The function to convert.
6856 * @returns {string} Returns the source code.
6857 */
6858function toSource(func) {
6859 if (func != null) {
6860 try {
6861 return funcToString$1.call(func);
6862 } catch (e) {}
6863 try {
6864 return (func + '');
6865 } catch (e) {}
6866 }
6867 return '';
6868}
6869
6870var _toSource = toSource;
6871
6872/**
6873 * Used to match `RegExp`
6874 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
6875 */
6876var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
6877
6878/** Used to detect host constructors (Safari). */
6879var reIsHostCtor = /^\[object .+?Constructor\]$/;
6880
6881/** Used for built-in method references. */
6882var funcProto = Function.prototype,
6883 objectProto$3 = Object.prototype;
6884
6885/** Used to resolve the decompiled source of functions. */
6886var funcToString = funcProto.toString;
6887
6888/** Used to check objects for own properties. */
6889var hasOwnProperty$3 = objectProto$3.hasOwnProperty;
6890
6891/** Used to detect if a method is native. */
6892var reIsNative = RegExp('^' +
6893 funcToString.call(hasOwnProperty$3).replace(reRegExpChar, '\\$&')
6894 .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
6895);
6896
6897/**
6898 * The base implementation of `_.isNative` without bad shim checks.
6899 *
6900 * @private
6901 * @param {*} value The value to check.
6902 * @returns {boolean} Returns `true` if `value` is a native function,
6903 * else `false`.
6904 */
6905function baseIsNative(value) {
6906 if (!isObject_1(value) || _isMasked(value)) {
6907 return false;
6908 }
6909 var pattern = isFunction_1(value) ? reIsNative : reIsHostCtor;
6910 return pattern.test(_toSource(value));
6911}
6912
6913var _baseIsNative = baseIsNative;
6914
6915/**
6916 * Gets the value at `key` of `object`.
6917 *
6918 * @private
6919 * @param {Object} [object] The object to query.
6920 * @param {string} key The key of the property to get.
6921 * @returns {*} Returns the property value.
6922 */
6923function getValue$1(object, key) {
6924 return object == null ? undefined : object[key];
6925}
6926
6927var _getValue = getValue$1;
6928
6929/**
6930 * Gets the native function at `key` of `object`.
6931 *
6932 * @private
6933 * @param {Object} object The object to query.
6934 * @param {string} key The key of the method to get.
6935 * @returns {*} Returns the function if it's native, else `undefined`.
6936 */
6937function getNative(object, key) {
6938 var value = _getValue(object, key);
6939 return _baseIsNative(value) ? value : undefined;
6940}
6941
6942var _getNative = getNative;
6943
6944/* Built-in method references that are verified to be native. */
6945var nativeCreate = _getNative(Object, 'create');
6946
6947var _nativeCreate = nativeCreate;
6948
6949/**
6950 * Removes all key-value entries from the hash.
6951 *
6952 * @private
6953 * @name clear
6954 * @memberOf Hash
6955 */
6956function hashClear() {
6957 this.__data__ = _nativeCreate ? _nativeCreate(null) : {};
6958 this.size = 0;
6959}
6960
6961var _hashClear = hashClear;
6962
6963/**
6964 * Removes `key` and its value from the hash.
6965 *
6966 * @private
6967 * @name delete
6968 * @memberOf Hash
6969 * @param {Object} hash The hash to modify.
6970 * @param {string} key The key of the value to remove.
6971 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
6972 */
6973function hashDelete(key) {
6974 var result = this.has(key) && delete this.__data__[key];
6975 this.size -= result ? 1 : 0;
6976 return result;
6977}
6978
6979var _hashDelete = hashDelete;
6980
6981/** Used to stand-in for `undefined` hash values. */
6982var HASH_UNDEFINED$1 = '__lodash_hash_undefined__';
6983
6984/** Used for built-in method references. */
6985var objectProto$2 = Object.prototype;
6986
6987/** Used to check objects for own properties. */
6988var hasOwnProperty$2 = objectProto$2.hasOwnProperty;
6989
6990/**
6991 * Gets the hash value for `key`.
6992 *
6993 * @private
6994 * @name get
6995 * @memberOf Hash
6996 * @param {string} key The key of the value to get.
6997 * @returns {*} Returns the entry value.
6998 */
6999function hashGet(key) {
7000 var data = this.__data__;
7001 if (_nativeCreate) {
7002 var result = data[key];
7003 return result === HASH_UNDEFINED$1 ? undefined : result;
7004 }
7005 return hasOwnProperty$2.call(data, key) ? data[key] : undefined;
7006}
7007
7008var _hashGet = hashGet;
7009
7010/** Used for built-in method references. */
7011var objectProto$1 = Object.prototype;
7012
7013/** Used to check objects for own properties. */
7014var hasOwnProperty$1 = objectProto$1.hasOwnProperty;
7015
7016/**
7017 * Checks if a hash value for `key` exists.
7018 *
7019 * @private
7020 * @name has
7021 * @memberOf Hash
7022 * @param {string} key The key of the entry to check.
7023 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7024 */
7025function hashHas(key) {
7026 var data = this.__data__;
7027 return _nativeCreate ? (data[key] !== undefined) : hasOwnProperty$1.call(data, key);
7028}
7029
7030var _hashHas = hashHas;
7031
7032/** Used to stand-in for `undefined` hash values. */
7033var HASH_UNDEFINED = '__lodash_hash_undefined__';
7034
7035/**
7036 * Sets the hash `key` to `value`.
7037 *
7038 * @private
7039 * @name set
7040 * @memberOf Hash
7041 * @param {string} key The key of the value to set.
7042 * @param {*} value The value to set.
7043 * @returns {Object} Returns the hash instance.
7044 */
7045function hashSet(key, value) {
7046 var data = this.__data__;
7047 this.size += this.has(key) ? 0 : 1;
7048 data[key] = (_nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
7049 return this;
7050}
7051
7052var _hashSet = hashSet;
7053
7054/**
7055 * Creates a hash object.
7056 *
7057 * @private
7058 * @constructor
7059 * @param {Array} [entries] The key-value pairs to cache.
7060 */
7061function Hash(entries) {
7062 var index = -1,
7063 length = entries == null ? 0 : entries.length;
7064
7065 this.clear();
7066 while (++index < length) {
7067 var entry = entries[index];
7068 this.set(entry[0], entry[1]);
7069 }
7070}
7071
7072// Add methods to `Hash`.
7073Hash.prototype.clear = _hashClear;
7074Hash.prototype['delete'] = _hashDelete;
7075Hash.prototype.get = _hashGet;
7076Hash.prototype.has = _hashHas;
7077Hash.prototype.set = _hashSet;
7078
7079var _Hash = Hash;
7080
7081/**
7082 * Removes all key-value entries from the list cache.
7083 *
7084 * @private
7085 * @name clear
7086 * @memberOf ListCache
7087 */
7088function listCacheClear() {
7089 this.__data__ = [];
7090 this.size = 0;
7091}
7092
7093var _listCacheClear = listCacheClear;
7094
7095/**
7096 * Performs a
7097 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
7098 * comparison between two values to determine if they are equivalent.
7099 *
7100 * @static
7101 * @memberOf _
7102 * @since 4.0.0
7103 * @category Lang
7104 * @param {*} value The value to compare.
7105 * @param {*} other The other value to compare.
7106 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
7107 * @example
7108 *
7109 * var object = { 'a': 1 };
7110 * var other = { 'a': 1 };
7111 *
7112 * _.eq(object, object);
7113 * // => true
7114 *
7115 * _.eq(object, other);
7116 * // => false
7117 *
7118 * _.eq('a', 'a');
7119 * // => true
7120 *
7121 * _.eq('a', Object('a'));
7122 * // => false
7123 *
7124 * _.eq(NaN, NaN);
7125 * // => true
7126 */
7127function eq(value, other) {
7128 return value === other || (value !== value && other !== other);
7129}
7130
7131var eq_1 = eq;
7132
7133/**
7134 * Gets the index at which the `key` is found in `array` of key-value pairs.
7135 *
7136 * @private
7137 * @param {Array} array The array to inspect.
7138 * @param {*} key The key to search for.
7139 * @returns {number} Returns the index of the matched value, else `-1`.
7140 */
7141function assocIndexOf(array, key) {
7142 var length = array.length;
7143 while (length--) {
7144 if (eq_1(array[length][0], key)) {
7145 return length;
7146 }
7147 }
7148 return -1;
7149}
7150
7151var _assocIndexOf = assocIndexOf;
7152
7153/** Used for built-in method references. */
7154var arrayProto = Array.prototype;
7155
7156/** Built-in value references. */
7157var splice = arrayProto.splice;
7158
7159/**
7160 * Removes `key` and its value from the list cache.
7161 *
7162 * @private
7163 * @name delete
7164 * @memberOf ListCache
7165 * @param {string} key The key of the value to remove.
7166 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
7167 */
7168function listCacheDelete(key) {
7169 var data = this.__data__,
7170 index = _assocIndexOf(data, key);
7171
7172 if (index < 0) {
7173 return false;
7174 }
7175 var lastIndex = data.length - 1;
7176 if (index == lastIndex) {
7177 data.pop();
7178 } else {
7179 splice.call(data, index, 1);
7180 }
7181 --this.size;
7182 return true;
7183}
7184
7185var _listCacheDelete = listCacheDelete;
7186
7187/**
7188 * Gets the list cache value for `key`.
7189 *
7190 * @private
7191 * @name get
7192 * @memberOf ListCache
7193 * @param {string} key The key of the value to get.
7194 * @returns {*} Returns the entry value.
7195 */
7196function listCacheGet(key) {
7197 var data = this.__data__,
7198 index = _assocIndexOf(data, key);
7199
7200 return index < 0 ? undefined : data[index][1];
7201}
7202
7203var _listCacheGet = listCacheGet;
7204
7205/**
7206 * Checks if a list cache value for `key` exists.
7207 *
7208 * @private
7209 * @name has
7210 * @memberOf ListCache
7211 * @param {string} key The key of the entry to check.
7212 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7213 */
7214function listCacheHas(key) {
7215 return _assocIndexOf(this.__data__, key) > -1;
7216}
7217
7218var _listCacheHas = listCacheHas;
7219
7220/**
7221 * Sets the list cache `key` to `value`.
7222 *
7223 * @private
7224 * @name set
7225 * @memberOf ListCache
7226 * @param {string} key The key of the value to set.
7227 * @param {*} value The value to set.
7228 * @returns {Object} Returns the list cache instance.
7229 */
7230function listCacheSet(key, value) {
7231 var data = this.__data__,
7232 index = _assocIndexOf(data, key);
7233
7234 if (index < 0) {
7235 ++this.size;
7236 data.push([key, value]);
7237 } else {
7238 data[index][1] = value;
7239 }
7240 return this;
7241}
7242
7243var _listCacheSet = listCacheSet;
7244
7245/**
7246 * Creates an list cache object.
7247 *
7248 * @private
7249 * @constructor
7250 * @param {Array} [entries] The key-value pairs to cache.
7251 */
7252function ListCache(entries) {
7253 var index = -1,
7254 length = entries == null ? 0 : entries.length;
7255
7256 this.clear();
7257 while (++index < length) {
7258 var entry = entries[index];
7259 this.set(entry[0], entry[1]);
7260 }
7261}
7262
7263// Add methods to `ListCache`.
7264ListCache.prototype.clear = _listCacheClear;
7265ListCache.prototype['delete'] = _listCacheDelete;
7266ListCache.prototype.get = _listCacheGet;
7267ListCache.prototype.has = _listCacheHas;
7268ListCache.prototype.set = _listCacheSet;
7269
7270var _ListCache = ListCache;
7271
7272/* Built-in method references that are verified to be native. */
7273var Map$1 = _getNative(_root, 'Map');
7274
7275var _Map = Map$1;
7276
7277/**
7278 * Removes all key-value entries from the map.
7279 *
7280 * @private
7281 * @name clear
7282 * @memberOf MapCache
7283 */
7284function mapCacheClear() {
7285 this.size = 0;
7286 this.__data__ = {
7287 'hash': new _Hash,
7288 'map': new (_Map || _ListCache),
7289 'string': new _Hash
7290 };
7291}
7292
7293var _mapCacheClear = mapCacheClear;
7294
7295/**
7296 * Checks if `value` is suitable for use as unique object key.
7297 *
7298 * @private
7299 * @param {*} value The value to check.
7300 * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
7301 */
7302function isKeyable(value) {
7303 var type = typeof value;
7304 return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
7305 ? (value !== '__proto__')
7306 : (value === null);
7307}
7308
7309var _isKeyable = isKeyable;
7310
7311/**
7312 * Gets the data for `map`.
7313 *
7314 * @private
7315 * @param {Object} map The map to query.
7316 * @param {string} key The reference key.
7317 * @returns {*} Returns the map data.
7318 */
7319function getMapData(map, key) {
7320 var data = map.__data__;
7321 return _isKeyable(key)
7322 ? data[typeof key == 'string' ? 'string' : 'hash']
7323 : data.map;
7324}
7325
7326var _getMapData = getMapData;
7327
7328/**
7329 * Removes `key` and its value from the map.
7330 *
7331 * @private
7332 * @name delete
7333 * @memberOf MapCache
7334 * @param {string} key The key of the value to remove.
7335 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
7336 */
7337function mapCacheDelete(key) {
7338 var result = _getMapData(this, key)['delete'](key);
7339 this.size -= result ? 1 : 0;
7340 return result;
7341}
7342
7343var _mapCacheDelete = mapCacheDelete;
7344
7345/**
7346 * Gets the map value for `key`.
7347 *
7348 * @private
7349 * @name get
7350 * @memberOf MapCache
7351 * @param {string} key The key of the value to get.
7352 * @returns {*} Returns the entry value.
7353 */
7354function mapCacheGet(key) {
7355 return _getMapData(this, key).get(key);
7356}
7357
7358var _mapCacheGet = mapCacheGet;
7359
7360/**
7361 * Checks if a map value for `key` exists.
7362 *
7363 * @private
7364 * @name has
7365 * @memberOf MapCache
7366 * @param {string} key The key of the entry to check.
7367 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7368 */
7369function mapCacheHas(key) {
7370 return _getMapData(this, key).has(key);
7371}
7372
7373var _mapCacheHas = mapCacheHas;
7374
7375/**
7376 * Sets the map `key` to `value`.
7377 *
7378 * @private
7379 * @name set
7380 * @memberOf MapCache
7381 * @param {string} key The key of the value to set.
7382 * @param {*} value The value to set.
7383 * @returns {Object} Returns the map cache instance.
7384 */
7385function mapCacheSet(key, value) {
7386 var data = _getMapData(this, key),
7387 size = data.size;
7388
7389 data.set(key, value);
7390 this.size += data.size == size ? 0 : 1;
7391 return this;
7392}
7393
7394var _mapCacheSet = mapCacheSet;
7395
7396/**
7397 * Creates a map cache object to store key-value pairs.
7398 *
7399 * @private
7400 * @constructor
7401 * @param {Array} [entries] The key-value pairs to cache.
7402 */
7403function MapCache(entries) {
7404 var index = -1,
7405 length = entries == null ? 0 : entries.length;
7406
7407 this.clear();
7408 while (++index < length) {
7409 var entry = entries[index];
7410 this.set(entry[0], entry[1]);
7411 }
7412}
7413
7414// Add methods to `MapCache`.
7415MapCache.prototype.clear = _mapCacheClear;
7416MapCache.prototype['delete'] = _mapCacheDelete;
7417MapCache.prototype.get = _mapCacheGet;
7418MapCache.prototype.has = _mapCacheHas;
7419MapCache.prototype.set = _mapCacheSet;
7420
7421var _MapCache = MapCache;
7422
7423/** Error message constants. */
7424var FUNC_ERROR_TEXT = 'Expected a function';
7425
7426/**
7427 * Creates a function that memoizes the result of `func`. If `resolver` is
7428 * provided, it determines the cache key for storing the result based on the
7429 * arguments provided to the memoized function. By default, the first argument
7430 * provided to the memoized function is used as the map cache key. The `func`
7431 * is invoked with the `this` binding of the memoized function.
7432 *
7433 * **Note:** The cache is exposed as the `cache` property on the memoized
7434 * function. Its creation may be customized by replacing the `_.memoize.Cache`
7435 * constructor with one whose instances implement the
7436 * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
7437 * method interface of `clear`, `delete`, `get`, `has`, and `set`.
7438 *
7439 * @static
7440 * @memberOf _
7441 * @since 0.1.0
7442 * @category Function
7443 * @param {Function} func The function to have its output memoized.
7444 * @param {Function} [resolver] The function to resolve the cache key.
7445 * @returns {Function} Returns the new memoized function.
7446 * @example
7447 *
7448 * var object = { 'a': 1, 'b': 2 };
7449 * var other = { 'c': 3, 'd': 4 };
7450 *
7451 * var values = _.memoize(_.values);
7452 * values(object);
7453 * // => [1, 2]
7454 *
7455 * values(other);
7456 * // => [3, 4]
7457 *
7458 * object.a = 2;
7459 * values(object);
7460 * // => [1, 2]
7461 *
7462 * // Modify the result cache.
7463 * values.cache.set(object, ['a', 'b']);
7464 * values(object);
7465 * // => ['a', 'b']
7466 *
7467 * // Replace `_.memoize.Cache`.
7468 * _.memoize.Cache = WeakMap;
7469 */
7470function memoize(func, resolver) {
7471 if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
7472 throw new TypeError(FUNC_ERROR_TEXT);
7473 }
7474 var memoized = function() {
7475 var args = arguments,
7476 key = resolver ? resolver.apply(this, args) : args[0],
7477 cache = memoized.cache;
7478
7479 if (cache.has(key)) {
7480 return cache.get(key);
7481 }
7482 var result = func.apply(this, args);
7483 memoized.cache = cache.set(key, result) || cache;
7484 return result;
7485 };
7486 memoized.cache = new (memoize.Cache || _MapCache);
7487 return memoized;
7488}
7489
7490// Expose `MapCache`.
7491memoize.Cache = _MapCache;
7492
7493var memoize_1 = memoize;
7494
7495/** Used as the maximum memoize cache size. */
7496var MAX_MEMOIZE_SIZE = 500;
7497
7498/**
7499 * A specialized version of `_.memoize` which clears the memoized function's
7500 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
7501 *
7502 * @private
7503 * @param {Function} func The function to have its output memoized.
7504 * @returns {Function} Returns the new memoized function.
7505 */
7506function memoizeCapped(func) {
7507 var result = memoize_1(func, function(key) {
7508 if (cache.size === MAX_MEMOIZE_SIZE) {
7509 cache.clear();
7510 }
7511 return key;
7512 });
7513
7514 var cache = result.cache;
7515 return result;
7516}
7517
7518var _memoizeCapped = memoizeCapped;
7519
7520/** Used to match property names within property paths. */
7521var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
7522
7523/** Used to match backslashes in property paths. */
7524var reEscapeChar = /\\(\\)?/g;
7525
7526/**
7527 * Converts `string` to a property path array.
7528 *
7529 * @private
7530 * @param {string} string The string to convert.
7531 * @returns {Array} Returns the property path array.
7532 */
7533var stringToPath = _memoizeCapped(function(string) {
7534 var result = [];
7535 if (string.charCodeAt(0) === 46 /* . */) {
7536 result.push('');
7537 }
7538 string.replace(rePropName, function(match, number, quote, subString) {
7539 result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
7540 });
7541 return result;
7542});
7543
7544var _stringToPath = stringToPath;
7545
7546/**
7547 * A specialized version of `_.map` for arrays without support for iteratee
7548 * shorthands.
7549 *
7550 * @private
7551 * @param {Array} [array] The array to iterate over.
7552 * @param {Function} iteratee The function invoked per iteration.
7553 * @returns {Array} Returns the new mapped array.
7554 */
7555function arrayMap(array, iteratee) {
7556 var index = -1,
7557 length = array == null ? 0 : array.length,
7558 result = Array(length);
7559
7560 while (++index < length) {
7561 result[index] = iteratee(array[index], index, array);
7562 }
7563 return result;
7564}
7565
7566var _arrayMap = arrayMap;
7567
7568/** Used as references for various `Number` constants. */
7569var INFINITY$1 = 1 / 0;
7570
7571/** Used to convert symbols to primitives and strings. */
7572var symbolProto = _Symbol ? _Symbol.prototype : undefined,
7573 symbolToString = symbolProto ? symbolProto.toString : undefined;
7574
7575/**
7576 * The base implementation of `_.toString` which doesn't convert nullish
7577 * values to empty strings.
7578 *
7579 * @private
7580 * @param {*} value The value to process.
7581 * @returns {string} Returns the string.
7582 */
7583function baseToString(value) {
7584 // Exit early for strings to avoid a performance hit in some environments.
7585 if (typeof value == 'string') {
7586 return value;
7587 }
7588 if (isArray_1(value)) {
7589 // Recursively convert values (susceptible to call stack limits).
7590 return _arrayMap(value, baseToString) + '';
7591 }
7592 if (isSymbol_1(value)) {
7593 return symbolToString ? symbolToString.call(value) : '';
7594 }
7595 var result = (value + '');
7596 return (result == '0' && (1 / value) == -INFINITY$1) ? '-0' : result;
7597}
7598
7599var _baseToString = baseToString;
7600
7601/**
7602 * Converts `value` to a string. An empty string is returned for `null`
7603 * and `undefined` values. The sign of `-0` is preserved.
7604 *
7605 * @static
7606 * @memberOf _
7607 * @since 4.0.0
7608 * @category Lang
7609 * @param {*} value The value to convert.
7610 * @returns {string} Returns the converted string.
7611 * @example
7612 *
7613 * _.toString(null);
7614 * // => ''
7615 *
7616 * _.toString(-0);
7617 * // => '-0'
7618 *
7619 * _.toString([1, 2, 3]);
7620 * // => '1,2,3'
7621 */
7622function toString$1(value) {
7623 return value == null ? '' : _baseToString(value);
7624}
7625
7626var toString_1 = toString$1;
7627
7628/**
7629 * Casts `value` to a path array if it's not one.
7630 *
7631 * @private
7632 * @param {*} value The value to inspect.
7633 * @param {Object} [object] The object to query keys on.
7634 * @returns {Array} Returns the cast property path array.
7635 */
7636function castPath(value, object) {
7637 if (isArray_1(value)) {
7638 return value;
7639 }
7640 return _isKey(value, object) ? [value] : _stringToPath(toString_1(value));
7641}
7642
7643var _castPath = castPath;
7644
7645/** Used as references for various `Number` constants. */
7646var INFINITY = 1 / 0;
7647
7648/**
7649 * Converts `value` to a string key if it's not a string or symbol.
7650 *
7651 * @private
7652 * @param {*} value The value to inspect.
7653 * @returns {string|symbol} Returns the key.
7654 */
7655function toKey(value) {
7656 if (typeof value == 'string' || isSymbol_1(value)) {
7657 return value;
7658 }
7659 var result = (value + '');
7660 return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
7661}
7662
7663var _toKey = toKey;
7664
7665/**
7666 * The base implementation of `_.get` without support for default values.
7667 *
7668 * @private
7669 * @param {Object} object The object to query.
7670 * @param {Array|string} path The path of the property to get.
7671 * @returns {*} Returns the resolved value.
7672 */
7673function baseGet(object, path) {
7674 path = _castPath(path, object);
7675
7676 var index = 0,
7677 length = path.length;
7678
7679 while (object != null && index < length) {
7680 object = object[_toKey(path[index++])];
7681 }
7682 return (index && index == length) ? object : undefined;
7683}
7684
7685var _baseGet = baseGet;
7686
7687/**
7688 * Gets the value at `path` of `object`. If the resolved value is
7689 * `undefined`, the `defaultValue` is returned in its place.
7690 *
7691 * @static
7692 * @memberOf _
7693 * @since 3.7.0
7694 * @category Object
7695 * @param {Object} object The object to query.
7696 * @param {Array|string} path The path of the property to get.
7697 * @param {*} [defaultValue] The value returned for `undefined` resolved values.
7698 * @returns {*} Returns the resolved value.
7699 * @example
7700 *
7701 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
7702 *
7703 * _.get(object, 'a[0].b.c');
7704 * // => 3
7705 *
7706 * _.get(object, ['a', '0', 'b', 'c']);
7707 * // => 3
7708 *
7709 * _.get(object, 'a.b.c', 'default');
7710 * // => 'default'
7711 */
7712function get(object, path, defaultValue) {
7713 var result = object == null ? undefined : _baseGet(object, path);
7714 return result === undefined ? defaultValue : result;
7715}
7716
7717var get_1 = get;
7718
7719var defineProperty = (function() {
7720 try {
7721 var func = _getNative(Object, 'defineProperty');
7722 func({}, '', {});
7723 return func;
7724 } catch (e) {}
7725}());
7726
7727var _defineProperty = defineProperty;
7728
7729/**
7730 * The base implementation of `assignValue` and `assignMergeValue` without
7731 * value checks.
7732 *
7733 * @private
7734 * @param {Object} object The object to modify.
7735 * @param {string} key The key of the property to assign.
7736 * @param {*} value The value to assign.
7737 */
7738function baseAssignValue(object, key, value) {
7739 if (key == '__proto__' && _defineProperty) {
7740 _defineProperty(object, key, {
7741 'configurable': true,
7742 'enumerable': true,
7743 'value': value,
7744 'writable': true
7745 });
7746 } else {
7747 object[key] = value;
7748 }
7749}
7750
7751var _baseAssignValue = baseAssignValue;
7752
7753/** Used for built-in method references. */
7754var objectProto = Object.prototype;
7755
7756/** Used to check objects for own properties. */
7757var hasOwnProperty = objectProto.hasOwnProperty;
7758
7759/**
7760 * Assigns `value` to `key` of `object` if the existing value is not equivalent
7761 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
7762 * for equality comparisons.
7763 *
7764 * @private
7765 * @param {Object} object The object to modify.
7766 * @param {string} key The key of the property to assign.
7767 * @param {*} value The value to assign.
7768 */
7769function assignValue(object, key, value) {
7770 var objValue = object[key];
7771 if (!(hasOwnProperty.call(object, key) && eq_1(objValue, value)) ||
7772 (value === undefined && !(key in object))) {
7773 _baseAssignValue(object, key, value);
7774 }
7775}
7776
7777var _assignValue = assignValue;
7778
7779/** Used as references for various `Number` constants. */
7780var MAX_SAFE_INTEGER = 9007199254740991;
7781
7782/** Used to detect unsigned integer values. */
7783var reIsUint = /^(?:0|[1-9]\d*)$/;
7784
7785/**
7786 * Checks if `value` is a valid array-like index.
7787 *
7788 * @private
7789 * @param {*} value The value to check.
7790 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
7791 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
7792 */
7793function isIndex(value, length) {
7794 var type = typeof value;
7795 length = length == null ? MAX_SAFE_INTEGER : length;
7796
7797 return !!length &&
7798 (type == 'number' ||
7799 (type != 'symbol' && reIsUint.test(value))) &&
7800 (value > -1 && value % 1 == 0 && value < length);
7801}
7802
7803var _isIndex = isIndex;
7804
7805/**
7806 * The base implementation of `_.set`.
7807 *
7808 * @private
7809 * @param {Object} object The object to modify.
7810 * @param {Array|string} path The path of the property to set.
7811 * @param {*} value The value to set.
7812 * @param {Function} [customizer] The function to customize path creation.
7813 * @returns {Object} Returns `object`.
7814 */
7815function baseSet(object, path, value, customizer) {
7816 if (!isObject_1(object)) {
7817 return object;
7818 }
7819 path = _castPath(path, object);
7820
7821 var index = -1,
7822 length = path.length,
7823 lastIndex = length - 1,
7824 nested = object;
7825
7826 while (nested != null && ++index < length) {
7827 var key = _toKey(path[index]),
7828 newValue = value;
7829
7830 if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
7831 return object;
7832 }
7833
7834 if (index != lastIndex) {
7835 var objValue = nested[key];
7836 newValue = customizer ? customizer(objValue, key, nested) : undefined;
7837 if (newValue === undefined) {
7838 newValue = isObject_1(objValue)
7839 ? objValue
7840 : (_isIndex(path[index + 1]) ? [] : {});
7841 }
7842 }
7843 _assignValue(nested, key, newValue);
7844 nested = nested[key];
7845 }
7846 return object;
7847}
7848
7849var _baseSet = baseSet;
7850
7851/**
7852 * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
7853 * it's created. Arrays are created for missing index properties while objects
7854 * are created for all other missing properties. Use `_.setWith` to customize
7855 * `path` creation.
7856 *
7857 * **Note:** This method mutates `object`.
7858 *
7859 * @static
7860 * @memberOf _
7861 * @since 3.7.0
7862 * @category Object
7863 * @param {Object} object The object to modify.
7864 * @param {Array|string} path The path of the property to set.
7865 * @param {*} value The value to set.
7866 * @returns {Object} Returns `object`.
7867 * @example
7868 *
7869 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
7870 *
7871 * _.set(object, 'a[0].b.c', 4);
7872 * console.log(object.a[0].b.c);
7873 * // => 4
7874 *
7875 * _.set(object, ['x', '0', 'y', 'z'], 5);
7876 * console.log(object.x[0].y.z);
7877 * // => 5
7878 */
7879function set(object, path, value) {
7880 return object == null ? object : _baseSet(object, path, value);
7881}
7882
7883var set_1 = set;
7884
7885/**
7886 * Copies the values of `source` to `array`.
7887 *
7888 * @private
7889 * @param {Array} source The array to copy values from.
7890 * @param {Array} [array=[]] The array to copy values to.
7891 * @returns {Array} Returns `array`.
7892 */
7893function copyArray(source, array) {
7894 var index = -1,
7895 length = source.length;
7896
7897 array || (array = Array(length));
7898 while (++index < length) {
7899 array[index] = source[index];
7900 }
7901 return array;
7902}
7903
7904var _copyArray = copyArray;
7905
7906/**
7907 * Converts `value` to a property path array.
7908 *
7909 * @static
7910 * @memberOf _
7911 * @since 4.0.0
7912 * @category Util
7913 * @param {*} value The value to convert.
7914 * @returns {Array} Returns the new property path array.
7915 * @example
7916 *
7917 * _.toPath('a.b.c');
7918 * // => ['a', 'b', 'c']
7919 *
7920 * _.toPath('a[0].b.c');
7921 * // => ['a', '0', 'b', 'c']
7922 */
7923function toPath(value) {
7924 if (isArray_1(value)) {
7925 return _arrayMap(value, _toKey);
7926 }
7927 return isSymbol_1(value) ? [value] : _copyArray(_stringToPath(toString_1(value)));
7928}
7929
7930var toPath_1 = toPath;
7931
7932var define$2 = {
7933 // access data field
7934 data: function data(params) {
7935 var defaults = {
7936 field: 'data',
7937 bindingEvent: 'data',
7938 allowBinding: false,
7939 allowSetting: false,
7940 allowGetting: false,
7941 settingEvent: 'data',
7942 settingTriggersEvent: false,
7943 triggerFnName: 'trigger',
7944 immutableKeys: {},
7945 // key => true if immutable
7946 updateStyle: false,
7947 beforeGet: function beforeGet(self) {},
7948 beforeSet: function beforeSet(self, obj) {},
7949 onSet: function onSet(self) {},
7950 canSet: function canSet(self) {
7951 return true;
7952 }
7953 };
7954 params = extend({}, defaults, params);
7955 return function dataImpl(name, value) {
7956 var p = params;
7957 var self = this;
7958 var selfIsArrayLike = self.length !== undefined;
7959 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7960 var single = selfIsArrayLike ? self[0] : self;
7961
7962 // .data('foo', ...)
7963 if (string(name)) {
7964 // set or get property
7965 var isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
7966 var path = isPathLike && toPath_1(name);
7967
7968 // .data('foo')
7969 if (p.allowGetting && value === undefined) {
7970 // get
7971
7972 var ret;
7973 if (single) {
7974 p.beforeGet(single);
7975
7976 // check if it's path and a field with the same name doesn't exist
7977 if (path && single._private[p.field][name] === undefined) {
7978 ret = get_1(single._private[p.field], path);
7979 } else {
7980 ret = single._private[p.field][name];
7981 }
7982 }
7983 return ret;
7984
7985 // .data('foo', 'bar')
7986 } else if (p.allowSetting && value !== undefined) {
7987 // set
7988 var valid = !p.immutableKeys[name];
7989 if (valid) {
7990 var change = _defineProperty$1({}, name, value);
7991 p.beforeSet(self, change);
7992 for (var i = 0, l = all.length; i < l; i++) {
7993 var ele = all[i];
7994 if (p.canSet(ele)) {
7995 if (path && single._private[p.field][name] === undefined) {
7996 set_1(ele._private[p.field], path, value);
7997 } else {
7998 ele._private[p.field][name] = value;
7999 }
8000 }
8001 }
8002
8003 // update mappers if asked
8004 if (p.updateStyle) {
8005 self.updateStyle();
8006 }
8007
8008 // call onSet callback
8009 p.onSet(self);
8010 if (p.settingTriggersEvent) {
8011 self[p.triggerFnName](p.settingEvent);
8012 }
8013 }
8014 }
8015
8016 // .data({ 'foo': 'bar' })
8017 } else if (p.allowSetting && plainObject(name)) {
8018 // extend
8019 var obj = name;
8020 var k, v;
8021 var keys = Object.keys(obj);
8022 p.beforeSet(self, obj);
8023 for (var _i = 0; _i < keys.length; _i++) {
8024 k = keys[_i];
8025 v = obj[k];
8026 var _valid = !p.immutableKeys[k];
8027 if (_valid) {
8028 for (var j = 0; j < all.length; j++) {
8029 var _ele = all[j];
8030 if (p.canSet(_ele)) {
8031 _ele._private[p.field][k] = v;
8032 }
8033 }
8034 }
8035 }
8036
8037 // update mappers if asked
8038 if (p.updateStyle) {
8039 self.updateStyle();
8040 }
8041
8042 // call onSet callback
8043 p.onSet(self);
8044 if (p.settingTriggersEvent) {
8045 self[p.triggerFnName](p.settingEvent);
8046 }
8047
8048 // .data(function(){ ... })
8049 } else if (p.allowBinding && fn$6(name)) {
8050 // bind to event
8051 var fn = name;
8052 self.on(p.bindingEvent, fn);
8053
8054 // .data()
8055 } else if (p.allowGetting && name === undefined) {
8056 // get whole object
8057 var _ret;
8058 if (single) {
8059 p.beforeGet(single);
8060 _ret = single._private[p.field];
8061 }
8062 return _ret;
8063 }
8064 return self; // maintain chainability
8065 }; // function
8066 },
8067
8068 // data
8069
8070 // remove data field
8071 removeData: function removeData(params) {
8072 var defaults = {
8073 field: 'data',
8074 event: 'data',
8075 triggerFnName: 'trigger',
8076 triggerEvent: false,
8077 immutableKeys: {} // key => true if immutable
8078 };
8079
8080 params = extend({}, defaults, params);
8081 return function removeDataImpl(names) {
8082 var p = params;
8083 var self = this;
8084 var selfIsArrayLike = self.length !== undefined;
8085 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
8086
8087 // .removeData('foo bar')
8088 if (string(names)) {
8089 // then get the list of keys, and delete them
8090 var keys = names.split(/\s+/);
8091 var l = keys.length;
8092 for (var i = 0; i < l; i++) {
8093 // delete each non-empty key
8094 var key = keys[i];
8095 if (emptyString(key)) {
8096 continue;
8097 }
8098 var valid = !p.immutableKeys[key]; // not valid if immutable
8099 if (valid) {
8100 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
8101 all[i_a]._private[p.field][key] = undefined;
8102 }
8103 }
8104 }
8105 if (p.triggerEvent) {
8106 self[p.triggerFnName](p.event);
8107 }
8108
8109 // .removeData()
8110 } else if (names === undefined) {
8111 // then delete all keys
8112
8113 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
8114 var _privateFields = all[_i_a]._private[p.field];
8115 var _keys = Object.keys(_privateFields);
8116 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
8117 var _key = _keys[_i2];
8118 var validKeyToDelete = !p.immutableKeys[_key];
8119 if (validKeyToDelete) {
8120 _privateFields[_key] = undefined;
8121 }
8122 }
8123 }
8124 if (p.triggerEvent) {
8125 self[p.triggerFnName](p.event);
8126 }
8127 }
8128 return self; // maintain chaining
8129 }; // function
8130 } // removeData
8131}; // define
8132
8133var define$1 = {
8134 eventAliasesOn: function eventAliasesOn(proto) {
8135 var p = proto;
8136 p.addListener = p.listen = p.bind = p.on;
8137 p.unlisten = p.unbind = p.off = p.removeListener;
8138 p.trigger = p.emit;
8139
8140 // this is just a wrapper alias of .on()
8141 p.pon = p.promiseOn = function (events, selector) {
8142 var self = this;
8143 var args = Array.prototype.slice.call(arguments, 0);
8144 return new Promise$1(function (resolve, reject) {
8145 var callback = function callback(e) {
8146 self.off.apply(self, offArgs);
8147 resolve(e);
8148 };
8149 var onArgs = args.concat([callback]);
8150 var offArgs = onArgs.concat([]);
8151 self.on.apply(self, onArgs);
8152 });
8153 };
8154 }
8155}; // define
8156
8157// use this module to cherry pick functions into your prototype
8158var define = {};
8159[define$3, define$2, define$1].forEach(function (m) {
8160 extend(define, m);
8161});
8162
8163var elesfn$i = {
8164 animate: define.animate(),
8165 animation: define.animation(),
8166 animated: define.animated(),
8167 clearQueue: define.clearQueue(),
8168 delay: define.delay(),
8169 delayAnimation: define.delayAnimation(),
8170 stop: define.stop()
8171};
8172
8173var elesfn$h = {
8174 classes: function classes(_classes) {
8175 var self = this;
8176 if (_classes === undefined) {
8177 var ret = [];
8178 self[0]._private.classes.forEach(function (cls) {
8179 return ret.push(cls);
8180 });
8181 return ret;
8182 } else if (!array(_classes)) {
8183 // extract classes from string
8184 _classes = (_classes || '').match(/\S+/g) || [];
8185 }
8186 var changed = [];
8187 var classesSet = new Set$1(_classes);
8188
8189 // check and update each ele
8190 for (var j = 0; j < self.length; j++) {
8191 var ele = self[j];
8192 var _p = ele._private;
8193 var eleClasses = _p.classes;
8194 var changedEle = false;
8195
8196 // check if ele has all of the passed classes
8197 for (var i = 0; i < _classes.length; i++) {
8198 var cls = _classes[i];
8199 var eleHasClass = eleClasses.has(cls);
8200 if (!eleHasClass) {
8201 changedEle = true;
8202 break;
8203 }
8204 }
8205
8206 // check if ele has classes outside of those passed
8207 if (!changedEle) {
8208 changedEle = eleClasses.size !== _classes.length;
8209 }
8210 if (changedEle) {
8211 _p.classes = classesSet;
8212 changed.push(ele);
8213 }
8214 }
8215
8216 // trigger update style on those eles that had class changes
8217 if (changed.length > 0) {
8218 this.spawn(changed).updateStyle().emit('class');
8219 }
8220 return self;
8221 },
8222 addClass: function addClass(classes) {
8223 return this.toggleClass(classes, true);
8224 },
8225 hasClass: function hasClass(className) {
8226 var ele = this[0];
8227 return ele != null && ele._private.classes.has(className);
8228 },
8229 toggleClass: function toggleClass(classes, toggle) {
8230 if (!array(classes)) {
8231 // extract classes from string
8232 classes = classes.match(/\S+/g) || [];
8233 }
8234 var self = this;
8235 var toggleUndefd = toggle === undefined;
8236 var changed = []; // eles who had classes changed
8237
8238 for (var i = 0, il = self.length; i < il; i++) {
8239 var ele = self[i];
8240 var eleClasses = ele._private.classes;
8241 var changedEle = false;
8242 for (var j = 0; j < classes.length; j++) {
8243 var cls = classes[j];
8244 var hasClass = eleClasses.has(cls);
8245 var changedNow = false;
8246 if (toggle || toggleUndefd && !hasClass) {
8247 eleClasses.add(cls);
8248 changedNow = true;
8249 } else if (!toggle || toggleUndefd && hasClass) {
8250 eleClasses["delete"](cls);
8251 changedNow = true;
8252 }
8253 if (!changedEle && changedNow) {
8254 changed.push(ele);
8255 changedEle = true;
8256 }
8257 } // for j classes
8258 } // for i eles
8259
8260 // trigger update style on those eles that had class changes
8261 if (changed.length > 0) {
8262 this.spawn(changed).updateStyle().emit('class');
8263 }
8264 return self;
8265 },
8266 removeClass: function removeClass(classes) {
8267 return this.toggleClass(classes, false);
8268 },
8269 flashClass: function flashClass(classes, duration) {
8270 var self = this;
8271 if (duration == null) {
8272 duration = 250;
8273 } else if (duration === 0) {
8274 return self; // nothing to do really
8275 }
8276
8277 self.addClass(classes);
8278 setTimeout(function () {
8279 self.removeClass(classes);
8280 }, duration);
8281 return self;
8282 }
8283};
8284elesfn$h.className = elesfn$h.classNames = elesfn$h.classes;
8285
8286// tokens in the query language
8287var tokens = {
8288 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
8289 // chars we need to escape in let names, etc
8290 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
8291 // binary comparison op (used in data selectors)
8292 boolOp: '\\?|\\!|\\^',
8293 // boolean (unary) operators (used in data selectors)
8294 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
8295 // string literals (used in data selectors) -- doublequotes | singlequotes
8296 number: number,
8297 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
8298 meta: 'degree|indegree|outdegree',
8299 // allowed metadata fields (i.e. allowed functions to use from Collection)
8300 separator: '\\s*,\\s*',
8301 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
8302 descendant: '\\s+',
8303 child: '\\s+>\\s+',
8304 subject: '\\$',
8305 group: 'node|edge|\\*',
8306 directedEdge: '\\s+->\\s+',
8307 undirectedEdge: '\\s+<->\\s+'
8308};
8309tokens.variable = '(?:[\\w-.]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name can have letters, numbers, dashes, and periods
8310tokens.className = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a class name has the same rules as a variable except it can't have a '.' in the name
8311tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
8312tokens.id = tokens.variable; // an element id (follows variable conventions)
8313
8314(function () {
8315 var ops, op, i;
8316
8317 // add @ variants to comparatorOp
8318 ops = tokens.comparatorOp.split('|');
8319 for (i = 0; i < ops.length; i++) {
8320 op = ops[i];
8321 tokens.comparatorOp += '|@' + op;
8322 }
8323
8324 // add ! variants to comparatorOp
8325 ops = tokens.comparatorOp.split('|');
8326 for (i = 0; i < ops.length; i++) {
8327 op = ops[i];
8328 if (op.indexOf('!') >= 0) {
8329 continue;
8330 } // skip ops that explicitly contain !
8331 if (op === '=') {
8332 continue;
8333 } // skip = b/c != is explicitly defined
8334
8335 tokens.comparatorOp += '|\\!' + op;
8336 }
8337})();
8338
8339/**
8340 * Make a new query object
8341 *
8342 * @prop type {Type} The type enum (int) of the query
8343 * @prop checks List of checks to make against an ele to test for a match
8344 */
8345var newQuery = function newQuery() {
8346 return {
8347 checks: []
8348 };
8349};
8350
8351/**
8352 * A check type enum-like object. Uses integer values for fast match() lookup.
8353 * The ordering does not matter as long as the ints are unique.
8354 */
8355var Type = {
8356 /** E.g. node */
8357 GROUP: 0,
8358 /** A collection of elements */
8359 COLLECTION: 1,
8360 /** A filter(ele) function */
8361 FILTER: 2,
8362 /** E.g. [foo > 1] */
8363 DATA_COMPARE: 3,
8364 /** E.g. [foo] */
8365 DATA_EXIST: 4,
8366 /** E.g. [?foo] */
8367 DATA_BOOL: 5,
8368 /** E.g. [[degree > 2]] */
8369 META_COMPARE: 6,
8370 /** E.g. :selected */
8371 STATE: 7,
8372 /** E.g. #foo */
8373 ID: 8,
8374 /** E.g. .foo */
8375 CLASS: 9,
8376 /** E.g. #foo <-> #bar */
8377 UNDIRECTED_EDGE: 10,
8378 /** E.g. #foo -> #bar */
8379 DIRECTED_EDGE: 11,
8380 /** E.g. $#foo -> #bar */
8381 NODE_SOURCE: 12,
8382 /** E.g. #foo -> $#bar */
8383 NODE_TARGET: 13,
8384 /** E.g. $#foo <-> #bar */
8385 NODE_NEIGHBOR: 14,
8386 /** E.g. #foo > #bar */
8387 CHILD: 15,
8388 /** E.g. #foo #bar */
8389 DESCENDANT: 16,
8390 /** E.g. $#foo > #bar */
8391 PARENT: 17,
8392 /** E.g. $#foo #bar */
8393 ANCESTOR: 18,
8394 /** E.g. #foo > $bar > #baz */
8395 COMPOUND_SPLIT: 19,
8396 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
8397 TRUE: 20
8398};
8399
8400var stateSelectors = [{
8401 selector: ':selected',
8402 matches: function matches(ele) {
8403 return ele.selected();
8404 }
8405}, {
8406 selector: ':unselected',
8407 matches: function matches(ele) {
8408 return !ele.selected();
8409 }
8410}, {
8411 selector: ':selectable',
8412 matches: function matches(ele) {
8413 return ele.selectable();
8414 }
8415}, {
8416 selector: ':unselectable',
8417 matches: function matches(ele) {
8418 return !ele.selectable();
8419 }
8420}, {
8421 selector: ':locked',
8422 matches: function matches(ele) {
8423 return ele.locked();
8424 }
8425}, {
8426 selector: ':unlocked',
8427 matches: function matches(ele) {
8428 return !ele.locked();
8429 }
8430}, {
8431 selector: ':visible',
8432 matches: function matches(ele) {
8433 return ele.visible();
8434 }
8435}, {
8436 selector: ':hidden',
8437 matches: function matches(ele) {
8438 return !ele.visible();
8439 }
8440}, {
8441 selector: ':transparent',
8442 matches: function matches(ele) {
8443 return ele.transparent();
8444 }
8445}, {
8446 selector: ':grabbed',
8447 matches: function matches(ele) {
8448 return ele.grabbed();
8449 }
8450}, {
8451 selector: ':free',
8452 matches: function matches(ele) {
8453 return !ele.grabbed();
8454 }
8455}, {
8456 selector: ':removed',
8457 matches: function matches(ele) {
8458 return ele.removed();
8459 }
8460}, {
8461 selector: ':inside',
8462 matches: function matches(ele) {
8463 return !ele.removed();
8464 }
8465}, {
8466 selector: ':grabbable',
8467 matches: function matches(ele) {
8468 return ele.grabbable();
8469 }
8470}, {
8471 selector: ':ungrabbable',
8472 matches: function matches(ele) {
8473 return !ele.grabbable();
8474 }
8475}, {
8476 selector: ':animated',
8477 matches: function matches(ele) {
8478 return ele.animated();
8479 }
8480}, {
8481 selector: ':unanimated',
8482 matches: function matches(ele) {
8483 return !ele.animated();
8484 }
8485}, {
8486 selector: ':parent',
8487 matches: function matches(ele) {
8488 return ele.isParent();
8489 }
8490}, {
8491 selector: ':childless',
8492 matches: function matches(ele) {
8493 return ele.isChildless();
8494 }
8495}, {
8496 selector: ':child',
8497 matches: function matches(ele) {
8498 return ele.isChild();
8499 }
8500}, {
8501 selector: ':orphan',
8502 matches: function matches(ele) {
8503 return ele.isOrphan();
8504 }
8505}, {
8506 selector: ':nonorphan',
8507 matches: function matches(ele) {
8508 return ele.isChild();
8509 }
8510}, {
8511 selector: ':compound',
8512 matches: function matches(ele) {
8513 if (ele.isNode()) {
8514 return ele.isParent();
8515 } else {
8516 return ele.source().isParent() || ele.target().isParent();
8517 }
8518 }
8519}, {
8520 selector: ':loop',
8521 matches: function matches(ele) {
8522 return ele.isLoop();
8523 }
8524}, {
8525 selector: ':simple',
8526 matches: function matches(ele) {
8527 return ele.isSimple();
8528 }
8529}, {
8530 selector: ':active',
8531 matches: function matches(ele) {
8532 return ele.active();
8533 }
8534}, {
8535 selector: ':inactive',
8536 matches: function matches(ele) {
8537 return !ele.active();
8538 }
8539}, {
8540 selector: ':backgrounding',
8541 matches: function matches(ele) {
8542 return ele.backgrounding();
8543 }
8544}, {
8545 selector: ':nonbackgrounding',
8546 matches: function matches(ele) {
8547 return !ele.backgrounding();
8548 }
8549}].sort(function (a, b) {
8550 // n.b. selectors that are starting substrings of others must have the longer ones first
8551 return descending(a.selector, b.selector);
8552});
8553var lookup = function () {
8554 var selToFn = {};
8555 var s;
8556 for (var i = 0; i < stateSelectors.length; i++) {
8557 s = stateSelectors[i];
8558 selToFn[s.selector] = s.matches;
8559 }
8560 return selToFn;
8561}();
8562var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
8563 return lookup[sel](ele);
8564};
8565var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
8566 return s.selector;
8567}).join('|') + ')';
8568
8569// when a token like a variable has escaped meta characters, we need to clean the backslashes out
8570// so that values get compared properly in Selector.filter()
8571var cleanMetaChars = function cleanMetaChars(str) {
8572 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
8573 return $1;
8574 });
8575};
8576var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
8577 selector[selector.length - 1] = replacementQuery;
8578};
8579
8580// NOTE: add new expression syntax here to have it recognised by the parser;
8581// - a query contains all adjacent (i.e. no separator in between) expressions;
8582// - the current query is stored in selector[i]
8583// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
8584var exprs = [{
8585 name: 'group',
8586 // just used for identifying when debugging
8587 query: true,
8588 regex: '(' + tokens.group + ')',
8589 populate: function populate(selector, query, _ref) {
8590 var _ref2 = _slicedToArray(_ref, 1),
8591 group = _ref2[0];
8592 query.checks.push({
8593 type: Type.GROUP,
8594 value: group === '*' ? group : group + 's'
8595 });
8596 }
8597}, {
8598 name: 'state',
8599 query: true,
8600 regex: stateSelectorRegex,
8601 populate: function populate(selector, query, _ref3) {
8602 var _ref4 = _slicedToArray(_ref3, 1),
8603 state = _ref4[0];
8604 query.checks.push({
8605 type: Type.STATE,
8606 value: state
8607 });
8608 }
8609}, {
8610 name: 'id',
8611 query: true,
8612 regex: '\\#(' + tokens.id + ')',
8613 populate: function populate(selector, query, _ref5) {
8614 var _ref6 = _slicedToArray(_ref5, 1),
8615 id = _ref6[0];
8616 query.checks.push({
8617 type: Type.ID,
8618 value: cleanMetaChars(id)
8619 });
8620 }
8621}, {
8622 name: 'className',
8623 query: true,
8624 regex: '\\.(' + tokens.className + ')',
8625 populate: function populate(selector, query, _ref7) {
8626 var _ref8 = _slicedToArray(_ref7, 1),
8627 className = _ref8[0];
8628 query.checks.push({
8629 type: Type.CLASS,
8630 value: cleanMetaChars(className)
8631 });
8632 }
8633}, {
8634 name: 'dataExists',
8635 query: true,
8636 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
8637 populate: function populate(selector, query, _ref9) {
8638 var _ref10 = _slicedToArray(_ref9, 1),
8639 variable = _ref10[0];
8640 query.checks.push({
8641 type: Type.DATA_EXIST,
8642 field: cleanMetaChars(variable)
8643 });
8644 }
8645}, {
8646 name: 'dataCompare',
8647 query: true,
8648 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
8649 populate: function populate(selector, query, _ref11) {
8650 var _ref12 = _slicedToArray(_ref11, 3),
8651 variable = _ref12[0],
8652 comparatorOp = _ref12[1],
8653 value = _ref12[2];
8654 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
8655 if (valueIsString) {
8656 value = value.substring(1, value.length - 1);
8657 } else {
8658 value = parseFloat(value);
8659 }
8660 query.checks.push({
8661 type: Type.DATA_COMPARE,
8662 field: cleanMetaChars(variable),
8663 operator: comparatorOp,
8664 value: value
8665 });
8666 }
8667}, {
8668 name: 'dataBool',
8669 query: true,
8670 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
8671 populate: function populate(selector, query, _ref13) {
8672 var _ref14 = _slicedToArray(_ref13, 2),
8673 boolOp = _ref14[0],
8674 variable = _ref14[1];
8675 query.checks.push({
8676 type: Type.DATA_BOOL,
8677 field: cleanMetaChars(variable),
8678 operator: boolOp
8679 });
8680 }
8681}, {
8682 name: 'metaCompare',
8683 query: true,
8684 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
8685 populate: function populate(selector, query, _ref15) {
8686 var _ref16 = _slicedToArray(_ref15, 3),
8687 meta = _ref16[0],
8688 comparatorOp = _ref16[1],
8689 number = _ref16[2];
8690 query.checks.push({
8691 type: Type.META_COMPARE,
8692 field: cleanMetaChars(meta),
8693 operator: comparatorOp,
8694 value: parseFloat(number)
8695 });
8696 }
8697}, {
8698 name: 'nextQuery',
8699 separator: true,
8700 regex: tokens.separator,
8701 populate: function populate(selector, query) {
8702 var currentSubject = selector.currentSubject;
8703 var edgeCount = selector.edgeCount;
8704 var compoundCount = selector.compoundCount;
8705 var lastQ = selector[selector.length - 1];
8706 if (currentSubject != null) {
8707 lastQ.subject = currentSubject;
8708 selector.currentSubject = null;
8709 }
8710 lastQ.edgeCount = edgeCount;
8711 lastQ.compoundCount = compoundCount;
8712 selector.edgeCount = 0;
8713 selector.compoundCount = 0;
8714
8715 // go on to next query
8716 var nextQuery = selector[selector.length++] = newQuery();
8717 return nextQuery; // this is the new query to be filled by the following exprs
8718 }
8719}, {
8720 name: 'directedEdge',
8721 separator: true,
8722 regex: tokens.directedEdge,
8723 populate: function populate(selector, query) {
8724 if (selector.currentSubject == null) {
8725 // undirected edge
8726 var edgeQuery = newQuery();
8727 var source = query;
8728 var target = newQuery();
8729 edgeQuery.checks.push({
8730 type: Type.DIRECTED_EDGE,
8731 source: source,
8732 target: target
8733 });
8734
8735 // the query in the selector should be the edge rather than the source
8736 replaceLastQuery(selector, query, edgeQuery);
8737 selector.edgeCount++;
8738
8739 // we're now populating the target query with expressions that follow
8740 return target;
8741 } else {
8742 // source/target
8743 var srcTgtQ = newQuery();
8744 var _source = query;
8745 var _target = newQuery();
8746 srcTgtQ.checks.push({
8747 type: Type.NODE_SOURCE,
8748 source: _source,
8749 target: _target
8750 });
8751
8752 // the query in the selector should be the neighbourhood rather than the node
8753 replaceLastQuery(selector, query, srcTgtQ);
8754 selector.edgeCount++;
8755 return _target; // now populating the target with the following expressions
8756 }
8757 }
8758}, {
8759 name: 'undirectedEdge',
8760 separator: true,
8761 regex: tokens.undirectedEdge,
8762 populate: function populate(selector, query) {
8763 if (selector.currentSubject == null) {
8764 // undirected edge
8765 var edgeQuery = newQuery();
8766 var source = query;
8767 var target = newQuery();
8768 edgeQuery.checks.push({
8769 type: Type.UNDIRECTED_EDGE,
8770 nodes: [source, target]
8771 });
8772
8773 // the query in the selector should be the edge rather than the source
8774 replaceLastQuery(selector, query, edgeQuery);
8775 selector.edgeCount++;
8776
8777 // we're now populating the target query with expressions that follow
8778 return target;
8779 } else {
8780 // neighbourhood
8781 var nhoodQ = newQuery();
8782 var node = query;
8783 var neighbor = newQuery();
8784 nhoodQ.checks.push({
8785 type: Type.NODE_NEIGHBOR,
8786 node: node,
8787 neighbor: neighbor
8788 });
8789
8790 // the query in the selector should be the neighbourhood rather than the node
8791 replaceLastQuery(selector, query, nhoodQ);
8792 return neighbor; // now populating the neighbor with following expressions
8793 }
8794 }
8795}, {
8796 name: 'child',
8797 separator: true,
8798 regex: tokens.child,
8799 populate: function populate(selector, query) {
8800 if (selector.currentSubject == null) {
8801 // default: child query
8802 var parentChildQuery = newQuery();
8803 var child = newQuery();
8804 var parent = selector[selector.length - 1];
8805 parentChildQuery.checks.push({
8806 type: Type.CHILD,
8807 parent: parent,
8808 child: child
8809 });
8810
8811 // the query in the selector should be the '>' itself
8812 replaceLastQuery(selector, query, parentChildQuery);
8813 selector.compoundCount++;
8814
8815 // we're now populating the child query with expressions that follow
8816 return child;
8817 } else if (selector.currentSubject === query) {
8818 // compound split query
8819 var compound = newQuery();
8820 var left = selector[selector.length - 1];
8821 var right = newQuery();
8822 var subject = newQuery();
8823 var _child = newQuery();
8824 var _parent = newQuery();
8825
8826 // set up the root compound q
8827 compound.checks.push({
8828 type: Type.COMPOUND_SPLIT,
8829 left: left,
8830 right: right,
8831 subject: subject
8832 });
8833
8834 // populate the subject and replace the q at the old spot (within left) with TRUE
8835 subject.checks = query.checks; // take the checks from the left
8836 query.checks = [{
8837 type: Type.TRUE
8838 }]; // checks under left refs the subject implicitly
8839
8840 // set up the right q
8841 _parent.checks.push({
8842 type: Type.TRUE
8843 }); // parent implicitly refs the subject
8844 right.checks.push({
8845 type: Type.PARENT,
8846 // type is swapped on right side queries
8847 parent: _parent,
8848 child: _child // empty for now
8849 });
8850
8851 replaceLastQuery(selector, left, compound);
8852
8853 // update the ref since we moved things around for `query`
8854 selector.currentSubject = subject;
8855 selector.compoundCount++;
8856 return _child; // now populating the right side's child
8857 } else {
8858 // parent query
8859 // info for parent query
8860 var _parent2 = newQuery();
8861 var _child2 = newQuery();
8862 var pcQChecks = [{
8863 type: Type.PARENT,
8864 parent: _parent2,
8865 child: _child2
8866 }];
8867
8868 // the parent-child query takes the place of the query previously being populated
8869 _parent2.checks = query.checks; // the previous query contains the checks for the parent
8870 query.checks = pcQChecks; // pc query takes over
8871
8872 selector.compoundCount++;
8873 return _child2; // we're now populating the child
8874 }
8875 }
8876}, {
8877 name: 'descendant',
8878 separator: true,
8879 regex: tokens.descendant,
8880 populate: function populate(selector, query) {
8881 if (selector.currentSubject == null) {
8882 // default: descendant query
8883 var ancChQuery = newQuery();
8884 var descendant = newQuery();
8885 var ancestor = selector[selector.length - 1];
8886 ancChQuery.checks.push({
8887 type: Type.DESCENDANT,
8888 ancestor: ancestor,
8889 descendant: descendant
8890 });
8891
8892 // the query in the selector should be the '>' itself
8893 replaceLastQuery(selector, query, ancChQuery);
8894 selector.compoundCount++;
8895
8896 // we're now populating the descendant query with expressions that follow
8897 return descendant;
8898 } else if (selector.currentSubject === query) {
8899 // compound split query
8900 var compound = newQuery();
8901 var left = selector[selector.length - 1];
8902 var right = newQuery();
8903 var subject = newQuery();
8904 var _descendant = newQuery();
8905 var _ancestor = newQuery();
8906
8907 // set up the root compound q
8908 compound.checks.push({
8909 type: Type.COMPOUND_SPLIT,
8910 left: left,
8911 right: right,
8912 subject: subject
8913 });
8914
8915 // populate the subject and replace the q at the old spot (within left) with TRUE
8916 subject.checks = query.checks; // take the checks from the left
8917 query.checks = [{
8918 type: Type.TRUE
8919 }]; // checks under left refs the subject implicitly
8920
8921 // set up the right q
8922 _ancestor.checks.push({
8923 type: Type.TRUE
8924 }); // ancestor implicitly refs the subject
8925 right.checks.push({
8926 type: Type.ANCESTOR,
8927 // type is swapped on right side queries
8928 ancestor: _ancestor,
8929 descendant: _descendant // empty for now
8930 });
8931
8932 replaceLastQuery(selector, left, compound);
8933
8934 // update the ref since we moved things around for `query`
8935 selector.currentSubject = subject;
8936 selector.compoundCount++;
8937 return _descendant; // now populating the right side's descendant
8938 } else {
8939 // ancestor query
8940 // info for parent query
8941 var _ancestor2 = newQuery();
8942 var _descendant2 = newQuery();
8943 var adQChecks = [{
8944 type: Type.ANCESTOR,
8945 ancestor: _ancestor2,
8946 descendant: _descendant2
8947 }];
8948
8949 // the parent-child query takes the place of the query previously being populated
8950 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
8951 query.checks = adQChecks; // pc query takes over
8952
8953 selector.compoundCount++;
8954 return _descendant2; // we're now populating the child
8955 }
8956 }
8957}, {
8958 name: 'subject',
8959 modifier: true,
8960 regex: tokens.subject,
8961 populate: function populate(selector, query) {
8962 if (selector.currentSubject != null && selector.currentSubject !== query) {
8963 warn('Redefinition of subject in selector `' + selector.toString() + '`');
8964 return false;
8965 }
8966 selector.currentSubject = query;
8967 var topQ = selector[selector.length - 1];
8968 var topChk = topQ.checks[0];
8969 var topType = topChk == null ? null : topChk.type;
8970 if (topType === Type.DIRECTED_EDGE) {
8971 // directed edge with subject on the target
8972
8973 // change to target node check
8974 topChk.type = Type.NODE_TARGET;
8975 } else if (topType === Type.UNDIRECTED_EDGE) {
8976 // undirected edge with subject on the second node
8977
8978 // change to neighbor check
8979 topChk.type = Type.NODE_NEIGHBOR;
8980 topChk.node = topChk.nodes[1]; // second node is subject
8981 topChk.neighbor = topChk.nodes[0];
8982
8983 // clean up unused fields for new type
8984 topChk.nodes = null;
8985 }
8986 }
8987}];
8988exprs.forEach(function (e) {
8989 return e.regexObj = new RegExp('^' + e.regex);
8990});
8991
8992/**
8993 * Of all the expressions, find the first match in the remaining text.
8994 * @param {string} remaining The remaining text to parse
8995 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
8996 */
8997var consumeExpr = function consumeExpr(remaining) {
8998 var expr;
8999 var match;
9000 var name;
9001 for (var j = 0; j < exprs.length; j++) {
9002 var e = exprs[j];
9003 var n = e.name;
9004 var m = remaining.match(e.regexObj);
9005 if (m != null) {
9006 match = m;
9007 expr = e;
9008 name = n;
9009 var consumed = m[0];
9010 remaining = remaining.substring(consumed.length);
9011 break; // we've consumed one expr, so we can return now
9012 }
9013 }
9014
9015 return {
9016 expr: expr,
9017 match: match,
9018 name: name,
9019 remaining: remaining
9020 };
9021};
9022
9023/**
9024 * Consume all the leading whitespace
9025 * @param {string} remaining The text to consume
9026 * @returns The text with the leading whitespace removed
9027 */
9028var consumeWhitespace = function consumeWhitespace(remaining) {
9029 var match = remaining.match(/^\s+/);
9030 if (match) {
9031 var consumed = match[0];
9032 remaining = remaining.substring(consumed.length);
9033 }
9034 return remaining;
9035};
9036
9037/**
9038 * Parse the string and store the parsed representation in the Selector.
9039 * @param {string} selector The selector string
9040 * @returns `true` if the selector was successfully parsed, `false` otherwise
9041 */
9042var parse = function parse(selector) {
9043 var self = this;
9044 var remaining = self.inputText = selector;
9045 var currentQuery = self[0] = newQuery();
9046 self.length = 1;
9047 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
9048
9049 for (;;) {
9050 var exprInfo = consumeExpr(remaining);
9051 if (exprInfo.expr == null) {
9052 warn('The selector `' + selector + '`is invalid');
9053 return false;
9054 } else {
9055 var args = exprInfo.match.slice(1);
9056
9057 // let the token populate the selector object in currentQuery
9058 var ret = exprInfo.expr.populate(self, currentQuery, args);
9059 if (ret === false) {
9060 return false; // exit if population failed
9061 } else if (ret != null) {
9062 currentQuery = ret; // change the current query to be filled if the expr specifies
9063 }
9064 }
9065
9066 remaining = exprInfo.remaining;
9067
9068 // we're done when there's nothing left to parse
9069 if (remaining.match(/^\s*$/)) {
9070 break;
9071 }
9072 }
9073 var lastQ = self[self.length - 1];
9074 if (self.currentSubject != null) {
9075 lastQ.subject = self.currentSubject;
9076 }
9077 lastQ.edgeCount = self.edgeCount;
9078 lastQ.compoundCount = self.compoundCount;
9079 for (var i = 0; i < self.length; i++) {
9080 var q = self[i];
9081
9082 // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
9083 if (q.compoundCount > 0 && q.edgeCount > 0) {
9084 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
9085 return false;
9086 }
9087 if (q.edgeCount > 1) {
9088 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
9089 return false;
9090 } else if (q.edgeCount === 1) {
9091 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.');
9092 }
9093 }
9094 return true; // success
9095};
9096
9097/**
9098 * Get the selector represented as a string. This value uses default formatting,
9099 * so things like spacing may differ from the input text passed to the constructor.
9100 * @returns {string} The selector string
9101 */
9102var toString = function toString() {
9103 if (this.toStringCache != null) {
9104 return this.toStringCache;
9105 }
9106 var clean = function clean(obj) {
9107 if (obj == null) {
9108 return '';
9109 } else {
9110 return obj;
9111 }
9112 };
9113 var cleanVal = function cleanVal(val) {
9114 if (string(val)) {
9115 return '"' + val + '"';
9116 } else {
9117 return clean(val);
9118 }
9119 };
9120 var space = function space(val) {
9121 return ' ' + val + ' ';
9122 };
9123 var checkToString = function checkToString(check, subject) {
9124 var type = check.type,
9125 value = check.value;
9126 switch (type) {
9127 case Type.GROUP:
9128 {
9129 var group = clean(value);
9130 return group.substring(0, group.length - 1);
9131 }
9132 case Type.DATA_COMPARE:
9133 {
9134 var field = check.field,
9135 operator = check.operator;
9136 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
9137 }
9138 case Type.DATA_BOOL:
9139 {
9140 var _operator = check.operator,
9141 _field = check.field;
9142 return '[' + clean(_operator) + _field + ']';
9143 }
9144 case Type.DATA_EXIST:
9145 {
9146 var _field2 = check.field;
9147 return '[' + _field2 + ']';
9148 }
9149 case Type.META_COMPARE:
9150 {
9151 var _operator2 = check.operator,
9152 _field3 = check.field;
9153 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
9154 }
9155 case Type.STATE:
9156 {
9157 return value;
9158 }
9159 case Type.ID:
9160 {
9161 return '#' + value;
9162 }
9163 case Type.CLASS:
9164 {
9165 return '.' + value;
9166 }
9167 case Type.PARENT:
9168 case Type.CHILD:
9169 {
9170 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
9171 }
9172 case Type.ANCESTOR:
9173 case Type.DESCENDANT:
9174 {
9175 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
9176 }
9177 case Type.COMPOUND_SPLIT:
9178 {
9179 var lhs = queryToString(check.left, subject);
9180 var sub = queryToString(check.subject, subject);
9181 var rhs = queryToString(check.right, subject);
9182 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
9183 }
9184 case Type.TRUE:
9185 {
9186 return '';
9187 }
9188 }
9189 };
9190 var queryToString = function queryToString(query, subject) {
9191 return query.checks.reduce(function (str, chk, i) {
9192 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
9193 }, '');
9194 };
9195 var str = '';
9196 for (var i = 0; i < this.length; i++) {
9197 var query = this[i];
9198 str += queryToString(query, query.subject);
9199 if (this.length > 1 && i < this.length - 1) {
9200 str += ', ';
9201 }
9202 }
9203 this.toStringCache = str;
9204 return str;
9205};
9206var parse$1 = {
9207 parse: parse,
9208 toString: toString
9209};
9210
9211var valCmp = function valCmp(fieldVal, operator, value) {
9212 var matches;
9213 var isFieldStr = string(fieldVal);
9214 var isFieldNum = number$1(fieldVal);
9215 var isValStr = string(value);
9216 var fieldStr, valStr;
9217 var caseInsensitive = false;
9218 var notExpr = false;
9219 var isIneqCmp = false;
9220 if (operator.indexOf('!') >= 0) {
9221 operator = operator.replace('!', '');
9222 notExpr = true;
9223 }
9224 if (operator.indexOf('@') >= 0) {
9225 operator = operator.replace('@', '');
9226 caseInsensitive = true;
9227 }
9228 if (isFieldStr || isValStr || caseInsensitive) {
9229 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
9230 valStr = '' + value;
9231 }
9232
9233 // if we're doing a case insensitive comparison, then we're using a STRING comparison
9234 // even if we're comparing numbers
9235 if (caseInsensitive) {
9236 fieldVal = fieldStr = fieldStr.toLowerCase();
9237 value = valStr = valStr.toLowerCase();
9238 }
9239 switch (operator) {
9240 case '*=':
9241 matches = fieldStr.indexOf(valStr) >= 0;
9242 break;
9243 case '$=':
9244 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
9245 break;
9246 case '^=':
9247 matches = fieldStr.indexOf(valStr) === 0;
9248 break;
9249 case '=':
9250 matches = fieldVal === value;
9251 break;
9252 case '>':
9253 isIneqCmp = true;
9254 matches = fieldVal > value;
9255 break;
9256 case '>=':
9257 isIneqCmp = true;
9258 matches = fieldVal >= value;
9259 break;
9260 case '<':
9261 isIneqCmp = true;
9262 matches = fieldVal < value;
9263 break;
9264 case '<=':
9265 isIneqCmp = true;
9266 matches = fieldVal <= value;
9267 break;
9268 default:
9269 matches = false;
9270 break;
9271 }
9272
9273 // apply the not op, but null vals for inequalities should always stay non-matching
9274 if (notExpr && (fieldVal != null || !isIneqCmp)) {
9275 matches = !matches;
9276 }
9277 return matches;
9278};
9279var boolCmp = function boolCmp(fieldVal, operator) {
9280 switch (operator) {
9281 case '?':
9282 return fieldVal ? true : false;
9283 case '!':
9284 return fieldVal ? false : true;
9285 case '^':
9286 return fieldVal === undefined;
9287 }
9288};
9289var existCmp = function existCmp(fieldVal) {
9290 return fieldVal !== undefined;
9291};
9292var data$1 = function data(ele, field) {
9293 return ele.data(field);
9294};
9295var meta = function meta(ele, field) {
9296 return ele[field]();
9297};
9298
9299/** A lookup of `match(check, ele)` functions by `Type` int */
9300var match = [];
9301
9302/**
9303 * Returns whether the query matches for the element
9304 * @param query The `{ type, value, ... }` query object
9305 * @param ele The element to compare against
9306*/
9307var matches$1 = function matches(query, ele) {
9308 return query.checks.every(function (chk) {
9309 return match[chk.type](chk, ele);
9310 });
9311};
9312match[Type.GROUP] = function (check, ele) {
9313 var group = check.value;
9314 return group === '*' || group === ele.group();
9315};
9316match[Type.STATE] = function (check, ele) {
9317 var stateSelector = check.value;
9318 return stateSelectorMatches(stateSelector, ele);
9319};
9320match[Type.ID] = function (check, ele) {
9321 var id = check.value;
9322 return ele.id() === id;
9323};
9324match[Type.CLASS] = function (check, ele) {
9325 var cls = check.value;
9326 return ele.hasClass(cls);
9327};
9328match[Type.META_COMPARE] = function (check, ele) {
9329 var field = check.field,
9330 operator = check.operator,
9331 value = check.value;
9332 return valCmp(meta(ele, field), operator, value);
9333};
9334match[Type.DATA_COMPARE] = function (check, ele) {
9335 var field = check.field,
9336 operator = check.operator,
9337 value = check.value;
9338 return valCmp(data$1(ele, field), operator, value);
9339};
9340match[Type.DATA_BOOL] = function (check, ele) {
9341 var field = check.field,
9342 operator = check.operator;
9343 return boolCmp(data$1(ele, field), operator);
9344};
9345match[Type.DATA_EXIST] = function (check, ele) {
9346 var field = check.field;
9347 check.operator;
9348 return existCmp(data$1(ele, field));
9349};
9350match[Type.UNDIRECTED_EDGE] = function (check, ele) {
9351 var qA = check.nodes[0];
9352 var qB = check.nodes[1];
9353 var src = ele.source();
9354 var tgt = ele.target();
9355 return matches$1(qA, src) && matches$1(qB, tgt) || matches$1(qB, src) && matches$1(qA, tgt);
9356};
9357match[Type.NODE_NEIGHBOR] = function (check, ele) {
9358 return matches$1(check.node, ele) && ele.neighborhood().some(function (n) {
9359 return n.isNode() && matches$1(check.neighbor, n);
9360 });
9361};
9362match[Type.DIRECTED_EDGE] = function (check, ele) {
9363 return matches$1(check.source, ele.source()) && matches$1(check.target, ele.target());
9364};
9365match[Type.NODE_SOURCE] = function (check, ele) {
9366 return matches$1(check.source, ele) && ele.outgoers().some(function (n) {
9367 return n.isNode() && matches$1(check.target, n);
9368 });
9369};
9370match[Type.NODE_TARGET] = function (check, ele) {
9371 return matches$1(check.target, ele) && ele.incomers().some(function (n) {
9372 return n.isNode() && matches$1(check.source, n);
9373 });
9374};
9375match[Type.CHILD] = function (check, ele) {
9376 return matches$1(check.child, ele) && matches$1(check.parent, ele.parent());
9377};
9378match[Type.PARENT] = function (check, ele) {
9379 return matches$1(check.parent, ele) && ele.children().some(function (c) {
9380 return matches$1(check.child, c);
9381 });
9382};
9383match[Type.DESCENDANT] = function (check, ele) {
9384 return matches$1(check.descendant, ele) && ele.ancestors().some(function (a) {
9385 return matches$1(check.ancestor, a);
9386 });
9387};
9388match[Type.ANCESTOR] = function (check, ele) {
9389 return matches$1(check.ancestor, ele) && ele.descendants().some(function (d) {
9390 return matches$1(check.descendant, d);
9391 });
9392};
9393match[Type.COMPOUND_SPLIT] = function (check, ele) {
9394 return matches$1(check.subject, ele) && matches$1(check.left, ele) && matches$1(check.right, ele);
9395};
9396match[Type.TRUE] = function () {
9397 return true;
9398};
9399match[Type.COLLECTION] = function (check, ele) {
9400 var collection = check.value;
9401 return collection.has(ele);
9402};
9403match[Type.FILTER] = function (check, ele) {
9404 var filter = check.value;
9405 return filter(ele);
9406};
9407
9408// filter an existing collection
9409var filter = function filter(collection) {
9410 var self = this;
9411
9412 // for 1 id #foo queries, just get the element
9413 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
9414 return collection.getElementById(self[0].checks[0].value).collection();
9415 }
9416 var selectorFunction = function selectorFunction(element) {
9417 for (var j = 0; j < self.length; j++) {
9418 var query = self[j];
9419 if (matches$1(query, element)) {
9420 return true;
9421 }
9422 }
9423 return false;
9424 };
9425 if (self.text() == null) {
9426 selectorFunction = function selectorFunction() {
9427 return true;
9428 };
9429 }
9430 return collection.filter(selectorFunction);
9431}; // filter
9432
9433// does selector match a single element?
9434var matches = function matches(ele) {
9435 var self = this;
9436 for (var j = 0; j < self.length; j++) {
9437 var query = self[j];
9438 if (matches$1(query, ele)) {
9439 return true;
9440 }
9441 }
9442 return false;
9443}; // matches
9444
9445var matching = {
9446 matches: matches,
9447 filter: filter
9448};
9449
9450var Selector = function Selector(selector) {
9451 this.inputText = selector;
9452 this.currentSubject = null;
9453 this.compoundCount = 0;
9454 this.edgeCount = 0;
9455 this.length = 0;
9456 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
9457 this.addQuery({
9458 checks: [{
9459 type: Type.COLLECTION,
9460 value: selector.collection()
9461 }]
9462 });
9463 } else if (fn$6(selector)) {
9464 this.addQuery({
9465 checks: [{
9466 type: Type.FILTER,
9467 value: selector
9468 }]
9469 });
9470 } else if (string(selector)) {
9471 if (!this.parse(selector)) {
9472 this.invalid = true;
9473 }
9474 } else {
9475 error('A selector must be created from a string; found ');
9476 }
9477};
9478var selfn = Selector.prototype;
9479[parse$1, matching].forEach(function (p) {
9480 return extend(selfn, p);
9481});
9482selfn.text = function () {
9483 return this.inputText;
9484};
9485selfn.size = function () {
9486 return this.length;
9487};
9488selfn.eq = function (i) {
9489 return this[i];
9490};
9491selfn.sameText = function (otherSel) {
9492 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
9493};
9494selfn.addQuery = function (q) {
9495 this[this.length++] = q;
9496};
9497selfn.selector = selfn.toString;
9498
9499var elesfn$g = {
9500 allAre: function allAre(selector) {
9501 var selObj = new Selector(selector);
9502 return this.every(function (ele) {
9503 return selObj.matches(ele);
9504 });
9505 },
9506 is: function is(selector) {
9507 var selObj = new Selector(selector);
9508 return this.some(function (ele) {
9509 return selObj.matches(ele);
9510 });
9511 },
9512 some: function some(fn, thisArg) {
9513 for (var i = 0; i < this.length; i++) {
9514 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9515 if (ret) {
9516 return true;
9517 }
9518 }
9519 return false;
9520 },
9521 every: function every(fn, thisArg) {
9522 for (var i = 0; i < this.length; i++) {
9523 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9524 if (!ret) {
9525 return false;
9526 }
9527 }
9528 return true;
9529 },
9530 same: function same(collection) {
9531 // cheap collection ref check
9532 if (this === collection) {
9533 return true;
9534 }
9535 collection = this.cy().collection(collection);
9536 var thisLength = this.length;
9537 var collectionLength = collection.length;
9538
9539 // cheap length check
9540 if (thisLength !== collectionLength) {
9541 return false;
9542 }
9543
9544 // cheap element ref check
9545 if (thisLength === 1) {
9546 return this[0] === collection[0];
9547 }
9548 return this.every(function (ele) {
9549 return collection.hasElementWithId(ele.id());
9550 });
9551 },
9552 anySame: function anySame(collection) {
9553 collection = this.cy().collection(collection);
9554 return this.some(function (ele) {
9555 return collection.hasElementWithId(ele.id());
9556 });
9557 },
9558 allAreNeighbors: function allAreNeighbors(collection) {
9559 collection = this.cy().collection(collection);
9560 var nhood = this.neighborhood();
9561 return collection.every(function (ele) {
9562 return nhood.hasElementWithId(ele.id());
9563 });
9564 },
9565 contains: function contains(collection) {
9566 collection = this.cy().collection(collection);
9567 var self = this;
9568 return collection.every(function (ele) {
9569 return self.hasElementWithId(ele.id());
9570 });
9571 }
9572};
9573elesfn$g.allAreNeighbours = elesfn$g.allAreNeighbors;
9574elesfn$g.has = elesfn$g.contains;
9575elesfn$g.equal = elesfn$g.equals = elesfn$g.same;
9576
9577var cache = function cache(fn, name) {
9578 return function traversalCache(arg1, arg2, arg3, arg4) {
9579 var selectorOrEles = arg1;
9580 var eles = this;
9581 var key;
9582 if (selectorOrEles == null) {
9583 key = '';
9584 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
9585 key = selectorOrEles.id();
9586 }
9587 if (eles.length === 1 && key) {
9588 var _p = eles[0]._private;
9589 var tch = _p.traversalCache = _p.traversalCache || {};
9590 var ch = tch[name] = tch[name] || [];
9591 var hash = hashString(key);
9592 var cacheHit = ch[hash];
9593 if (cacheHit) {
9594 return cacheHit;
9595 } else {
9596 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
9597 }
9598 } else {
9599 return fn.call(eles, arg1, arg2, arg3, arg4);
9600 }
9601 };
9602};
9603
9604var elesfn$f = {
9605 parent: function parent(selector) {
9606 var parents = [];
9607
9608 // optimisation for single ele call
9609 if (this.length === 1) {
9610 var parent = this[0]._private.parent;
9611 if (parent) {
9612 return parent;
9613 }
9614 }
9615 for (var i = 0; i < this.length; i++) {
9616 var ele = this[i];
9617 var _parent = ele._private.parent;
9618 if (_parent) {
9619 parents.push(_parent);
9620 }
9621 }
9622 return this.spawn(parents, true).filter(selector);
9623 },
9624 parents: function parents(selector) {
9625 var parents = [];
9626 var eles = this.parent();
9627 while (eles.nonempty()) {
9628 for (var i = 0; i < eles.length; i++) {
9629 var ele = eles[i];
9630 parents.push(ele);
9631 }
9632 eles = eles.parent();
9633 }
9634 return this.spawn(parents, true).filter(selector);
9635 },
9636 commonAncestors: function commonAncestors(selector) {
9637 var ancestors;
9638 for (var i = 0; i < this.length; i++) {
9639 var ele = this[i];
9640 var parents = ele.parents();
9641 ancestors = ancestors || parents;
9642 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
9643 }
9644
9645 return ancestors.filter(selector);
9646 },
9647 orphans: function orphans(selector) {
9648 return this.stdFilter(function (ele) {
9649 return ele.isOrphan();
9650 }).filter(selector);
9651 },
9652 nonorphans: function nonorphans(selector) {
9653 return this.stdFilter(function (ele) {
9654 return ele.isChild();
9655 }).filter(selector);
9656 },
9657 children: cache(function (selector) {
9658 var children = [];
9659 for (var i = 0; i < this.length; i++) {
9660 var ele = this[i];
9661 var eleChildren = ele._private.children;
9662 for (var j = 0; j < eleChildren.length; j++) {
9663 children.push(eleChildren[j]);
9664 }
9665 }
9666 return this.spawn(children, true).filter(selector);
9667 }, 'children'),
9668 siblings: function siblings(selector) {
9669 return this.parent().children().not(this).filter(selector);
9670 },
9671 isParent: function isParent() {
9672 var ele = this[0];
9673 if (ele) {
9674 return ele.isNode() && ele._private.children.length !== 0;
9675 }
9676 },
9677 isChildless: function isChildless() {
9678 var ele = this[0];
9679 if (ele) {
9680 return ele.isNode() && ele._private.children.length === 0;
9681 }
9682 },
9683 isChild: function isChild() {
9684 var ele = this[0];
9685 if (ele) {
9686 return ele.isNode() && ele._private.parent != null;
9687 }
9688 },
9689 isOrphan: function isOrphan() {
9690 var ele = this[0];
9691 if (ele) {
9692 return ele.isNode() && ele._private.parent == null;
9693 }
9694 },
9695 descendants: function descendants(selector) {
9696 var elements = [];
9697 function add(eles) {
9698 for (var i = 0; i < eles.length; i++) {
9699 var ele = eles[i];
9700 elements.push(ele);
9701 if (ele.children().nonempty()) {
9702 add(ele.children());
9703 }
9704 }
9705 }
9706 add(this.children());
9707 return this.spawn(elements, true).filter(selector);
9708 }
9709};
9710function forEachCompound(eles, fn, includeSelf, recursiveStep) {
9711 var q = [];
9712 var did = new Set$1();
9713 var cy = eles.cy();
9714 var hasCompounds = cy.hasCompoundNodes();
9715 for (var i = 0; i < eles.length; i++) {
9716 var ele = eles[i];
9717 if (includeSelf) {
9718 q.push(ele);
9719 } else if (hasCompounds) {
9720 recursiveStep(q, did, ele);
9721 }
9722 }
9723 while (q.length > 0) {
9724 var _ele = q.shift();
9725 fn(_ele);
9726 did.add(_ele.id());
9727 if (hasCompounds) {
9728 recursiveStep(q, did, _ele);
9729 }
9730 }
9731 return eles;
9732}
9733function addChildren(q, did, ele) {
9734 if (ele.isParent()) {
9735 var children = ele._private.children;
9736 for (var i = 0; i < children.length; i++) {
9737 var child = children[i];
9738 if (!did.has(child.id())) {
9739 q.push(child);
9740 }
9741 }
9742 }
9743}
9744
9745// very efficient version of eles.add( eles.descendants() ).forEach()
9746// for internal use
9747elesfn$f.forEachDown = function (fn) {
9748 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9749 return forEachCompound(this, fn, includeSelf, addChildren);
9750};
9751function addParent(q, did, ele) {
9752 if (ele.isChild()) {
9753 var parent = ele._private.parent;
9754 if (!did.has(parent.id())) {
9755 q.push(parent);
9756 }
9757 }
9758}
9759elesfn$f.forEachUp = function (fn) {
9760 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9761 return forEachCompound(this, fn, includeSelf, addParent);
9762};
9763function addParentAndChildren(q, did, ele) {
9764 addParent(q, did, ele);
9765 addChildren(q, did, ele);
9766}
9767elesfn$f.forEachUpAndDown = function (fn) {
9768 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9769 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
9770};
9771
9772// aliases
9773elesfn$f.ancestors = elesfn$f.parents;
9774
9775var fn$5, elesfn$e;
9776fn$5 = elesfn$e = {
9777 data: define.data({
9778 field: 'data',
9779 bindingEvent: 'data',
9780 allowBinding: true,
9781 allowSetting: true,
9782 settingEvent: 'data',
9783 settingTriggersEvent: true,
9784 triggerFnName: 'trigger',
9785 allowGetting: true,
9786 immutableKeys: {
9787 'id': true,
9788 'source': true,
9789 'target': true,
9790 'parent': true
9791 },
9792 updateStyle: true
9793 }),
9794 removeData: define.removeData({
9795 field: 'data',
9796 event: 'data',
9797 triggerFnName: 'trigger',
9798 triggerEvent: true,
9799 immutableKeys: {
9800 'id': true,
9801 'source': true,
9802 'target': true,
9803 'parent': true
9804 },
9805 updateStyle: true
9806 }),
9807 scratch: define.data({
9808 field: 'scratch',
9809 bindingEvent: 'scratch',
9810 allowBinding: true,
9811 allowSetting: true,
9812 settingEvent: 'scratch',
9813 settingTriggersEvent: true,
9814 triggerFnName: 'trigger',
9815 allowGetting: true,
9816 updateStyle: true
9817 }),
9818 removeScratch: define.removeData({
9819 field: 'scratch',
9820 event: 'scratch',
9821 triggerFnName: 'trigger',
9822 triggerEvent: true,
9823 updateStyle: true
9824 }),
9825 rscratch: define.data({
9826 field: 'rscratch',
9827 allowBinding: false,
9828 allowSetting: true,
9829 settingTriggersEvent: false,
9830 allowGetting: true
9831 }),
9832 removeRscratch: define.removeData({
9833 field: 'rscratch',
9834 triggerEvent: false
9835 }),
9836 id: function id() {
9837 var ele = this[0];
9838 if (ele) {
9839 return ele._private.data.id;
9840 }
9841 }
9842};
9843
9844// aliases
9845fn$5.attr = fn$5.data;
9846fn$5.removeAttr = fn$5.removeData;
9847var data = elesfn$e;
9848
9849var elesfn$d = {};
9850function defineDegreeFunction(callback) {
9851 return function (includeLoops) {
9852 var self = this;
9853 if (includeLoops === undefined) {
9854 includeLoops = true;
9855 }
9856 if (self.length === 0) {
9857 return;
9858 }
9859 if (self.isNode() && !self.removed()) {
9860 var degree = 0;
9861 var node = self[0];
9862 var connectedEdges = node._private.edges;
9863 for (var i = 0; i < connectedEdges.length; i++) {
9864 var edge = connectedEdges[i];
9865 if (!includeLoops && edge.isLoop()) {
9866 continue;
9867 }
9868 degree += callback(node, edge);
9869 }
9870 return degree;
9871 } else {
9872 return;
9873 }
9874 };
9875}
9876extend(elesfn$d, {
9877 degree: defineDegreeFunction(function (node, edge) {
9878 if (edge.source().same(edge.target())) {
9879 return 2;
9880 } else {
9881 return 1;
9882 }
9883 }),
9884 indegree: defineDegreeFunction(function (node, edge) {
9885 if (edge.target().same(node)) {
9886 return 1;
9887 } else {
9888 return 0;
9889 }
9890 }),
9891 outdegree: defineDegreeFunction(function (node, edge) {
9892 if (edge.source().same(node)) {
9893 return 1;
9894 } else {
9895 return 0;
9896 }
9897 })
9898});
9899function defineDegreeBoundsFunction(degreeFn, callback) {
9900 return function (includeLoops) {
9901 var ret;
9902 var nodes = this.nodes();
9903 for (var i = 0; i < nodes.length; i++) {
9904 var ele = nodes[i];
9905 var degree = ele[degreeFn](includeLoops);
9906 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
9907 ret = degree;
9908 }
9909 }
9910 return ret;
9911 };
9912}
9913extend(elesfn$d, {
9914 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
9915 return degree < min;
9916 }),
9917 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
9918 return degree > max;
9919 }),
9920 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
9921 return degree < min;
9922 }),
9923 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
9924 return degree > max;
9925 }),
9926 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
9927 return degree < min;
9928 }),
9929 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
9930 return degree > max;
9931 })
9932});
9933extend(elesfn$d, {
9934 totalDegree: function totalDegree(includeLoops) {
9935 var total = 0;
9936 var nodes = this.nodes();
9937 for (var i = 0; i < nodes.length; i++) {
9938 total += nodes[i].degree(includeLoops);
9939 }
9940 return total;
9941 }
9942});
9943
9944var fn$4, elesfn$c;
9945var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
9946 for (var i = 0; i < eles.length; i++) {
9947 var ele = eles[i];
9948 if (!ele.locked()) {
9949 var oldPos = ele._private.position;
9950 var delta = {
9951 x: newPos.x != null ? newPos.x - oldPos.x : 0,
9952 y: newPos.y != null ? newPos.y - oldPos.y : 0
9953 };
9954 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
9955 ele.children().shift(delta, silent);
9956 }
9957 ele.dirtyBoundingBoxCache();
9958 }
9959 }
9960};
9961var positionDef = {
9962 field: 'position',
9963 bindingEvent: 'position',
9964 allowBinding: true,
9965 allowSetting: true,
9966 settingEvent: 'position',
9967 settingTriggersEvent: true,
9968 triggerFnName: 'emitAndNotify',
9969 allowGetting: true,
9970 validKeys: ['x', 'y'],
9971 beforeGet: function beforeGet(ele) {
9972 ele.updateCompoundBounds();
9973 },
9974 beforeSet: function beforeSet(eles, newPos) {
9975 beforePositionSet(eles, newPos, false);
9976 },
9977 onSet: function onSet(eles) {
9978 eles.dirtyCompoundBoundsCache();
9979 },
9980 canSet: function canSet(ele) {
9981 return !ele.locked();
9982 }
9983};
9984fn$4 = elesfn$c = {
9985 position: define.data(positionDef),
9986 // position but no notification to renderer
9987 silentPosition: define.data(extend({}, positionDef, {
9988 allowBinding: false,
9989 allowSetting: true,
9990 settingTriggersEvent: false,
9991 allowGetting: false,
9992 beforeSet: function beforeSet(eles, newPos) {
9993 beforePositionSet(eles, newPos, true);
9994 },
9995 onSet: function onSet(eles) {
9996 eles.dirtyCompoundBoundsCache();
9997 }
9998 })),
9999 positions: function positions(pos, silent) {
10000 if (plainObject(pos)) {
10001 if (silent) {
10002 this.silentPosition(pos);
10003 } else {
10004 this.position(pos);
10005 }
10006 } else if (fn$6(pos)) {
10007 var _fn = pos;
10008 var cy = this.cy();
10009 cy.startBatch();
10010 for (var i = 0; i < this.length; i++) {
10011 var ele = this[i];
10012 var _pos = void 0;
10013 if (_pos = _fn(ele, i)) {
10014 if (silent) {
10015 ele.silentPosition(_pos);
10016 } else {
10017 ele.position(_pos);
10018 }
10019 }
10020 }
10021 cy.endBatch();
10022 }
10023 return this; // chaining
10024 },
10025
10026 silentPositions: function silentPositions(pos) {
10027 return this.positions(pos, true);
10028 },
10029 shift: function shift(dim, val, silent) {
10030 var delta;
10031 if (plainObject(dim)) {
10032 delta = {
10033 x: number$1(dim.x) ? dim.x : 0,
10034 y: number$1(dim.y) ? dim.y : 0
10035 };
10036 silent = val;
10037 } else if (string(dim) && number$1(val)) {
10038 delta = {
10039 x: 0,
10040 y: 0
10041 };
10042 delta[dim] = val;
10043 }
10044 if (delta != null) {
10045 var cy = this.cy();
10046 cy.startBatch();
10047 for (var i = 0; i < this.length; i++) {
10048 var ele = this[i];
10049
10050 // exclude any node that is a descendant of the calling collection
10051 if (cy.hasCompoundNodes() && ele.isChild() && ele.ancestors().anySame(this)) {
10052 continue;
10053 }
10054 var pos = ele.position();
10055 var newPos = {
10056 x: pos.x + delta.x,
10057 y: pos.y + delta.y
10058 };
10059 if (silent) {
10060 ele.silentPosition(newPos);
10061 } else {
10062 ele.position(newPos);
10063 }
10064 }
10065 cy.endBatch();
10066 }
10067 return this;
10068 },
10069 silentShift: function silentShift(dim, val) {
10070 if (plainObject(dim)) {
10071 this.shift(dim, true);
10072 } else if (string(dim) && number$1(val)) {
10073 this.shift(dim, val, true);
10074 }
10075 return this;
10076 },
10077 // get/set the rendered (i.e. on screen) positon of the element
10078 renderedPosition: function renderedPosition(dim, val) {
10079 var ele = this[0];
10080 var cy = this.cy();
10081 var zoom = cy.zoom();
10082 var pan = cy.pan();
10083 var rpos = plainObject(dim) ? dim : undefined;
10084 var setting = rpos !== undefined || val !== undefined && string(dim);
10085 if (ele && ele.isNode()) {
10086 // must have an element and must be a node to return position
10087 if (setting) {
10088 for (var i = 0; i < this.length; i++) {
10089 var _ele = this[i];
10090 if (val !== undefined) {
10091 // set one dimension
10092 _ele.position(dim, (val - pan[dim]) / zoom);
10093 } else if (rpos !== undefined) {
10094 // set whole position
10095 _ele.position(renderedToModelPosition(rpos, zoom, pan));
10096 }
10097 }
10098 } else {
10099 // getting
10100 var pos = ele.position();
10101 rpos = modelToRenderedPosition(pos, zoom, pan);
10102 if (dim === undefined) {
10103 // then return the whole rendered position
10104 return rpos;
10105 } else {
10106 // then return the specified dimension
10107 return rpos[dim];
10108 }
10109 }
10110 } else if (!setting) {
10111 return undefined; // for empty collection case
10112 }
10113
10114 return this; // chaining
10115 },
10116
10117 // get/set the position relative to the parent
10118 relativePosition: function relativePosition(dim, val) {
10119 var ele = this[0];
10120 var cy = this.cy();
10121 var ppos = plainObject(dim) ? dim : undefined;
10122 var setting = ppos !== undefined || val !== undefined && string(dim);
10123 var hasCompoundNodes = cy.hasCompoundNodes();
10124 if (ele && ele.isNode()) {
10125 // must have an element and must be a node to return position
10126 if (setting) {
10127 for (var i = 0; i < this.length; i++) {
10128 var _ele2 = this[i];
10129 var parent = hasCompoundNodes ? _ele2.parent() : null;
10130 var hasParent = parent && parent.length > 0;
10131 var relativeToParent = hasParent;
10132 if (hasParent) {
10133 parent = parent[0];
10134 }
10135 var origin = relativeToParent ? parent.position() : {
10136 x: 0,
10137 y: 0
10138 };
10139 if (val !== undefined) {
10140 // set one dimension
10141 _ele2.position(dim, val + origin[dim]);
10142 } else if (ppos !== undefined) {
10143 // set whole position
10144 _ele2.position({
10145 x: ppos.x + origin.x,
10146 y: ppos.y + origin.y
10147 });
10148 }
10149 }
10150 } else {
10151 // getting
10152 var pos = ele.position();
10153 var _parent = hasCompoundNodes ? ele.parent() : null;
10154 var _hasParent = _parent && _parent.length > 0;
10155 var _relativeToParent = _hasParent;
10156 if (_hasParent) {
10157 _parent = _parent[0];
10158 }
10159 var _origin = _relativeToParent ? _parent.position() : {
10160 x: 0,
10161 y: 0
10162 };
10163 ppos = {
10164 x: pos.x - _origin.x,
10165 y: pos.y - _origin.y
10166 };
10167 if (dim === undefined) {
10168 // then return the whole rendered position
10169 return ppos;
10170 } else {
10171 // then return the specified dimension
10172 return ppos[dim];
10173 }
10174 }
10175 } else if (!setting) {
10176 return undefined; // for empty collection case
10177 }
10178
10179 return this; // chaining
10180 }
10181};
10182
10183// aliases
10184fn$4.modelPosition = fn$4.point = fn$4.position;
10185fn$4.modelPositions = fn$4.points = fn$4.positions;
10186fn$4.renderedPoint = fn$4.renderedPosition;
10187fn$4.relativePoint = fn$4.relativePosition;
10188var position = elesfn$c;
10189
10190var fn$3, elesfn$b;
10191fn$3 = elesfn$b = {};
10192elesfn$b.renderedBoundingBox = function (options) {
10193 var bb = this.boundingBox(options);
10194 var cy = this.cy();
10195 var zoom = cy.zoom();
10196 var pan = cy.pan();
10197 var x1 = bb.x1 * zoom + pan.x;
10198 var x2 = bb.x2 * zoom + pan.x;
10199 var y1 = bb.y1 * zoom + pan.y;
10200 var y2 = bb.y2 * zoom + pan.y;
10201 return {
10202 x1: x1,
10203 x2: x2,
10204 y1: y1,
10205 y2: y2,
10206 w: x2 - x1,
10207 h: y2 - y1
10208 };
10209};
10210elesfn$b.dirtyCompoundBoundsCache = function () {
10211 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
10212 var cy = this.cy();
10213 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
10214 return this;
10215 }
10216 this.forEachUp(function (ele) {
10217 if (ele.isParent()) {
10218 var _p = ele._private;
10219 _p.compoundBoundsClean = false;
10220 _p.bbCache = null;
10221 if (!silent) {
10222 ele.emitAndNotify('bounds');
10223 }
10224 }
10225 });
10226 return this;
10227};
10228elesfn$b.updateCompoundBounds = function () {
10229 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
10230 var cy = this.cy();
10231
10232 // not possible to do on non-compound graphs or with the style disabled
10233 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
10234 return this;
10235 }
10236
10237 // save cycles when batching -- but bounds will be stale (or not exist yet)
10238 if (!force && cy.batching()) {
10239 return this;
10240 }
10241 function update(parent) {
10242 if (!parent.isParent()) {
10243 return;
10244 }
10245 var _p = parent._private;
10246 var children = parent.children();
10247 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
10248 var min = {
10249 width: {
10250 val: parent.pstyle('min-width').pfValue,
10251 left: parent.pstyle('min-width-bias-left'),
10252 right: parent.pstyle('min-width-bias-right')
10253 },
10254 height: {
10255 val: parent.pstyle('min-height').pfValue,
10256 top: parent.pstyle('min-height-bias-top'),
10257 bottom: parent.pstyle('min-height-bias-bottom')
10258 }
10259 };
10260 var bb = children.boundingBox({
10261 includeLabels: includeLabels,
10262 includeOverlays: false,
10263 // updating the compound bounds happens outside of the regular
10264 // cache cycle (i.e. before fired events)
10265 useCache: false
10266 });
10267 var pos = _p.position;
10268
10269 // if children take up zero area then keep position and fall back on stylesheet w/h
10270 if (bb.w === 0 || bb.h === 0) {
10271 bb = {
10272 w: parent.pstyle('width').pfValue,
10273 h: parent.pstyle('height').pfValue
10274 };
10275 bb.x1 = pos.x - bb.w / 2;
10276 bb.x2 = pos.x + bb.w / 2;
10277 bb.y1 = pos.y - bb.h / 2;
10278 bb.y2 = pos.y + bb.h / 2;
10279 }
10280 function computeBiasValues(propDiff, propBias, propBiasComplement) {
10281 var biasDiff = 0;
10282 var biasComplementDiff = 0;
10283 var biasTotal = propBias + propBiasComplement;
10284 if (propDiff > 0 && biasTotal > 0) {
10285 biasDiff = propBias / biasTotal * propDiff;
10286 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
10287 }
10288 return {
10289 biasDiff: biasDiff,
10290 biasComplementDiff: biasComplementDiff
10291 };
10292 }
10293 function computePaddingValues(width, height, paddingObject, relativeTo) {
10294 // Assuming percentage is number from 0 to 1
10295 if (paddingObject.units === '%') {
10296 switch (relativeTo) {
10297 case 'width':
10298 return width > 0 ? paddingObject.pfValue * width : 0;
10299 case 'height':
10300 return height > 0 ? paddingObject.pfValue * height : 0;
10301 case 'average':
10302 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
10303 case 'min':
10304 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
10305 case 'max':
10306 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
10307 default:
10308 return 0;
10309 }
10310 } else if (paddingObject.units === 'px') {
10311 return paddingObject.pfValue;
10312 } else {
10313 return 0;
10314 }
10315 }
10316 var leftVal = min.width.left.value;
10317 if (min.width.left.units === 'px' && min.width.val > 0) {
10318 leftVal = leftVal * 100 / min.width.val;
10319 }
10320 var rightVal = min.width.right.value;
10321 if (min.width.right.units === 'px' && min.width.val > 0) {
10322 rightVal = rightVal * 100 / min.width.val;
10323 }
10324 var topVal = min.height.top.value;
10325 if (min.height.top.units === 'px' && min.height.val > 0) {
10326 topVal = topVal * 100 / min.height.val;
10327 }
10328 var bottomVal = min.height.bottom.value;
10329 if (min.height.bottom.units === 'px' && min.height.val > 0) {
10330 bottomVal = bottomVal * 100 / min.height.val;
10331 }
10332 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
10333 var diffLeft = widthBiasDiffs.biasDiff;
10334 var diffRight = widthBiasDiffs.biasComplementDiff;
10335 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
10336 var diffTop = heightBiasDiffs.biasDiff;
10337 var diffBottom = heightBiasDiffs.biasComplementDiff;
10338 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
10339 _p.autoWidth = Math.max(bb.w, min.width.val);
10340 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
10341 _p.autoHeight = Math.max(bb.h, min.height.val);
10342 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
10343 }
10344 for (var i = 0; i < this.length; i++) {
10345 var ele = this[i];
10346 var _p = ele._private;
10347 if (!_p.compoundBoundsClean || force) {
10348 update(ele);
10349 if (!cy.batching()) {
10350 _p.compoundBoundsClean = true;
10351 }
10352 }
10353 }
10354 return this;
10355};
10356var noninf = function noninf(x) {
10357 if (x === Infinity || x === -Infinity) {
10358 return 0;
10359 }
10360 return x;
10361};
10362var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
10363 // don't update with zero area boxes
10364 if (x2 - x1 === 0 || y2 - y1 === 0) {
10365 return;
10366 }
10367
10368 // don't update with null dim
10369 if (x1 == null || y1 == null || x2 == null || y2 == null) {
10370 return;
10371 }
10372 b.x1 = x1 < b.x1 ? x1 : b.x1;
10373 b.x2 = x2 > b.x2 ? x2 : b.x2;
10374 b.y1 = y1 < b.y1 ? y1 : b.y1;
10375 b.y2 = y2 > b.y2 ? y2 : b.y2;
10376 b.w = b.x2 - b.x1;
10377 b.h = b.y2 - b.y1;
10378};
10379var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
10380 if (b2 == null) {
10381 return b;
10382 }
10383 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
10384};
10385var prefixedProperty = function prefixedProperty(obj, field, prefix) {
10386 return getPrefixedProperty(obj, field, prefix);
10387};
10388var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
10389 if (ele.cy().headless()) {
10390 return;
10391 }
10392 var _p = ele._private;
10393 var rstyle = _p.rstyle;
10394 var halfArW = rstyle.arrowWidth / 2;
10395 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
10396 var x;
10397 var y;
10398 if (arrowType !== 'none') {
10399 if (prefix === 'source') {
10400 x = rstyle.srcX;
10401 y = rstyle.srcY;
10402 } else if (prefix === 'target') {
10403 x = rstyle.tgtX;
10404 y = rstyle.tgtY;
10405 } else {
10406 x = rstyle.midX;
10407 y = rstyle.midY;
10408 }
10409
10410 // always store the individual arrow bounds
10411 var bbs = _p.arrowBounds = _p.arrowBounds || {};
10412 var bb = bbs[prefix] = bbs[prefix] || {};
10413 bb.x1 = x - halfArW;
10414 bb.y1 = y - halfArW;
10415 bb.x2 = x + halfArW;
10416 bb.y2 = y + halfArW;
10417 bb.w = bb.x2 - bb.x1;
10418 bb.h = bb.y2 - bb.y1;
10419 expandBoundingBox(bb, 1);
10420 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
10421 }
10422};
10423var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
10424 if (ele.cy().headless()) {
10425 return;
10426 }
10427 var prefixDash;
10428 if (prefix) {
10429 prefixDash = prefix + '-';
10430 } else {
10431 prefixDash = '';
10432 }
10433 var _p = ele._private;
10434 var rstyle = _p.rstyle;
10435 var label = ele.pstyle(prefixDash + 'label').strValue;
10436 if (label) {
10437 var halign = ele.pstyle('text-halign');
10438 var valign = ele.pstyle('text-valign');
10439 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
10440 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
10441 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
10442 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
10443 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
10444 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
10445 var isEdge = ele.isEdge();
10446 var rotation = ele.pstyle(prefixDash + 'text-rotation');
10447 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
10448 var borderWidth = ele.pstyle('text-border-width').pfValue;
10449 var halfBorderWidth = borderWidth / 2;
10450 var padding = ele.pstyle('text-background-padding').pfValue;
10451 var marginOfError = 2; // expand to work around browser dimension inaccuracies
10452
10453 var lh = labelHeight;
10454 var lw = labelWidth;
10455 var lw_2 = lw / 2;
10456 var lh_2 = lh / 2;
10457 var lx1, lx2, ly1, ly2;
10458 if (isEdge) {
10459 lx1 = labelX - lw_2;
10460 lx2 = labelX + lw_2;
10461 ly1 = labelY - lh_2;
10462 ly2 = labelY + lh_2;
10463 } else {
10464 switch (halign.value) {
10465 case 'left':
10466 lx1 = labelX - lw;
10467 lx2 = labelX;
10468 break;
10469 case 'center':
10470 lx1 = labelX - lw_2;
10471 lx2 = labelX + lw_2;
10472 break;
10473 case 'right':
10474 lx1 = labelX;
10475 lx2 = labelX + lw;
10476 break;
10477 }
10478 switch (valign.value) {
10479 case 'top':
10480 ly1 = labelY - lh;
10481 ly2 = labelY;
10482 break;
10483 case 'center':
10484 ly1 = labelY - lh_2;
10485 ly2 = labelY + lh_2;
10486 break;
10487 case 'bottom':
10488 ly1 = labelY;
10489 ly2 = labelY + lh;
10490 break;
10491 }
10492 }
10493
10494 // shift by margin and expand by outline and border
10495 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
10496 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
10497 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
10498 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
10499
10500 // always store the unrotated label bounds separately
10501 var bbPrefix = prefix || 'main';
10502 var bbs = _p.labelBounds;
10503 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
10504 bb.x1 = lx1;
10505 bb.y1 = ly1;
10506 bb.x2 = lx2;
10507 bb.y2 = ly2;
10508 bb.w = lx2 - lx1;
10509 bb.h = ly2 - ly1;
10510 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
10511 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
10512 if (isAutorotate || isPfValue) {
10513 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
10514 var cos = Math.cos(theta);
10515 var sin = Math.sin(theta);
10516
10517 // rotation point (default value for center-center)
10518 var xo = (lx1 + lx2) / 2;
10519 var yo = (ly1 + ly2) / 2;
10520 if (!isEdge) {
10521 switch (halign.value) {
10522 case 'left':
10523 xo = lx2;
10524 break;
10525 case 'right':
10526 xo = lx1;
10527 break;
10528 }
10529 switch (valign.value) {
10530 case 'top':
10531 yo = ly2;
10532 break;
10533 case 'bottom':
10534 yo = ly1;
10535 break;
10536 }
10537 }
10538 var rotate = function rotate(x, y) {
10539 x = x - xo;
10540 y = y - yo;
10541 return {
10542 x: x * cos - y * sin + xo,
10543 y: x * sin + y * cos + yo
10544 };
10545 };
10546 var px1y1 = rotate(lx1, ly1);
10547 var px1y2 = rotate(lx1, ly2);
10548 var px2y1 = rotate(lx2, ly1);
10549 var px2y2 = rotate(lx2, ly2);
10550 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10551 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10552 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10553 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10554 }
10555 var bbPrefixRot = bbPrefix + 'Rot';
10556 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
10557 bbRot.x1 = lx1;
10558 bbRot.y1 = ly1;
10559 bbRot.x2 = lx2;
10560 bbRot.y2 = ly2;
10561 bbRot.w = lx2 - lx1;
10562 bbRot.h = ly2 - ly1;
10563 updateBounds(bounds, lx1, ly1, lx2, ly2);
10564 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
10565 }
10566 return bounds;
10567};
10568var updateBoundsFromOutline = function updateBoundsFromOutline(bounds, ele) {
10569 if (ele.cy().headless()) {
10570 return;
10571 }
10572 var outlineOpacity = ele.pstyle('outline-opacity').value;
10573 var outlineWidth = ele.pstyle('outline-width').value;
10574 if (outlineOpacity > 0 && outlineWidth > 0) {
10575 var outlineOffset = ele.pstyle('outline-offset').value;
10576 var nodeShape = ele.pstyle('shape').value;
10577 var outlineSize = outlineWidth + outlineOffset;
10578 var scaleX = (bounds.w + outlineSize * 2) / bounds.w;
10579 var scaleY = (bounds.h + outlineSize * 2) / bounds.h;
10580 var xOffset = 0;
10581 var yOffset = 0;
10582 if (["diamond", "pentagon", "round-triangle"].includes(nodeShape)) {
10583 scaleX = (bounds.w + outlineSize * 2.4) / bounds.w;
10584 yOffset = -outlineSize / 3.6;
10585 } else if (["concave-hexagon", "rhomboid", "right-rhomboid"].includes(nodeShape)) {
10586 scaleX = (bounds.w + outlineSize * 2.4) / bounds.w;
10587 } else if (nodeShape === "star") {
10588 scaleX = (bounds.w + outlineSize * 2.8) / bounds.w;
10589 scaleY = (bounds.h + outlineSize * 2.6) / bounds.h;
10590 yOffset = -outlineSize / 3.8;
10591 } else if (nodeShape === "triangle") {
10592 scaleX = (bounds.w + outlineSize * 2.8) / bounds.w;
10593 scaleY = (bounds.h + outlineSize * 2.4) / bounds.h;
10594 yOffset = -outlineSize / 1.4;
10595 } else if (nodeShape === "vee") {
10596 scaleX = (bounds.w + outlineSize * 4.4) / bounds.w;
10597 scaleY = (bounds.h + outlineSize * 3.8) / bounds.h;
10598 yOffset = -outlineSize * .5;
10599 }
10600 var hDelta = bounds.h * scaleY - bounds.h;
10601 var wDelta = bounds.w * scaleX - bounds.w;
10602 expandBoundingBoxSides(bounds, [Math.ceil(hDelta / 2), Math.ceil(wDelta / 2)]);
10603 if (xOffset != 0 || yOffset !== 0) {
10604 var oBounds = shiftBoundingBox(bounds, xOffset, yOffset);
10605 updateBoundingBox(bounds, oBounds);
10606 }
10607 }
10608};
10609
10610// get the bounding box of the elements (in raw model position)
10611var boundingBoxImpl = function boundingBoxImpl(ele, options) {
10612 var cy = ele._private.cy;
10613 var styleEnabled = cy.styleEnabled();
10614 var headless = cy.headless();
10615 var bounds = makeBoundingBox();
10616 var _p = ele._private;
10617 var isNode = ele.isNode();
10618 var isEdge = ele.isEdge();
10619 var ex1, ex2, ey1, ey2; // extrema of body / lines
10620 var x, y; // node pos
10621 var rstyle = _p.rstyle;
10622 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0];
10623
10624 // must use `display` prop only, as reading `compound.width()` causes recursion
10625 // (other factors like width values will be considered later in this function anyway)
10626 var isDisplayed = function isDisplayed(ele) {
10627 return ele.pstyle('display').value !== 'none';
10628 };
10629 var displayed = !styleEnabled || isDisplayed(ele)
10630
10631 // must take into account connected nodes b/c of implicit edge hiding on display:none node
10632 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
10633 if (displayed) {
10634 // displayed suffices, since we will find zero area eles anyway
10635 var overlayOpacity = 0;
10636 var overlayPadding = 0;
10637 if (styleEnabled && options.includeOverlays) {
10638 overlayOpacity = ele.pstyle('overlay-opacity').value;
10639 if (overlayOpacity !== 0) {
10640 overlayPadding = ele.pstyle('overlay-padding').value;
10641 }
10642 }
10643 var underlayOpacity = 0;
10644 var underlayPadding = 0;
10645 if (styleEnabled && options.includeUnderlays) {
10646 underlayOpacity = ele.pstyle('underlay-opacity').value;
10647 if (underlayOpacity !== 0) {
10648 underlayPadding = ele.pstyle('underlay-padding').value;
10649 }
10650 }
10651 var padding = Math.max(overlayPadding, underlayPadding);
10652 var w = 0;
10653 var wHalf = 0;
10654 if (styleEnabled) {
10655 w = ele.pstyle('width').pfValue;
10656 wHalf = w / 2;
10657 }
10658 if (isNode && options.includeNodes) {
10659 var pos = ele.position();
10660 x = pos.x;
10661 y = pos.y;
10662 var _w = ele.outerWidth();
10663 var halfW = _w / 2;
10664 var h = ele.outerHeight();
10665 var halfH = h / 2;
10666
10667 // handle node dimensions
10668 /////////////////////////
10669
10670 ex1 = x - halfW;
10671 ex2 = x + halfW;
10672 ey1 = y - halfH;
10673 ey2 = y + halfH;
10674 updateBounds(bounds, ex1, ey1, ex2, ey2);
10675 if (styleEnabled && options.includeOutlines) {
10676 updateBoundsFromOutline(bounds, ele);
10677 }
10678 } else if (isEdge && options.includeEdges) {
10679 if (styleEnabled && !headless) {
10680 var curveStyle = ele.pstyle('curve-style').strValue;
10681
10682 // handle edge dimensions (rough box estimate)
10683 //////////////////////////////////////////////
10684
10685 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10686 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10687 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10688 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10689
10690 // take into account edge width
10691 ex1 -= wHalf;
10692 ex2 += wHalf;
10693 ey1 -= wHalf;
10694 ey2 += wHalf;
10695 updateBounds(bounds, ex1, ey1, ex2, ey2);
10696
10697 // precise edges
10698 ////////////////
10699
10700 if (curveStyle === 'haystack') {
10701 var hpts = rstyle.haystackPts;
10702 if (hpts && hpts.length === 2) {
10703 ex1 = hpts[0].x;
10704 ey1 = hpts[0].y;
10705 ex2 = hpts[1].x;
10706 ey2 = hpts[1].y;
10707 if (ex1 > ex2) {
10708 var temp = ex1;
10709 ex1 = ex2;
10710 ex2 = temp;
10711 }
10712 if (ey1 > ey2) {
10713 var _temp = ey1;
10714 ey1 = ey2;
10715 ey2 = _temp;
10716 }
10717 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
10718 }
10719 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle.endsWith('segments') || curveStyle.endsWith('taxi')) {
10720 var pts;
10721 switch (curveStyle) {
10722 case 'bezier':
10723 case 'unbundled-bezier':
10724 pts = rstyle.bezierPts;
10725 break;
10726 case 'segments':
10727 case 'taxi':
10728 case 'round-segments':
10729 case 'round-taxi':
10730 pts = rstyle.linePts;
10731 break;
10732 }
10733 if (pts != null) {
10734 for (var j = 0; j < pts.length; j++) {
10735 var pt = pts[j];
10736 ex1 = pt.x - wHalf;
10737 ex2 = pt.x + wHalf;
10738 ey1 = pt.y - wHalf;
10739 ey2 = pt.y + wHalf;
10740 updateBounds(bounds, ex1, ey1, ex2, ey2);
10741 }
10742 }
10743 } // bezier-like or segment-like edge
10744 } else {
10745 // headless or style disabled
10746
10747 // fallback on source and target positions
10748 //////////////////////////////////////////
10749
10750 var n1 = ele.source();
10751 var n1pos = n1.position();
10752 var n2 = ele.target();
10753 var n2pos = n2.position();
10754 ex1 = n1pos.x;
10755 ex2 = n2pos.x;
10756 ey1 = n1pos.y;
10757 ey2 = n2pos.y;
10758 if (ex1 > ex2) {
10759 var _temp2 = ex1;
10760 ex1 = ex2;
10761 ex2 = _temp2;
10762 }
10763 if (ey1 > ey2) {
10764 var _temp3 = ey1;
10765 ey1 = ey2;
10766 ey2 = _temp3;
10767 }
10768
10769 // take into account edge width
10770 ex1 -= wHalf;
10771 ex2 += wHalf;
10772 ey1 -= wHalf;
10773 ey2 += wHalf;
10774 updateBounds(bounds, ex1, ey1, ex2, ey2);
10775 } // headless or style disabled
10776 } // edges
10777
10778 // handle edge arrow size
10779 /////////////////////////
10780
10781 if (styleEnabled && options.includeEdges && isEdge) {
10782 updateBoundsFromArrow(bounds, ele, 'mid-source');
10783 updateBoundsFromArrow(bounds, ele, 'mid-target');
10784 updateBoundsFromArrow(bounds, ele, 'source');
10785 updateBoundsFromArrow(bounds, ele, 'target');
10786 }
10787
10788 // ghost
10789 ////////
10790
10791 if (styleEnabled) {
10792 var ghost = ele.pstyle('ghost').value === 'yes';
10793 if (ghost) {
10794 var gx = ele.pstyle('ghost-offset-x').pfValue;
10795 var gy = ele.pstyle('ghost-offset-y').pfValue;
10796 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
10797 }
10798 }
10799
10800 // always store the body bounds separately from the labels
10801 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
10802 assignBoundingBox(bbBody, bounds);
10803 expandBoundingBoxSides(bbBody, manualExpansion);
10804 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
10805
10806 // overlay
10807 //////////
10808
10809 if (styleEnabled) {
10810 ex1 = bounds.x1;
10811 ex2 = bounds.x2;
10812 ey1 = bounds.y1;
10813 ey2 = bounds.y2;
10814 updateBounds(bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding);
10815 }
10816
10817 // always store the body bounds separately from the labels
10818 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
10819 assignBoundingBox(bbOverlay, bounds);
10820 expandBoundingBoxSides(bbOverlay, manualExpansion);
10821 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
10822
10823 // handle label dimensions
10824 //////////////////////////
10825
10826 var bbLabels = _p.labelBounds = _p.labelBounds || {};
10827 if (bbLabels.all != null) {
10828 clearBoundingBox(bbLabels.all);
10829 } else {
10830 bbLabels.all = makeBoundingBox();
10831 }
10832 if (styleEnabled && options.includeLabels) {
10833 if (options.includeMainLabels) {
10834 updateBoundsFromLabel(bounds, ele, null);
10835 }
10836 if (isEdge) {
10837 if (options.includeSourceLabels) {
10838 updateBoundsFromLabel(bounds, ele, 'source');
10839 }
10840 if (options.includeTargetLabels) {
10841 updateBoundsFromLabel(bounds, ele, 'target');
10842 }
10843 }
10844 } // style enabled for labels
10845 } // if displayed
10846
10847 bounds.x1 = noninf(bounds.x1);
10848 bounds.y1 = noninf(bounds.y1);
10849 bounds.x2 = noninf(bounds.x2);
10850 bounds.y2 = noninf(bounds.y2);
10851 bounds.w = noninf(bounds.x2 - bounds.x1);
10852 bounds.h = noninf(bounds.y2 - bounds.y1);
10853 if (bounds.w > 0 && bounds.h > 0 && displayed) {
10854 expandBoundingBoxSides(bounds, manualExpansion);
10855
10856 // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
10857 expandBoundingBox(bounds, 1);
10858 }
10859 return bounds;
10860};
10861var getKey = function getKey(opts) {
10862 var i = 0;
10863 var tf = function tf(val) {
10864 return (val ? 1 : 0) << i++;
10865 };
10866 var key = 0;
10867 key += tf(opts.incudeNodes);
10868 key += tf(opts.includeEdges);
10869 key += tf(opts.includeLabels);
10870 key += tf(opts.includeMainLabels);
10871 key += tf(opts.includeSourceLabels);
10872 key += tf(opts.includeTargetLabels);
10873 key += tf(opts.includeOverlays);
10874 key += tf(opts.includeOutlines);
10875 return key;
10876};
10877var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
10878 if (ele.isEdge()) {
10879 var p1 = ele.source().position();
10880 var p2 = ele.target().position();
10881 var r = function r(x) {
10882 return Math.round(x);
10883 };
10884 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
10885 } else {
10886 return 0;
10887 }
10888};
10889var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
10890 var _p = ele._private;
10891 var bb;
10892 var isEdge = ele.isEdge();
10893 var key = opts == null ? defBbOptsKey : getKey(opts);
10894 var usingDefOpts = key === defBbOptsKey;
10895 var currPosKey = getBoundingBoxPosKey(ele);
10896 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10897 var useCache = opts.useCache && isPosKeySame;
10898 var isDirty = function isDirty(ele) {
10899 return ele._private.bbCache == null || ele._private.styleDirty;
10900 };
10901 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
10902 if (needRecalc) {
10903 if (!isPosKeySame) {
10904 ele.recalculateRenderedStyle(useCache);
10905 }
10906 bb = boundingBoxImpl(ele, defBbOpts);
10907 _p.bbCache = bb;
10908 _p.bbCachePosKey = currPosKey;
10909 } else {
10910 bb = _p.bbCache;
10911 }
10912
10913 // not using def opts => need to build up bb from combination of sub bbs
10914 if (!usingDefOpts) {
10915 var isNode = ele.isNode();
10916 bb = makeBoundingBox();
10917 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
10918 if (opts.includeOverlays) {
10919 updateBoundsFromBox(bb, _p.overlayBounds);
10920 } else {
10921 updateBoundsFromBox(bb, _p.bodyBounds);
10922 }
10923 }
10924 if (opts.includeLabels) {
10925 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
10926 updateBoundsFromBox(bb, _p.labelBounds.all);
10927 } else {
10928 if (opts.includeMainLabels) {
10929 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
10930 }
10931 if (opts.includeSourceLabels) {
10932 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
10933 }
10934 if (opts.includeTargetLabels) {
10935 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
10936 }
10937 }
10938 }
10939 bb.w = bb.x2 - bb.x1;
10940 bb.h = bb.y2 - bb.y1;
10941 }
10942 return bb;
10943};
10944var defBbOpts = {
10945 includeNodes: true,
10946 includeEdges: true,
10947 includeLabels: true,
10948 includeMainLabels: true,
10949 includeSourceLabels: true,
10950 includeTargetLabels: true,
10951 includeOverlays: true,
10952 includeUnderlays: true,
10953 includeOutlines: true,
10954 useCache: true
10955};
10956var defBbOptsKey = getKey(defBbOpts);
10957var filledBbOpts = defaults$g(defBbOpts);
10958elesfn$b.boundingBox = function (options) {
10959 var bounds;
10960
10961 // the main usecase is ele.boundingBox() for a single element with no/def options
10962 // specified s.t. the cache is used, so check for this case to make it faster by
10963 // avoiding the overhead of the rest of the function
10964 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
10965 if (options === undefined) {
10966 options = defBbOpts;
10967 } else {
10968 options = filledBbOpts(options);
10969 }
10970 bounds = cachedBoundingBoxImpl(this[0], options);
10971 } else {
10972 bounds = makeBoundingBox();
10973 options = options || defBbOpts;
10974 var opts = filledBbOpts(options);
10975 var eles = this;
10976 var cy = eles.cy();
10977 var styleEnabled = cy.styleEnabled();
10978 if (styleEnabled) {
10979 for (var i = 0; i < eles.length; i++) {
10980 var ele = eles[i];
10981 var _p = ele._private;
10982 var currPosKey = getBoundingBoxPosKey(ele);
10983 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10984 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
10985 ele.recalculateRenderedStyle(useCache);
10986 }
10987 }
10988 this.updateCompoundBounds(!options.useCache);
10989 for (var _i = 0; _i < eles.length; _i++) {
10990 var _ele = eles[_i];
10991 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
10992 }
10993 }
10994 bounds.x1 = noninf(bounds.x1);
10995 bounds.y1 = noninf(bounds.y1);
10996 bounds.x2 = noninf(bounds.x2);
10997 bounds.y2 = noninf(bounds.y2);
10998 bounds.w = noninf(bounds.x2 - bounds.x1);
10999 bounds.h = noninf(bounds.y2 - bounds.y1);
11000 return bounds;
11001};
11002elesfn$b.dirtyBoundingBoxCache = function () {
11003 for (var i = 0; i < this.length; i++) {
11004 var _p = this[i]._private;
11005 _p.bbCache = null;
11006 _p.bbCachePosKey = null;
11007 _p.bodyBounds = null;
11008 _p.overlayBounds = null;
11009 _p.labelBounds.all = null;
11010 _p.labelBounds.source = null;
11011 _p.labelBounds.target = null;
11012 _p.labelBounds.main = null;
11013 _p.labelBounds.sourceRot = null;
11014 _p.labelBounds.targetRot = null;
11015 _p.labelBounds.mainRot = null;
11016 _p.arrowBounds.source = null;
11017 _p.arrowBounds.target = null;
11018 _p.arrowBounds['mid-source'] = null;
11019 _p.arrowBounds['mid-target'] = null;
11020 }
11021 this.emitAndNotify('bounds');
11022 return this;
11023};
11024
11025// private helper to get bounding box for custom node positions
11026// - good for perf in certain cases but currently requires dirtying the rendered style
11027// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
11028// - try to use for only things like discrete layouts where the node position would change anyway
11029elesfn$b.boundingBoxAt = function (fn) {
11030 var nodes = this.nodes();
11031 var cy = this.cy();
11032 var hasCompoundNodes = cy.hasCompoundNodes();
11033 var parents = cy.collection();
11034 if (hasCompoundNodes) {
11035 parents = nodes.filter(function (node) {
11036 return node.isParent();
11037 });
11038 nodes = nodes.not(parents);
11039 }
11040 if (plainObject(fn)) {
11041 var obj = fn;
11042 fn = function fn() {
11043 return obj;
11044 };
11045 }
11046 var storeOldPos = function storeOldPos(node, i) {
11047 return node._private.bbAtOldPos = fn(node, i);
11048 };
11049 var getOldPos = function getOldPos(node) {
11050 return node._private.bbAtOldPos;
11051 };
11052 cy.startBatch();
11053 nodes.forEach(storeOldPos).silentPositions(fn);
11054 if (hasCompoundNodes) {
11055 parents.dirtyCompoundBoundsCache();
11056 parents.dirtyBoundingBoxCache();
11057 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
11058 }
11059
11060 var bb = copyBoundingBox(this.boundingBox({
11061 useCache: false
11062 }));
11063 nodes.silentPositions(getOldPos);
11064 if (hasCompoundNodes) {
11065 parents.dirtyCompoundBoundsCache();
11066 parents.dirtyBoundingBoxCache();
11067 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
11068 }
11069
11070 cy.endBatch();
11071 return bb;
11072};
11073fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
11074fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
11075var bounds = elesfn$b;
11076
11077var fn$2, elesfn$a;
11078fn$2 = elesfn$a = {};
11079var defineDimFns = function defineDimFns(opts) {
11080 opts.uppercaseName = capitalize(opts.name);
11081 opts.autoName = 'auto' + opts.uppercaseName;
11082 opts.labelName = 'label' + opts.uppercaseName;
11083 opts.outerName = 'outer' + opts.uppercaseName;
11084 opts.uppercaseOuterName = capitalize(opts.outerName);
11085 fn$2[opts.name] = function dimImpl() {
11086 var ele = this[0];
11087 var _p = ele._private;
11088 var cy = _p.cy;
11089 var styleEnabled = cy._private.styleEnabled;
11090 if (ele) {
11091 if (styleEnabled) {
11092 if (ele.isParent()) {
11093 ele.updateCompoundBounds();
11094 return _p[opts.autoName] || 0;
11095 }
11096 var d = ele.pstyle(opts.name);
11097 switch (d.strValue) {
11098 case 'label':
11099 ele.recalculateRenderedStyle();
11100 return _p.rstyle[opts.labelName] || 0;
11101 default:
11102 return d.pfValue;
11103 }
11104 } else {
11105 return 1;
11106 }
11107 }
11108 };
11109 fn$2['outer' + opts.uppercaseName] = function outerDimImpl() {
11110 var ele = this[0];
11111 var _p = ele._private;
11112 var cy = _p.cy;
11113 var styleEnabled = cy._private.styleEnabled;
11114 if (ele) {
11115 if (styleEnabled) {
11116 var dim = ele[opts.name]();
11117 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
11118 var padding = 2 * ele.padding();
11119 return dim + border + padding;
11120 } else {
11121 return 1;
11122 }
11123 }
11124 };
11125 fn$2['rendered' + opts.uppercaseName] = function renderedDimImpl() {
11126 var ele = this[0];
11127 if (ele) {
11128 var d = ele[opts.name]();
11129 return d * this.cy().zoom();
11130 }
11131 };
11132 fn$2['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
11133 var ele = this[0];
11134 if (ele) {
11135 var od = ele[opts.outerName]();
11136 return od * this.cy().zoom();
11137 }
11138 };
11139};
11140defineDimFns({
11141 name: 'width'
11142});
11143defineDimFns({
11144 name: 'height'
11145});
11146elesfn$a.padding = function () {
11147 var ele = this[0];
11148 var _p = ele._private;
11149 if (ele.isParent()) {
11150 ele.updateCompoundBounds();
11151 if (_p.autoPadding !== undefined) {
11152 return _p.autoPadding;
11153 } else {
11154 return ele.pstyle('padding').pfValue;
11155 }
11156 } else {
11157 return ele.pstyle('padding').pfValue;
11158 }
11159};
11160elesfn$a.paddedHeight = function () {
11161 var ele = this[0];
11162 return ele.height() + 2 * ele.padding();
11163};
11164elesfn$a.paddedWidth = function () {
11165 var ele = this[0];
11166 return ele.width() + 2 * ele.padding();
11167};
11168var widthHeight = elesfn$a;
11169
11170var ifEdge = function ifEdge(ele, getValue) {
11171 if (ele.isEdge()) {
11172 return getValue(ele);
11173 }
11174};
11175var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
11176 if (ele.isEdge()) {
11177 var cy = ele.cy();
11178 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
11179 }
11180};
11181var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
11182 if (ele.isEdge()) {
11183 var cy = ele.cy();
11184 var pan = cy.pan();
11185 var zoom = cy.zoom();
11186 return getPoints(ele).map(function (p) {
11187 return modelToRenderedPosition(p, zoom, pan);
11188 });
11189 }
11190};
11191var controlPoints = function controlPoints(ele) {
11192 return ele.renderer().getControlPoints(ele);
11193};
11194var segmentPoints = function segmentPoints(ele) {
11195 return ele.renderer().getSegmentPoints(ele);
11196};
11197var sourceEndpoint = function sourceEndpoint(ele) {
11198 return ele.renderer().getSourceEndpoint(ele);
11199};
11200var targetEndpoint = function targetEndpoint(ele) {
11201 return ele.renderer().getTargetEndpoint(ele);
11202};
11203var midpoint = function midpoint(ele) {
11204 return ele.renderer().getEdgeMidpoint(ele);
11205};
11206var pts = {
11207 controlPoints: {
11208 get: controlPoints,
11209 mult: true
11210 },
11211 segmentPoints: {
11212 get: segmentPoints,
11213 mult: true
11214 },
11215 sourceEndpoint: {
11216 get: sourceEndpoint
11217 },
11218 targetEndpoint: {
11219 get: targetEndpoint
11220 },
11221 midpoint: {
11222 get: midpoint
11223 }
11224};
11225var renderedName = function renderedName(name) {
11226 return 'rendered' + name[0].toUpperCase() + name.substr(1);
11227};
11228var edgePoints = Object.keys(pts).reduce(function (obj, name) {
11229 var spec = pts[name];
11230 var rName = renderedName(name);
11231 obj[name] = function () {
11232 return ifEdge(this, spec.get);
11233 };
11234 if (spec.mult) {
11235 obj[rName] = function () {
11236 return ifEdgeRenderedPositions(this, spec.get);
11237 };
11238 } else {
11239 obj[rName] = function () {
11240 return ifEdgeRenderedPosition(this, spec.get);
11241 };
11242 }
11243 return obj;
11244}, {});
11245
11246var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
11247
11248/*!
11249Event object based on jQuery events, MIT license
11250
11251https://jquery.org/license/
11252https://tldrlegal.com/license/mit-license
11253https://github.com/jquery/jquery/blob/master/src/event.js
11254*/
11255
11256var Event = function Event(src, props) {
11257 this.recycle(src, props);
11258};
11259function returnFalse() {
11260 return false;
11261}
11262function returnTrue() {
11263 return true;
11264}
11265
11266// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
11267Event.prototype = {
11268 instanceString: function instanceString() {
11269 return 'event';
11270 },
11271 recycle: function recycle(src, props) {
11272 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
11273 if (src != null && src.preventDefault) {
11274 // Browser Event object
11275 this.type = src.type;
11276
11277 // Events bubbling up the document may have been marked as prevented
11278 // by a handler lower down the tree; reflect the correct value.
11279 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
11280 } else if (src != null && src.type) {
11281 // Plain object containing all event details
11282 props = src;
11283 } else {
11284 // Event string
11285 this.type = src;
11286 }
11287
11288 // Put explicitly provided properties onto the event object
11289 if (props != null) {
11290 // more efficient to manually copy fields we use
11291 this.originalEvent = props.originalEvent;
11292 this.type = props.type != null ? props.type : this.type;
11293 this.cy = props.cy;
11294 this.target = props.target;
11295 this.position = props.position;
11296 this.renderedPosition = props.renderedPosition;
11297 this.namespace = props.namespace;
11298 this.layout = props.layout;
11299 }
11300 if (this.cy != null && this.position != null && this.renderedPosition == null) {
11301 // create a rendered position based on the passed position
11302 var pos = this.position;
11303 var zoom = this.cy.zoom();
11304 var pan = this.cy.pan();
11305 this.renderedPosition = {
11306 x: pos.x * zoom + pan.x,
11307 y: pos.y * zoom + pan.y
11308 };
11309 }
11310
11311 // Create a timestamp if incoming event doesn't have one
11312 this.timeStamp = src && src.timeStamp || Date.now();
11313 },
11314 preventDefault: function preventDefault() {
11315 this.isDefaultPrevented = returnTrue;
11316 var e = this.originalEvent;
11317 if (!e) {
11318 return;
11319 }
11320
11321 // if preventDefault exists run it on the original event
11322 if (e.preventDefault) {
11323 e.preventDefault();
11324 }
11325 },
11326 stopPropagation: function stopPropagation() {
11327 this.isPropagationStopped = returnTrue;
11328 var e = this.originalEvent;
11329 if (!e) {
11330 return;
11331 }
11332
11333 // if stopPropagation exists run it on the original event
11334 if (e.stopPropagation) {
11335 e.stopPropagation();
11336 }
11337 },
11338 stopImmediatePropagation: function stopImmediatePropagation() {
11339 this.isImmediatePropagationStopped = returnTrue;
11340 this.stopPropagation();
11341 },
11342 isDefaultPrevented: returnFalse,
11343 isPropagationStopped: returnFalse,
11344 isImmediatePropagationStopped: returnFalse
11345};
11346
11347var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
11348var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
11349
11350var defaults$8 = {
11351 qualifierCompare: function qualifierCompare(q1, q2) {
11352 return q1 === q2;
11353 },
11354 eventMatches: function eventMatches( /*context, listener, eventObj*/
11355 ) {
11356 return true;
11357 },
11358 addEventFields: function addEventFields( /*context, evt*/
11359 ) {},
11360 callbackContext: function callbackContext(context /*, listener, eventObj*/) {
11361 return context;
11362 },
11363 beforeEmit: function beforeEmit( /* context, listener, eventObj */
11364 ) {},
11365 afterEmit: function afterEmit( /* context, listener, eventObj */
11366 ) {},
11367 bubble: function bubble( /*context*/
11368 ) {
11369 return false;
11370 },
11371 parent: function parent( /*context*/
11372 ) {
11373 return null;
11374 },
11375 context: null
11376};
11377var defaultsKeys = Object.keys(defaults$8);
11378var emptyOpts = {};
11379function Emitter() {
11380 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
11381 var context = arguments.length > 1 ? arguments[1] : undefined;
11382 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
11383 for (var i = 0; i < defaultsKeys.length; i++) {
11384 var key = defaultsKeys[i];
11385 this[key] = opts[key] || defaults$8[key];
11386 }
11387 this.context = context || this.context;
11388 this.listeners = [];
11389 this.emitting = 0;
11390}
11391var p = Emitter.prototype;
11392var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
11393 if (fn$6(qualifier)) {
11394 callback = qualifier;
11395 qualifier = null;
11396 }
11397 if (confOverrides) {
11398 if (conf == null) {
11399 conf = confOverrides;
11400 } else {
11401 conf = extend({}, conf, confOverrides);
11402 }
11403 }
11404 var eventList = array(events) ? events : events.split(/\s+/);
11405 for (var i = 0; i < eventList.length; i++) {
11406 var evt = eventList[i];
11407 if (emptyString(evt)) {
11408 continue;
11409 }
11410 var match = evt.match(eventRegex); // type[.namespace]
11411
11412 if (match) {
11413 var type = match[1];
11414 var namespace = match[2] ? match[2] : null;
11415 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
11416 if (ret === false) {
11417 break;
11418 } // allow exiting early
11419 }
11420 }
11421};
11422
11423var makeEventObj = function makeEventObj(self, obj) {
11424 self.addEventFields(self.context, obj);
11425 return new Event(obj.type, obj);
11426};
11427var forEachEventObj = function forEachEventObj(self, handler, events) {
11428 if (event(events)) {
11429 handler(self, events);
11430 return;
11431 } else if (plainObject(events)) {
11432 handler(self, makeEventObj(self, events));
11433 return;
11434 }
11435 var eventList = array(events) ? events : events.split(/\s+/);
11436 for (var i = 0; i < eventList.length; i++) {
11437 var evt = eventList[i];
11438 if (emptyString(evt)) {
11439 continue;
11440 }
11441 var match = evt.match(eventRegex); // type[.namespace]
11442
11443 if (match) {
11444 var type = match[1];
11445 var namespace = match[2] ? match[2] : null;
11446 var eventObj = makeEventObj(self, {
11447 type: type,
11448 namespace: namespace,
11449 target: self.context
11450 });
11451 handler(self, eventObj);
11452 }
11453 }
11454};
11455p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
11456 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
11457 if (fn$6(callback)) {
11458 self.listeners.push({
11459 event: event,
11460 // full event string
11461 callback: callback,
11462 // callback to run
11463 type: type,
11464 // the event type (e.g. 'click')
11465 namespace: namespace,
11466 // the event namespace (e.g. ".foo")
11467 qualifier: qualifier,
11468 // a restriction on whether to match this emitter
11469 conf: conf // additional configuration
11470 });
11471 }
11472 }, events, qualifier, callback, conf, confOverrides);
11473 return this;
11474};
11475p.one = function (events, qualifier, callback, conf) {
11476 return this.on(events, qualifier, callback, conf, {
11477 one: true
11478 });
11479};
11480p.removeListener = p.off = function (events, qualifier, callback, conf) {
11481 var _this = this;
11482 if (this.emitting !== 0) {
11483 this.listeners = copyArray$1(this.listeners);
11484 }
11485 var listeners = this.listeners;
11486 var _loop = function _loop(i) {
11487 var listener = listeners[i];
11488 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback /*, conf*/) {
11489 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
11490 listeners.splice(i, 1);
11491 return false;
11492 }
11493 }, events, qualifier, callback, conf);
11494 };
11495 for (var i = listeners.length - 1; i >= 0; i--) {
11496 _loop(i);
11497 }
11498 return this;
11499};
11500p.removeAllListeners = function () {
11501 return this.removeListener('*');
11502};
11503p.emit = p.trigger = function (events, extraParams, manualCallback) {
11504 var listeners = this.listeners;
11505 var numListenersBeforeEmit = listeners.length;
11506 this.emitting++;
11507 if (!array(extraParams)) {
11508 extraParams = [extraParams];
11509 }
11510 forEachEventObj(this, function (self, eventObj) {
11511 if (manualCallback != null) {
11512 listeners = [{
11513 event: eventObj.event,
11514 type: eventObj.type,
11515 namespace: eventObj.namespace,
11516 callback: manualCallback
11517 }];
11518 numListenersBeforeEmit = listeners.length;
11519 }
11520 var _loop2 = function _loop2(i) {
11521 var listener = listeners[i];
11522 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
11523 var args = [eventObj];
11524 if (extraParams != null) {
11525 push(args, extraParams);
11526 }
11527 self.beforeEmit(self.context, listener, eventObj);
11528 if (listener.conf && listener.conf.one) {
11529 self.listeners = self.listeners.filter(function (l) {
11530 return l !== listener;
11531 });
11532 }
11533 var context = self.callbackContext(self.context, listener, eventObj);
11534 var ret = listener.callback.apply(context, args);
11535 self.afterEmit(self.context, listener, eventObj);
11536 if (ret === false) {
11537 eventObj.stopPropagation();
11538 eventObj.preventDefault();
11539 }
11540 } // if listener matches
11541 };
11542 for (var i = 0; i < numListenersBeforeEmit; i++) {
11543 _loop2(i);
11544 } // for listener
11545
11546 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
11547 self.parent(self.context).emit(eventObj, extraParams);
11548 }
11549 }, events);
11550 this.emitting--;
11551 return this;
11552};
11553
11554var emitterOptions$1 = {
11555 qualifierCompare: function qualifierCompare(selector1, selector2) {
11556 if (selector1 == null || selector2 == null) {
11557 return selector1 == null && selector2 == null;
11558 } else {
11559 return selector1.sameText(selector2);
11560 }
11561 },
11562 eventMatches: function eventMatches(ele, listener, eventObj) {
11563 var selector = listener.qualifier;
11564 if (selector != null) {
11565 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
11566 }
11567 return true;
11568 },
11569 addEventFields: function addEventFields(ele, evt) {
11570 evt.cy = ele.cy();
11571 evt.target = ele;
11572 },
11573 callbackContext: function callbackContext(ele, listener, eventObj) {
11574 return listener.qualifier != null ? eventObj.target : ele;
11575 },
11576 beforeEmit: function beforeEmit(context, listener /*, eventObj*/) {
11577 if (listener.conf && listener.conf.once) {
11578 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
11579 }
11580 },
11581 bubble: function bubble() {
11582 return true;
11583 },
11584 parent: function parent(ele) {
11585 return ele.isChild() ? ele.parent() : ele.cy();
11586 }
11587};
11588var argSelector$1 = function argSelector(arg) {
11589 if (string(arg)) {
11590 return new Selector(arg);
11591 } else {
11592 return arg;
11593 }
11594};
11595var elesfn$9 = {
11596 createEmitter: function createEmitter() {
11597 for (var i = 0; i < this.length; i++) {
11598 var ele = this[i];
11599 var _p = ele._private;
11600 if (!_p.emitter) {
11601 _p.emitter = new Emitter(emitterOptions$1, ele);
11602 }
11603 }
11604 return this;
11605 },
11606 emitter: function emitter() {
11607 return this._private.emitter;
11608 },
11609 on: function on(events, selector, callback) {
11610 var argSel = argSelector$1(selector);
11611 for (var i = 0; i < this.length; i++) {
11612 var ele = this[i];
11613 ele.emitter().on(events, argSel, callback);
11614 }
11615 return this;
11616 },
11617 removeListener: function removeListener(events, selector, callback) {
11618 var argSel = argSelector$1(selector);
11619 for (var i = 0; i < this.length; i++) {
11620 var ele = this[i];
11621 ele.emitter().removeListener(events, argSel, callback);
11622 }
11623 return this;
11624 },
11625 removeAllListeners: function removeAllListeners() {
11626 for (var i = 0; i < this.length; i++) {
11627 var ele = this[i];
11628 ele.emitter().removeAllListeners();
11629 }
11630 return this;
11631 },
11632 one: function one(events, selector, callback) {
11633 var argSel = argSelector$1(selector);
11634 for (var i = 0; i < this.length; i++) {
11635 var ele = this[i];
11636 ele.emitter().one(events, argSel, callback);
11637 }
11638 return this;
11639 },
11640 once: function once(events, selector, callback) {
11641 var argSel = argSelector$1(selector);
11642 for (var i = 0; i < this.length; i++) {
11643 var ele = this[i];
11644 ele.emitter().on(events, argSel, callback, {
11645 once: true,
11646 onceCollection: this
11647 });
11648 }
11649 },
11650 emit: function emit(events, extraParams) {
11651 for (var i = 0; i < this.length; i++) {
11652 var ele = this[i];
11653 ele.emitter().emit(events, extraParams);
11654 }
11655 return this;
11656 },
11657 emitAndNotify: function emitAndNotify(event, extraParams) {
11658 // for internal use only
11659 if (this.length === 0) {
11660 return;
11661 } // empty collections don't need to notify anything
11662
11663 // notify renderer
11664 this.cy().notify(event, this);
11665 this.emit(event, extraParams);
11666 return this;
11667 }
11668};
11669define.eventAliasesOn(elesfn$9);
11670
11671var elesfn$8 = {
11672 nodes: function nodes(selector) {
11673 return this.filter(function (ele) {
11674 return ele.isNode();
11675 }).filter(selector);
11676 },
11677 edges: function edges(selector) {
11678 return this.filter(function (ele) {
11679 return ele.isEdge();
11680 }).filter(selector);
11681 },
11682 // internal helper to get nodes and edges as separate collections with single iteration over elements
11683 byGroup: function byGroup() {
11684 var nodes = this.spawn();
11685 var edges = this.spawn();
11686 for (var i = 0; i < this.length; i++) {
11687 var ele = this[i];
11688 if (ele.isNode()) {
11689 nodes.push(ele);
11690 } else {
11691 edges.push(ele);
11692 }
11693 }
11694 return {
11695 nodes: nodes,
11696 edges: edges
11697 };
11698 },
11699 filter: function filter(_filter, thisArg) {
11700 if (_filter === undefined) {
11701 // check this first b/c it's the most common/performant case
11702 return this;
11703 } else if (string(_filter) || elementOrCollection(_filter)) {
11704 return new Selector(_filter).filter(this);
11705 } else if (fn$6(_filter)) {
11706 var filterEles = this.spawn();
11707 var eles = this;
11708 for (var i = 0; i < eles.length; i++) {
11709 var ele = eles[i];
11710 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
11711 if (include) {
11712 filterEles.push(ele);
11713 }
11714 }
11715 return filterEles;
11716 }
11717 return this.spawn(); // if not handled by above, give 'em an empty collection
11718 },
11719
11720 not: function not(toRemove) {
11721 if (!toRemove) {
11722 return this;
11723 } else {
11724 if (string(toRemove)) {
11725 toRemove = this.filter(toRemove);
11726 }
11727 var elements = this.spawn();
11728 for (var i = 0; i < this.length; i++) {
11729 var element = this[i];
11730 var remove = toRemove.has(element);
11731 if (!remove) {
11732 elements.push(element);
11733 }
11734 }
11735 return elements;
11736 }
11737 },
11738 absoluteComplement: function absoluteComplement() {
11739 var cy = this.cy();
11740 return cy.mutableElements().not(this);
11741 },
11742 intersect: function intersect(other) {
11743 // if a selector is specified, then filter by it instead
11744 if (string(other)) {
11745 var selector = other;
11746 return this.filter(selector);
11747 }
11748 var elements = this.spawn();
11749 var col1 = this;
11750 var col2 = other;
11751 var col1Smaller = this.length < other.length;
11752 var colS = col1Smaller ? col1 : col2;
11753 var colL = col1Smaller ? col2 : col1;
11754 for (var i = 0; i < colS.length; i++) {
11755 var ele = colS[i];
11756 if (colL.has(ele)) {
11757 elements.push(ele);
11758 }
11759 }
11760 return elements;
11761 },
11762 xor: function xor(other) {
11763 var cy = this._private.cy;
11764 if (string(other)) {
11765 other = cy.$(other);
11766 }
11767 var elements = this.spawn();
11768 var col1 = this;
11769 var col2 = other;
11770 var add = function add(col, other) {
11771 for (var i = 0; i < col.length; i++) {
11772 var ele = col[i];
11773 var id = ele._private.data.id;
11774 var inOther = other.hasElementWithId(id);
11775 if (!inOther) {
11776 elements.push(ele);
11777 }
11778 }
11779 };
11780 add(col1, col2);
11781 add(col2, col1);
11782 return elements;
11783 },
11784 diff: function diff(other) {
11785 var cy = this._private.cy;
11786 if (string(other)) {
11787 other = cy.$(other);
11788 }
11789 var left = this.spawn();
11790 var right = this.spawn();
11791 var both = this.spawn();
11792 var col1 = this;
11793 var col2 = other;
11794 var add = function add(col, other, retEles) {
11795 for (var i = 0; i < col.length; i++) {
11796 var ele = col[i];
11797 var id = ele._private.data.id;
11798 var inOther = other.hasElementWithId(id);
11799 if (inOther) {
11800 both.merge(ele);
11801 } else {
11802 retEles.push(ele);
11803 }
11804 }
11805 };
11806 add(col1, col2, left);
11807 add(col2, col1, right);
11808 return {
11809 left: left,
11810 right: right,
11811 both: both
11812 };
11813 },
11814 add: function add(toAdd) {
11815 var cy = this._private.cy;
11816 if (!toAdd) {
11817 return this;
11818 }
11819 if (string(toAdd)) {
11820 var selector = toAdd;
11821 toAdd = cy.mutableElements().filter(selector);
11822 }
11823 var elements = this.spawnSelf();
11824 for (var i = 0; i < toAdd.length; i++) {
11825 var ele = toAdd[i];
11826 var add = !this.has(ele);
11827 if (add) {
11828 elements.push(ele);
11829 }
11830 }
11831 return elements;
11832 },
11833 // in place merge on calling collection
11834 merge: function merge(toAdd) {
11835 var _p = this._private;
11836 var cy = _p.cy;
11837 if (!toAdd) {
11838 return this;
11839 }
11840 if (toAdd && string(toAdd)) {
11841 var selector = toAdd;
11842 toAdd = cy.mutableElements().filter(selector);
11843 }
11844 var map = _p.map;
11845 for (var i = 0; i < toAdd.length; i++) {
11846 var toAddEle = toAdd[i];
11847 var id = toAddEle._private.data.id;
11848 var add = !map.has(id);
11849 if (add) {
11850 var index = this.length++;
11851 this[index] = toAddEle;
11852 map.set(id, {
11853 ele: toAddEle,
11854 index: index
11855 });
11856 }
11857 }
11858 return this; // chaining
11859 },
11860
11861 unmergeAt: function unmergeAt(i) {
11862 var ele = this[i];
11863 var id = ele.id();
11864 var _p = this._private;
11865 var map = _p.map;
11866
11867 // remove ele
11868 this[i] = undefined;
11869 map["delete"](id);
11870 var unmergedLastEle = i === this.length - 1;
11871
11872 // replace empty spot with last ele in collection
11873 if (this.length > 1 && !unmergedLastEle) {
11874 var lastEleI = this.length - 1;
11875 var lastEle = this[lastEleI];
11876 var lastEleId = lastEle._private.data.id;
11877 this[lastEleI] = undefined;
11878 this[i] = lastEle;
11879 map.set(lastEleId, {
11880 ele: lastEle,
11881 index: i
11882 });
11883 }
11884
11885 // the collection is now 1 ele smaller
11886 this.length--;
11887 return this;
11888 },
11889 // remove single ele in place in calling collection
11890 unmergeOne: function unmergeOne(ele) {
11891 ele = ele[0];
11892 var _p = this._private;
11893 var id = ele._private.data.id;
11894 var map = _p.map;
11895 var entry = map.get(id);
11896 if (!entry) {
11897 return this; // no need to remove
11898 }
11899
11900 var i = entry.index;
11901 this.unmergeAt(i);
11902 return this;
11903 },
11904 // remove eles in place on calling collection
11905 unmerge: function unmerge(toRemove) {
11906 var cy = this._private.cy;
11907 if (!toRemove) {
11908 return this;
11909 }
11910 if (toRemove && string(toRemove)) {
11911 var selector = toRemove;
11912 toRemove = cy.mutableElements().filter(selector);
11913 }
11914 for (var i = 0; i < toRemove.length; i++) {
11915 this.unmergeOne(toRemove[i]);
11916 }
11917 return this; // chaining
11918 },
11919
11920 unmergeBy: function unmergeBy(toRmFn) {
11921 for (var i = this.length - 1; i >= 0; i--) {
11922 var ele = this[i];
11923 if (toRmFn(ele)) {
11924 this.unmergeAt(i);
11925 }
11926 }
11927 return this;
11928 },
11929 map: function map(mapFn, thisArg) {
11930 var arr = [];
11931 var eles = this;
11932 for (var i = 0; i < eles.length; i++) {
11933 var ele = eles[i];
11934 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11935 arr.push(ret);
11936 }
11937 return arr;
11938 },
11939 reduce: function reduce(fn, initialValue) {
11940 var val = initialValue;
11941 var eles = this;
11942 for (var i = 0; i < eles.length; i++) {
11943 val = fn(val, eles[i], i, eles);
11944 }
11945 return val;
11946 },
11947 max: function max(valFn, thisArg) {
11948 var max = -Infinity;
11949 var maxEle;
11950 var eles = this;
11951 for (var i = 0; i < eles.length; i++) {
11952 var ele = eles[i];
11953 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11954 if (val > max) {
11955 max = val;
11956 maxEle = ele;
11957 }
11958 }
11959 return {
11960 value: max,
11961 ele: maxEle
11962 };
11963 },
11964 min: function min(valFn, thisArg) {
11965 var min = Infinity;
11966 var minEle;
11967 var eles = this;
11968 for (var i = 0; i < eles.length; i++) {
11969 var ele = eles[i];
11970 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11971 if (val < min) {
11972 min = val;
11973 minEle = ele;
11974 }
11975 }
11976 return {
11977 value: min,
11978 ele: minEle
11979 };
11980 }
11981};
11982
11983// aliases
11984var fn$1 = elesfn$8;
11985fn$1['u'] = fn$1['|'] = fn$1['+'] = fn$1.union = fn$1.or = fn$1.add;
11986fn$1['\\'] = fn$1['!'] = fn$1['-'] = fn$1.difference = fn$1.relativeComplement = fn$1.subtract = fn$1.not;
11987fn$1['n'] = fn$1['&'] = fn$1['.'] = fn$1.and = fn$1.intersection = fn$1.intersect;
11988fn$1['^'] = fn$1['(+)'] = fn$1['(-)'] = fn$1.symmetricDifference = fn$1.symdiff = fn$1.xor;
11989fn$1.fnFilter = fn$1.filterFn = fn$1.stdFilter = fn$1.filter;
11990fn$1.complement = fn$1.abscomp = fn$1.absoluteComplement;
11991
11992var elesfn$7 = {
11993 isNode: function isNode() {
11994 return this.group() === 'nodes';
11995 },
11996 isEdge: function isEdge() {
11997 return this.group() === 'edges';
11998 },
11999 isLoop: function isLoop() {
12000 return this.isEdge() && this.source()[0] === this.target()[0];
12001 },
12002 isSimple: function isSimple() {
12003 return this.isEdge() && this.source()[0] !== this.target()[0];
12004 },
12005 group: function group() {
12006 var ele = this[0];
12007 if (ele) {
12008 return ele._private.group;
12009 }
12010 }
12011};
12012
12013/**
12014 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
12015 * and z-index (low to high). These styles affect how this applies:
12016 *
12017 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
12018 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
12019 * root to leaves of the compound graph. The last drawn is `top`.
12020 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
12021 * `manual` ignores this convention and draws based on the `z-index` value setting.
12022 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
12023 * `z-index` will be drawn on top of an element with a lower `z-index`.
12024 */
12025var zIndexSort = function zIndexSort(a, b) {
12026 var cy = a.cy();
12027 var hasCompoundNodes = cy.hasCompoundNodes();
12028 function getDepth(ele) {
12029 var style = ele.pstyle('z-compound-depth');
12030 if (style.value === 'auto') {
12031 return hasCompoundNodes ? ele.zDepth() : 0;
12032 } else if (style.value === 'bottom') {
12033 return -1;
12034 } else if (style.value === 'top') {
12035 return MAX_INT$1;
12036 }
12037 // 'orphan'
12038 return 0;
12039 }
12040 var depthDiff = getDepth(a) - getDepth(b);
12041 if (depthDiff !== 0) {
12042 return depthDiff;
12043 }
12044 function getEleDepth(ele) {
12045 var style = ele.pstyle('z-index-compare');
12046 if (style.value === 'auto') {
12047 return ele.isNode() ? 1 : 0;
12048 }
12049 // 'manual'
12050 return 0;
12051 }
12052 var eleDiff = getEleDepth(a) - getEleDepth(b);
12053 if (eleDiff !== 0) {
12054 return eleDiff;
12055 }
12056 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
12057 if (zDiff !== 0) {
12058 return zDiff;
12059 }
12060 // compare indices in the core (order added to graph w/ last on top)
12061 return a.poolIndex() - b.poolIndex();
12062};
12063
12064var elesfn$6 = {
12065 forEach: function forEach(fn, thisArg) {
12066 if (fn$6(fn)) {
12067 var N = this.length;
12068 for (var i = 0; i < N; i++) {
12069 var ele = this[i];
12070 var ret = thisArg ? fn.apply(thisArg, [ele, i, this]) : fn(ele, i, this);
12071 if (ret === false) {
12072 break;
12073 } // exit each early on return false
12074 }
12075 }
12076
12077 return this;
12078 },
12079 toArray: function toArray() {
12080 var array = [];
12081 for (var i = 0; i < this.length; i++) {
12082 array.push(this[i]);
12083 }
12084 return array;
12085 },
12086 slice: function slice(start, end) {
12087 var array = [];
12088 var thisSize = this.length;
12089 if (end == null) {
12090 end = thisSize;
12091 }
12092 if (start == null) {
12093 start = 0;
12094 }
12095 if (start < 0) {
12096 start = thisSize + start;
12097 }
12098 if (end < 0) {
12099 end = thisSize + end;
12100 }
12101 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
12102 array.push(this[i]);
12103 }
12104 return this.spawn(array);
12105 },
12106 size: function size() {
12107 return this.length;
12108 },
12109 eq: function eq(i) {
12110 return this[i] || this.spawn();
12111 },
12112 first: function first() {
12113 return this[0] || this.spawn();
12114 },
12115 last: function last() {
12116 return this[this.length - 1] || this.spawn();
12117 },
12118 empty: function empty() {
12119 return this.length === 0;
12120 },
12121 nonempty: function nonempty() {
12122 return !this.empty();
12123 },
12124 sort: function sort(sortFn) {
12125 if (!fn$6(sortFn)) {
12126 return this;
12127 }
12128 var sorted = this.toArray().sort(sortFn);
12129 return this.spawn(sorted);
12130 },
12131 sortByZIndex: function sortByZIndex() {
12132 return this.sort(zIndexSort);
12133 },
12134 zDepth: function zDepth() {
12135 var ele = this[0];
12136 if (!ele) {
12137 return undefined;
12138 }
12139
12140 // let cy = ele.cy();
12141 var _p = ele._private;
12142 var group = _p.group;
12143 if (group === 'nodes') {
12144 var depth = _p.data.parent ? ele.parents().size() : 0;
12145 if (!ele.isParent()) {
12146 return MAX_INT$1 - 1; // childless nodes always on top
12147 }
12148
12149 return depth;
12150 } else {
12151 var src = _p.source;
12152 var tgt = _p.target;
12153 var srcDepth = src.zDepth();
12154 var tgtDepth = tgt.zDepth();
12155 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
12156 }
12157 }
12158};
12159
12160elesfn$6.each = elesfn$6.forEach;
12161var defineSymbolIterator = function defineSymbolIterator() {
12162 var typeofUndef = "undefined" ;
12163 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
12164
12165 if (isIteratorSupported) {
12166 elesfn$6[Symbol.iterator] = function () {
12167 var _this = this;
12168 // eslint-disable-line no-undef
12169 var entry = {
12170 value: undefined,
12171 done: false
12172 };
12173 var i = 0;
12174 var length = this.length;
12175 return _defineProperty$1({
12176 next: function next() {
12177 if (i < length) {
12178 entry.value = _this[i++];
12179 } else {
12180 entry.value = undefined;
12181 entry.done = true;
12182 }
12183 return entry;
12184 }
12185 }, Symbol.iterator, function () {
12186 // eslint-disable-line no-undef
12187 return this;
12188 });
12189 };
12190 }
12191};
12192defineSymbolIterator();
12193
12194var getLayoutDimensionOptions = defaults$g({
12195 nodeDimensionsIncludeLabels: false
12196});
12197var elesfn$5 = {
12198 // Calculates and returns node dimensions { x, y } based on options given
12199 layoutDimensions: function layoutDimensions(options) {
12200 options = getLayoutDimensionOptions(options);
12201 var dims;
12202 if (!this.takesUpSpace()) {
12203 dims = {
12204 w: 0,
12205 h: 0
12206 };
12207 } else if (options.nodeDimensionsIncludeLabels) {
12208 var bbDim = this.boundingBox();
12209 dims = {
12210 w: bbDim.w,
12211 h: bbDim.h
12212 };
12213 } else {
12214 dims = {
12215 w: this.outerWidth(),
12216 h: this.outerHeight()
12217 };
12218 }
12219
12220 // sanitise the dimensions for external layouts (avoid division by zero)
12221 if (dims.w === 0 || dims.h === 0) {
12222 dims.w = dims.h = 1;
12223 }
12224 return dims;
12225 },
12226 // using standard layout options, apply position function (w/ or w/o animation)
12227 layoutPositions: function layoutPositions(layout, options, fn) {
12228 var nodes = this.nodes().filter(function (n) {
12229 return !n.isParent();
12230 });
12231 var cy = this.cy();
12232 var layoutEles = options.eles; // nodes & edges
12233 var getMemoizeKey = function getMemoizeKey(node) {
12234 return node.id();
12235 };
12236 var fnMem = memoize$1(fn, getMemoizeKey); // memoized version of position function
12237
12238 layout.emit({
12239 type: 'layoutstart',
12240 layout: layout
12241 });
12242 layout.animations = [];
12243 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
12244 var center = {
12245 x: nodesBb.x1 + nodesBb.w / 2,
12246 y: nodesBb.y1 + nodesBb.h / 2
12247 };
12248 var spacingVector = {
12249 // scale from center of bounding box (not necessarily 0,0)
12250 x: (pos.x - center.x) * spacing,
12251 y: (pos.y - center.y) * spacing
12252 };
12253 return {
12254 x: center.x + spacingVector.x,
12255 y: center.y + spacingVector.y
12256 };
12257 };
12258 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
12259 var spacingBb = function spacingBb() {
12260 if (!useSpacingFactor) {
12261 return null;
12262 }
12263 var bb = makeBoundingBox();
12264 for (var i = 0; i < nodes.length; i++) {
12265 var node = nodes[i];
12266 var pos = fnMem(node, i);
12267 expandBoundingBoxByPoint(bb, pos.x, pos.y);
12268 }
12269 return bb;
12270 };
12271 var bb = spacingBb();
12272 var getFinalPos = memoize$1(function (node, i) {
12273 var newPos = fnMem(node, i);
12274 if (useSpacingFactor) {
12275 var spacing = Math.abs(options.spacingFactor);
12276 newPos = calculateSpacing(spacing, bb, newPos);
12277 }
12278 if (options.transform != null) {
12279 newPos = options.transform(node, newPos);
12280 }
12281 return newPos;
12282 }, getMemoizeKey);
12283 if (options.animate) {
12284 for (var i = 0; i < nodes.length; i++) {
12285 var node = nodes[i];
12286 var newPos = getFinalPos(node, i);
12287 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
12288 if (animateNode) {
12289 var ani = node.animation({
12290 position: newPos,
12291 duration: options.animationDuration,
12292 easing: options.animationEasing
12293 });
12294 layout.animations.push(ani);
12295 } else {
12296 node.position(newPos);
12297 }
12298 }
12299 if (options.fit) {
12300 var fitAni = cy.animation({
12301 fit: {
12302 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
12303 padding: options.padding
12304 },
12305 duration: options.animationDuration,
12306 easing: options.animationEasing
12307 });
12308 layout.animations.push(fitAni);
12309 } else if (options.zoom !== undefined && options.pan !== undefined) {
12310 var zoomPanAni = cy.animation({
12311 zoom: options.zoom,
12312 pan: options.pan,
12313 duration: options.animationDuration,
12314 easing: options.animationEasing
12315 });
12316 layout.animations.push(zoomPanAni);
12317 }
12318 layout.animations.forEach(function (ani) {
12319 return ani.play();
12320 });
12321 layout.one('layoutready', options.ready);
12322 layout.emit({
12323 type: 'layoutready',
12324 layout: layout
12325 });
12326 Promise$1.all(layout.animations.map(function (ani) {
12327 return ani.promise();
12328 })).then(function () {
12329 layout.one('layoutstop', options.stop);
12330 layout.emit({
12331 type: 'layoutstop',
12332 layout: layout
12333 });
12334 });
12335 } else {
12336 nodes.positions(getFinalPos);
12337 if (options.fit) {
12338 cy.fit(options.eles, options.padding);
12339 }
12340 if (options.zoom != null) {
12341 cy.zoom(options.zoom);
12342 }
12343 if (options.pan) {
12344 cy.pan(options.pan);
12345 }
12346 layout.one('layoutready', options.ready);
12347 layout.emit({
12348 type: 'layoutready',
12349 layout: layout
12350 });
12351 layout.one('layoutstop', options.stop);
12352 layout.emit({
12353 type: 'layoutstop',
12354 layout: layout
12355 });
12356 }
12357 return this; // chaining
12358 },
12359
12360 layout: function layout(options) {
12361 var cy = this.cy();
12362 return cy.makeLayout(extend({}, options, {
12363 eles: this
12364 }));
12365 }
12366};
12367
12368// aliases:
12369elesfn$5.createLayout = elesfn$5.makeLayout = elesfn$5.layout;
12370
12371function styleCache(key, fn, ele) {
12372 var _p = ele._private;
12373 var cache = _p.styleCache = _p.styleCache || [];
12374 var val;
12375 if ((val = cache[key]) != null) {
12376 return val;
12377 } else {
12378 val = cache[key] = fn(ele);
12379 return val;
12380 }
12381}
12382function cacheStyleFunction(key, fn) {
12383 key = hashString(key);
12384 return function cachedStyleFunction(ele) {
12385 return styleCache(key, fn, ele);
12386 };
12387}
12388function cachePrototypeStyleFunction(key, fn) {
12389 key = hashString(key);
12390 var selfFn = function selfFn(ele) {
12391 return fn.call(ele);
12392 };
12393 return function cachedPrototypeStyleFunction() {
12394 var ele = this[0];
12395 if (ele) {
12396 return styleCache(key, selfFn, ele);
12397 }
12398 };
12399}
12400var elesfn$4 = {
12401 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
12402 var cy = this.cy();
12403 var renderer = cy.renderer();
12404 var styleEnabled = cy.styleEnabled();
12405 if (renderer && styleEnabled) {
12406 renderer.recalculateRenderedStyle(this, useCache);
12407 }
12408 return this;
12409 },
12410 dirtyStyleCache: function dirtyStyleCache() {
12411 var cy = this.cy();
12412 var dirty = function dirty(ele) {
12413 return ele._private.styleCache = null;
12414 };
12415 if (cy.hasCompoundNodes()) {
12416 var eles;
12417 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12418 eles.merge(eles.connectedEdges());
12419 eles.forEach(dirty);
12420 } else {
12421 this.forEach(function (ele) {
12422 dirty(ele);
12423 ele.connectedEdges().forEach(dirty);
12424 });
12425 }
12426 return this;
12427 },
12428 // fully updates (recalculates) the style for the elements
12429 updateStyle: function updateStyle(notifyRenderer) {
12430 var cy = this._private.cy;
12431 if (!cy.styleEnabled()) {
12432 return this;
12433 }
12434 if (cy.batching()) {
12435 var bEles = cy._private.batchStyleEles;
12436 bEles.merge(this);
12437 return this; // chaining and exit early when batching
12438 }
12439
12440 var hasCompounds = cy.hasCompoundNodes();
12441 var updatedEles = this;
12442 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
12443 if (hasCompounds) {
12444 // then add everything up and down for compound selector checks
12445 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12446 }
12447
12448 // let changedEles = style.apply( updatedEles );
12449 var changedEles = updatedEles;
12450 if (notifyRenderer) {
12451 changedEles.emitAndNotify('style'); // let renderer know we changed style
12452 } else {
12453 changedEles.emit('style'); // just fire the event
12454 }
12455
12456 updatedEles.forEach(function (ele) {
12457 return ele._private.styleDirty = true;
12458 });
12459 return this; // chaining
12460 },
12461
12462 // private: clears dirty flag and recalculates style
12463 cleanStyle: function cleanStyle() {
12464 var cy = this.cy();
12465 if (!cy.styleEnabled()) {
12466 return;
12467 }
12468 for (var i = 0; i < this.length; i++) {
12469 var ele = this[i];
12470 if (ele._private.styleDirty) {
12471 // n.b. this flag should be set before apply() to avoid potential infinite recursion
12472 ele._private.styleDirty = false;
12473 cy.style().apply(ele);
12474 }
12475 }
12476 },
12477 // get the internal parsed style object for the specified property
12478 parsedStyle: function parsedStyle(property) {
12479 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12480 var ele = this[0];
12481 var cy = ele.cy();
12482 if (!cy.styleEnabled()) {
12483 return;
12484 }
12485 if (ele) {
12486 this.cleanStyle();
12487 var overriddenStyle = ele._private.style[property];
12488 if (overriddenStyle != null) {
12489 return overriddenStyle;
12490 } else if (includeNonDefault) {
12491 return cy.style().getDefaultProperty(property);
12492 } else {
12493 return null;
12494 }
12495 }
12496 },
12497 numericStyle: function numericStyle(property) {
12498 var ele = this[0];
12499 if (!ele.cy().styleEnabled()) {
12500 return;
12501 }
12502 if (ele) {
12503 var pstyle = ele.pstyle(property);
12504 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
12505 }
12506 },
12507 numericStyleUnits: function numericStyleUnits(property) {
12508 var ele = this[0];
12509 if (!ele.cy().styleEnabled()) {
12510 return;
12511 }
12512 if (ele) {
12513 return ele.pstyle(property).units;
12514 }
12515 },
12516 // get the specified css property as a rendered value (i.e. on-screen value)
12517 // or get the whole rendered style if no property specified (NB doesn't allow setting)
12518 renderedStyle: function renderedStyle(property) {
12519 var cy = this.cy();
12520 if (!cy.styleEnabled()) {
12521 return this;
12522 }
12523 var ele = this[0];
12524 if (ele) {
12525 return cy.style().getRenderedStyle(ele, property);
12526 }
12527 },
12528 // read the calculated css style of the element or override the style (via a bypass)
12529 style: function style(name, value) {
12530 var cy = this.cy();
12531 if (!cy.styleEnabled()) {
12532 return this;
12533 }
12534 var updateTransitions = false;
12535 var style = cy.style();
12536 if (plainObject(name)) {
12537 // then extend the bypass
12538 var props = name;
12539 style.applyBypass(this, props, updateTransitions);
12540 this.emitAndNotify('style'); // let the renderer know we've updated style
12541 } else if (string(name)) {
12542 if (value === undefined) {
12543 // then get the property from the style
12544 var ele = this[0];
12545 if (ele) {
12546 return style.getStylePropertyValue(ele, name);
12547 } else {
12548 // empty collection => can't get any value
12549 return;
12550 }
12551 } else {
12552 // then set the bypass with the property value
12553 style.applyBypass(this, name, value, updateTransitions);
12554 this.emitAndNotify('style'); // let the renderer know we've updated style
12555 }
12556 } else if (name === undefined) {
12557 var _ele = this[0];
12558 if (_ele) {
12559 return style.getRawStyle(_ele);
12560 } else {
12561 // empty collection => can't get any value
12562 return;
12563 }
12564 }
12565 return this; // chaining
12566 },
12567
12568 removeStyle: function removeStyle(names) {
12569 var cy = this.cy();
12570 if (!cy.styleEnabled()) {
12571 return this;
12572 }
12573 var updateTransitions = false;
12574 var style = cy.style();
12575 var eles = this;
12576 if (names === undefined) {
12577 for (var i = 0; i < eles.length; i++) {
12578 var ele = eles[i];
12579 style.removeAllBypasses(ele, updateTransitions);
12580 }
12581 } else {
12582 names = names.split(/\s+/);
12583 for (var _i = 0; _i < eles.length; _i++) {
12584 var _ele2 = eles[_i];
12585 style.removeBypasses(_ele2, names, updateTransitions);
12586 }
12587 }
12588 this.emitAndNotify('style'); // let the renderer know we've updated style
12589
12590 return this; // chaining
12591 },
12592
12593 show: function show() {
12594 this.css('display', 'element');
12595 return this; // chaining
12596 },
12597
12598 hide: function hide() {
12599 this.css('display', 'none');
12600 return this; // chaining
12601 },
12602
12603 effectiveOpacity: function effectiveOpacity() {
12604 var cy = this.cy();
12605 if (!cy.styleEnabled()) {
12606 return 1;
12607 }
12608 var hasCompoundNodes = cy.hasCompoundNodes();
12609 var ele = this[0];
12610 if (ele) {
12611 var _p = ele._private;
12612 var parentOpacity = ele.pstyle('opacity').value;
12613 if (!hasCompoundNodes) {
12614 return parentOpacity;
12615 }
12616 var parents = !_p.data.parent ? null : ele.parents();
12617 if (parents) {
12618 for (var i = 0; i < parents.length; i++) {
12619 var parent = parents[i];
12620 var opacity = parent.pstyle('opacity').value;
12621 parentOpacity = opacity * parentOpacity;
12622 }
12623 }
12624 return parentOpacity;
12625 }
12626 },
12627 transparent: function transparent() {
12628 var cy = this.cy();
12629 if (!cy.styleEnabled()) {
12630 return false;
12631 }
12632 var ele = this[0];
12633 var hasCompoundNodes = ele.cy().hasCompoundNodes();
12634 if (ele) {
12635 if (!hasCompoundNodes) {
12636 return ele.pstyle('opacity').value === 0;
12637 } else {
12638 return ele.effectiveOpacity() === 0;
12639 }
12640 }
12641 },
12642 backgrounding: function backgrounding() {
12643 var cy = this.cy();
12644 if (!cy.styleEnabled()) {
12645 return false;
12646 }
12647 var ele = this[0];
12648 return ele._private.backgrounding ? true : false;
12649 }
12650};
12651function checkCompound(ele, parentOk) {
12652 var _p = ele._private;
12653 var parents = _p.data.parent ? ele.parents() : null;
12654 if (parents) {
12655 for (var i = 0; i < parents.length; i++) {
12656 var parent = parents[i];
12657 if (!parentOk(parent)) {
12658 return false;
12659 }
12660 }
12661 }
12662 return true;
12663}
12664function defineDerivedStateFunction(specs) {
12665 var ok = specs.ok;
12666 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
12667 var parentOk = specs.parentOk || specs.ok;
12668 return function () {
12669 var cy = this.cy();
12670 if (!cy.styleEnabled()) {
12671 return true;
12672 }
12673 var ele = this[0];
12674 var hasCompoundNodes = cy.hasCompoundNodes();
12675 if (ele) {
12676 var _p = ele._private;
12677 if (!ok(ele)) {
12678 return false;
12679 }
12680 if (ele.isNode()) {
12681 return !hasCompoundNodes || checkCompound(ele, parentOk);
12682 } else {
12683 var src = _p.source;
12684 var tgt = _p.target;
12685 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
12686 }
12687 }
12688 };
12689}
12690var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
12691 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
12692});
12693elesfn$4.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
12694 ok: eleTakesUpSpace
12695}));
12696var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
12697 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
12698});
12699var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
12700 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
12701});
12702elesfn$4.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
12703 ok: eleInteractive,
12704 parentOk: parentInteractive,
12705 edgeOkViaNode: eleTakesUpSpace
12706}));
12707elesfn$4.noninteractive = function () {
12708 var ele = this[0];
12709 if (ele) {
12710 return !ele.interactive();
12711 }
12712};
12713var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
12714 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
12715});
12716var edgeVisibleViaNode = eleTakesUpSpace;
12717elesfn$4.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
12718 ok: eleVisible,
12719 edgeOkViaNode: edgeVisibleViaNode
12720}));
12721elesfn$4.hidden = function () {
12722 var ele = this[0];
12723 if (ele) {
12724 return !ele.visible();
12725 }
12726};
12727elesfn$4.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
12728 if (!this.cy().styleEnabled()) {
12729 return false;
12730 }
12731 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
12732});
12733elesfn$4.bypass = elesfn$4.css = elesfn$4.style;
12734elesfn$4.renderedCss = elesfn$4.renderedStyle;
12735elesfn$4.removeBypass = elesfn$4.removeCss = elesfn$4.removeStyle;
12736elesfn$4.pstyle = elesfn$4.parsedStyle;
12737
12738var elesfn$3 = {};
12739function defineSwitchFunction(params) {
12740 return function () {
12741 var args = arguments;
12742 var changedEles = [];
12743
12744 // e.g. cy.nodes().select( data, handler )
12745 if (args.length === 2) {
12746 var data = args[0];
12747 var handler = args[1];
12748 this.on(params.event, data, handler);
12749 }
12750
12751 // e.g. cy.nodes().select( handler )
12752 else if (args.length === 1 && fn$6(args[0])) {
12753 var _handler = args[0];
12754 this.on(params.event, _handler);
12755 }
12756
12757 // e.g. cy.nodes().select()
12758 // e.g. (private) cy.nodes().select(['tapselect'])
12759 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12760 var addlEvents = args.length === 1 ? args[0] : null;
12761 for (var i = 0; i < this.length; i++) {
12762 var ele = this[i];
12763 var able = !params.ableField || ele._private[params.ableField];
12764 var changed = ele._private[params.field] != params.value;
12765 if (params.overrideAble) {
12766 var overrideAble = params.overrideAble(ele);
12767 if (overrideAble !== undefined) {
12768 able = overrideAble;
12769 if (!overrideAble) {
12770 return this;
12771 } // to save cycles assume not able for all on override
12772 }
12773 }
12774
12775 if (able) {
12776 ele._private[params.field] = params.value;
12777 if (changed) {
12778 changedEles.push(ele);
12779 }
12780 }
12781 }
12782 var changedColl = this.spawn(changedEles);
12783 changedColl.updateStyle(); // change of state => possible change of style
12784 changedColl.emit(params.event);
12785 if (addlEvents) {
12786 changedColl.emit(addlEvents);
12787 }
12788 }
12789 return this;
12790 };
12791}
12792function defineSwitchSet(params) {
12793 elesfn$3[params.field] = function () {
12794 var ele = this[0];
12795 if (ele) {
12796 if (params.overrideField) {
12797 var val = params.overrideField(ele);
12798 if (val !== undefined) {
12799 return val;
12800 }
12801 }
12802 return ele._private[params.field];
12803 }
12804 };
12805 elesfn$3[params.on] = defineSwitchFunction({
12806 event: params.on,
12807 field: params.field,
12808 ableField: params.ableField,
12809 overrideAble: params.overrideAble,
12810 value: true
12811 });
12812 elesfn$3[params.off] = defineSwitchFunction({
12813 event: params.off,
12814 field: params.field,
12815 ableField: params.ableField,
12816 overrideAble: params.overrideAble,
12817 value: false
12818 });
12819}
12820defineSwitchSet({
12821 field: 'locked',
12822 overrideField: function overrideField(ele) {
12823 return ele.cy().autolock() ? true : undefined;
12824 },
12825 on: 'lock',
12826 off: 'unlock'
12827});
12828defineSwitchSet({
12829 field: 'grabbable',
12830 overrideField: function overrideField(ele) {
12831 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12832 },
12833 on: 'grabify',
12834 off: 'ungrabify'
12835});
12836defineSwitchSet({
12837 field: 'selected',
12838 ableField: 'selectable',
12839 overrideAble: function overrideAble(ele) {
12840 return ele.cy().autounselectify() ? false : undefined;
12841 },
12842 on: 'select',
12843 off: 'unselect'
12844});
12845defineSwitchSet({
12846 field: 'selectable',
12847 overrideField: function overrideField(ele) {
12848 return ele.cy().autounselectify() ? false : undefined;
12849 },
12850 on: 'selectify',
12851 off: 'unselectify'
12852});
12853elesfn$3.deselect = elesfn$3.unselect;
12854elesfn$3.grabbed = function () {
12855 var ele = this[0];
12856 if (ele) {
12857 return ele._private.grabbed;
12858 }
12859};
12860defineSwitchSet({
12861 field: 'active',
12862 on: 'activate',
12863 off: 'unactivate'
12864});
12865defineSwitchSet({
12866 field: 'pannable',
12867 on: 'panify',
12868 off: 'unpanify'
12869});
12870elesfn$3.inactive = function () {
12871 var ele = this[0];
12872 if (ele) {
12873 return !ele._private.active;
12874 }
12875};
12876
12877var elesfn$2 = {};
12878
12879// DAG functions
12880////////////////
12881
12882var defineDagExtremity = function defineDagExtremity(params) {
12883 return function dagExtremityImpl(selector) {
12884 var eles = this;
12885 var ret = [];
12886 for (var i = 0; i < eles.length; i++) {
12887 var ele = eles[i];
12888 if (!ele.isNode()) {
12889 continue;
12890 }
12891 var disqualified = false;
12892 var edges = ele.connectedEdges();
12893 for (var j = 0; j < edges.length; j++) {
12894 var edge = edges[j];
12895 var src = edge.source();
12896 var tgt = edge.target();
12897 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12898 disqualified = true;
12899 break;
12900 }
12901 }
12902 if (!disqualified) {
12903 ret.push(ele);
12904 }
12905 }
12906 return this.spawn(ret, true).filter(selector);
12907 };
12908};
12909var defineDagOneHop = function defineDagOneHop(params) {
12910 return function (selector) {
12911 var eles = this;
12912 var oEles = [];
12913 for (var i = 0; i < eles.length; i++) {
12914 var ele = eles[i];
12915 if (!ele.isNode()) {
12916 continue;
12917 }
12918 var edges = ele.connectedEdges();
12919 for (var j = 0; j < edges.length; j++) {
12920 var edge = edges[j];
12921 var src = edge.source();
12922 var tgt = edge.target();
12923 if (params.outgoing && src === ele) {
12924 oEles.push(edge);
12925 oEles.push(tgt);
12926 } else if (params.incoming && tgt === ele) {
12927 oEles.push(edge);
12928 oEles.push(src);
12929 }
12930 }
12931 }
12932 return this.spawn(oEles, true).filter(selector);
12933 };
12934};
12935var defineDagAllHops = function defineDagAllHops(params) {
12936 return function (selector) {
12937 var eles = this;
12938 var sEles = [];
12939 var sElesIds = {};
12940 for (;;) {
12941 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12942 if (next.length === 0) {
12943 break;
12944 } // done if none left
12945
12946 var newNext = false;
12947 for (var i = 0; i < next.length; i++) {
12948 var n = next[i];
12949 var nid = n.id();
12950 if (!sElesIds[nid]) {
12951 sElesIds[nid] = true;
12952 sEles.push(n);
12953 newNext = true;
12954 }
12955 }
12956 if (!newNext) {
12957 break;
12958 } // done if touched all outgoers already
12959
12960 eles = next;
12961 }
12962 return this.spawn(sEles, true).filter(selector);
12963 };
12964};
12965elesfn$2.clearTraversalCache = function () {
12966 for (var i = 0; i < this.length; i++) {
12967 this[i]._private.traversalCache = null;
12968 }
12969};
12970extend(elesfn$2, {
12971 // get the root nodes in the DAG
12972 roots: defineDagExtremity({
12973 noIncomingEdges: true
12974 }),
12975 // get the leaf nodes in the DAG
12976 leaves: defineDagExtremity({
12977 noOutgoingEdges: true
12978 }),
12979 // normally called children in graph theory
12980 // these nodes =edges=> outgoing nodes
12981 outgoers: cache(defineDagOneHop({
12982 outgoing: true
12983 }), 'outgoers'),
12984 // aka DAG descendants
12985 successors: defineDagAllHops({
12986 outgoing: true
12987 }),
12988 // normally called parents in graph theory
12989 // these nodes <=edges= incoming nodes
12990 incomers: cache(defineDagOneHop({
12991 incoming: true
12992 }), 'incomers'),
12993 // aka DAG ancestors
12994 predecessors: defineDagAllHops({
12995 incoming: true
12996 })
12997});
12998
12999// Neighbourhood functions
13000//////////////////////////
13001
13002extend(elesfn$2, {
13003 neighborhood: cache(function (selector) {
13004 var elements = [];
13005 var nodes = this.nodes();
13006 for (var i = 0; i < nodes.length; i++) {
13007 // for all nodes
13008 var node = nodes[i];
13009 var connectedEdges = node.connectedEdges();
13010
13011 // for each connected edge, add the edge and the other node
13012 for (var j = 0; j < connectedEdges.length; j++) {
13013 var edge = connectedEdges[j];
13014 var src = edge.source();
13015 var tgt = edge.target();
13016 var otherNode = node === src ? tgt : src;
13017
13018 // need check in case of loop
13019 if (otherNode.length > 0) {
13020 elements.push(otherNode[0]); // add node 1 hop away
13021 }
13022
13023 // add connected edge
13024 elements.push(edge[0]);
13025 }
13026 }
13027 return this.spawn(elements, true).filter(selector);
13028 }, 'neighborhood'),
13029 closedNeighborhood: function closedNeighborhood(selector) {
13030 return this.neighborhood().add(this).filter(selector);
13031 },
13032 openNeighborhood: function openNeighborhood(selector) {
13033 return this.neighborhood(selector);
13034 }
13035});
13036
13037// aliases
13038elesfn$2.neighbourhood = elesfn$2.neighborhood;
13039elesfn$2.closedNeighbourhood = elesfn$2.closedNeighborhood;
13040elesfn$2.openNeighbourhood = elesfn$2.openNeighborhood;
13041
13042// Edge functions
13043/////////////////
13044
13045extend(elesfn$2, {
13046 source: cache(function sourceImpl(selector) {
13047 var ele = this[0];
13048 var src;
13049 if (ele) {
13050 src = ele._private.source || ele.cy().collection();
13051 }
13052 return src && selector ? src.filter(selector) : src;
13053 }, 'source'),
13054 target: cache(function targetImpl(selector) {
13055 var ele = this[0];
13056 var tgt;
13057 if (ele) {
13058 tgt = ele._private.target || ele.cy().collection();
13059 }
13060 return tgt && selector ? tgt.filter(selector) : tgt;
13061 }, 'target'),
13062 sources: defineSourceFunction({
13063 attr: 'source'
13064 }),
13065 targets: defineSourceFunction({
13066 attr: 'target'
13067 })
13068});
13069function defineSourceFunction(params) {
13070 return function sourceImpl(selector) {
13071 var sources = [];
13072 for (var i = 0; i < this.length; i++) {
13073 var ele = this[i];
13074 var src = ele._private[params.attr];
13075 if (src) {
13076 sources.push(src);
13077 }
13078 }
13079 return this.spawn(sources, true).filter(selector);
13080 };
13081}
13082extend(elesfn$2, {
13083 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
13084 edgesTo: cache(defineEdgesWithFunction({
13085 thisIsSrc: true
13086 }), 'edgesTo')
13087});
13088function defineEdgesWithFunction(params) {
13089 return function edgesWithImpl(otherNodes) {
13090 var elements = [];
13091 var cy = this._private.cy;
13092 var p = params || {};
13093
13094 // get elements if a selector is specified
13095 if (string(otherNodes)) {
13096 otherNodes = cy.$(otherNodes);
13097 }
13098 for (var h = 0; h < otherNodes.length; h++) {
13099 var edges = otherNodes[h]._private.edges;
13100 for (var i = 0; i < edges.length; i++) {
13101 var edge = edges[i];
13102 var edgeData = edge._private.data;
13103 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
13104 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
13105 var edgeConnectsThisAndOther = thisToOther || otherToThis;
13106 if (!edgeConnectsThisAndOther) {
13107 continue;
13108 }
13109 if (p.thisIsSrc || p.thisIsTgt) {
13110 if (p.thisIsSrc && !thisToOther) {
13111 continue;
13112 }
13113 if (p.thisIsTgt && !otherToThis) {
13114 continue;
13115 }
13116 }
13117 elements.push(edge);
13118 }
13119 }
13120 return this.spawn(elements, true);
13121 };
13122}
13123extend(elesfn$2, {
13124 connectedEdges: cache(function (selector) {
13125 var retEles = [];
13126 var eles = this;
13127 for (var i = 0; i < eles.length; i++) {
13128 var node = eles[i];
13129 if (!node.isNode()) {
13130 continue;
13131 }
13132 var edges = node._private.edges;
13133 for (var j = 0; j < edges.length; j++) {
13134 var edge = edges[j];
13135 retEles.push(edge);
13136 }
13137 }
13138 return this.spawn(retEles, true).filter(selector);
13139 }, 'connectedEdges'),
13140 connectedNodes: cache(function (selector) {
13141 var retEles = [];
13142 var eles = this;
13143 for (var i = 0; i < eles.length; i++) {
13144 var edge = eles[i];
13145 if (!edge.isEdge()) {
13146 continue;
13147 }
13148 retEles.push(edge.source()[0]);
13149 retEles.push(edge.target()[0]);
13150 }
13151 return this.spawn(retEles, true).filter(selector);
13152 }, 'connectedNodes'),
13153 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
13154 codirectedEdges: cache(defineParallelEdgesFunction({
13155 codirected: true
13156 }), 'codirectedEdges')
13157});
13158function defineParallelEdgesFunction(params) {
13159 var defaults = {
13160 codirected: false
13161 };
13162 params = extend({}, defaults, params);
13163 return function parallelEdgesImpl(selector) {
13164 // micro-optimised for renderer
13165 var elements = [];
13166 var edges = this.edges();
13167 var p = params;
13168
13169 // look at all the edges in the collection
13170 for (var i = 0; i < edges.length; i++) {
13171 var edge1 = edges[i];
13172 var edge1_p = edge1._private;
13173 var src1 = edge1_p.source;
13174 var srcid1 = src1._private.data.id;
13175 var tgtid1 = edge1_p.data.target;
13176 var srcEdges1 = src1._private.edges;
13177
13178 // look at edges connected to the src node of this edge
13179 for (var j = 0; j < srcEdges1.length; j++) {
13180 var edge2 = srcEdges1[j];
13181 var edge2data = edge2._private.data;
13182 var tgtid2 = edge2data.target;
13183 var srcid2 = edge2data.source;
13184 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
13185 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
13186 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
13187 elements.push(edge2);
13188 }
13189 }
13190 }
13191 return this.spawn(elements, true).filter(selector);
13192 };
13193}
13194
13195// Misc functions
13196/////////////////
13197
13198extend(elesfn$2, {
13199 components: function components(root) {
13200 var self = this;
13201 var cy = self.cy();
13202 var visited = cy.collection();
13203 var unvisited = root == null ? self.nodes() : root.nodes();
13204 var components = [];
13205 if (root != null && unvisited.empty()) {
13206 // root may contain only edges
13207 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
13208 }
13209
13210 var visitInComponent = function visitInComponent(node, component) {
13211 visited.merge(node);
13212 unvisited.unmerge(node);
13213 component.merge(node);
13214 };
13215 if (unvisited.empty()) {
13216 return self.spawn();
13217 }
13218 var _loop = function _loop() {
13219 // each iteration yields a component
13220 var cmpt = cy.collection();
13221 components.push(cmpt);
13222 var root = unvisited[0];
13223 visitInComponent(root, cmpt);
13224 self.bfs({
13225 directed: false,
13226 roots: root,
13227 visit: function visit(v) {
13228 return visitInComponent(v, cmpt);
13229 }
13230 });
13231 cmpt.forEach(function (node) {
13232 node.connectedEdges().forEach(function (e) {
13233 // connectedEdges() usually cached
13234 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
13235 // has() is cheap
13236 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
13237 }
13238 });
13239 });
13240 };
13241 do {
13242 _loop();
13243 } while (unvisited.length > 0);
13244 return components;
13245 },
13246 component: function component() {
13247 var ele = this[0];
13248 return ele.cy().mutableElements().components(ele)[0];
13249 }
13250});
13251elesfn$2.componentsOf = elesfn$2.components;
13252
13253// represents a set of nodes, edges, or both together
13254var Collection = function Collection(cy, elements) {
13255 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
13256 var removed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
13257 if (cy === undefined) {
13258 error('A collection must have a reference to the core');
13259 return;
13260 }
13261 var map = new Map$2();
13262 var createdElements = false;
13263 if (!elements) {
13264 elements = [];
13265 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
13266 createdElements = true;
13267
13268 // make elements from json and restore all at once later
13269 var eles = [];
13270 var elesIds = new Set$1();
13271 for (var i = 0, l = elements.length; i < l; i++) {
13272 var json = elements[i];
13273 if (json.data == null) {
13274 json.data = {};
13275 }
13276 var _data = json.data;
13277
13278 // make sure newly created elements have valid ids
13279 if (_data.id == null) {
13280 _data.id = uuid();
13281 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
13282 continue; // can't create element if prior id already exists
13283 }
13284
13285 var ele = new Element(cy, json, false);
13286 eles.push(ele);
13287 elesIds.add(_data.id);
13288 }
13289 elements = eles;
13290 }
13291 this.length = 0;
13292 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
13293 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
13294 if (element$1 == null) {
13295 continue;
13296 }
13297 var id = element$1._private.data.id;
13298 if (!unique || !map.has(id)) {
13299 if (unique) {
13300 map.set(id, {
13301 index: this.length,
13302 ele: element$1
13303 });
13304 }
13305 this[this.length] = element$1;
13306 this.length++;
13307 }
13308 }
13309 this._private = {
13310 eles: this,
13311 cy: cy,
13312 get map() {
13313 if (this.lazyMap == null) {
13314 this.rebuildMap();
13315 }
13316 return this.lazyMap;
13317 },
13318 set map(m) {
13319 this.lazyMap = m;
13320 },
13321 rebuildMap: function rebuildMap() {
13322 var m = this.lazyMap = new Map$2();
13323 var eles = this.eles;
13324 for (var _i2 = 0; _i2 < eles.length; _i2++) {
13325 var _ele = eles[_i2];
13326 m.set(_ele.id(), {
13327 index: _i2,
13328 ele: _ele
13329 });
13330 }
13331 }
13332 };
13333 if (unique) {
13334 this._private.map = map;
13335 }
13336
13337 // restore the elements if we created them from json
13338 if (createdElements && !removed) {
13339 this.restore();
13340 }
13341};
13342
13343// Functions
13344////////////////////////////////////////////////////////////////////////////////////////////////////
13345
13346// keep the prototypes in sync (an element has the same functions as a collection)
13347// and use elefn and elesfn as shorthands to the prototypes
13348var elesfn$1 = Element.prototype = Collection.prototype = Object.create(Array.prototype);
13349elesfn$1.instanceString = function () {
13350 return 'collection';
13351};
13352elesfn$1.spawn = function (eles, unique) {
13353 return new Collection(this.cy(), eles, unique);
13354};
13355elesfn$1.spawnSelf = function () {
13356 return this.spawn(this);
13357};
13358elesfn$1.cy = function () {
13359 return this._private.cy;
13360};
13361elesfn$1.renderer = function () {
13362 return this._private.cy.renderer();
13363};
13364elesfn$1.element = function () {
13365 return this[0];
13366};
13367elesfn$1.collection = function () {
13368 if (collection(this)) {
13369 return this;
13370 } else {
13371 // an element
13372 return new Collection(this._private.cy, [this]);
13373 }
13374};
13375elesfn$1.unique = function () {
13376 return new Collection(this._private.cy, this, true);
13377};
13378elesfn$1.hasElementWithId = function (id) {
13379 id = '' + id; // id must be string
13380
13381 return this._private.map.has(id);
13382};
13383elesfn$1.getElementById = function (id) {
13384 id = '' + id; // id must be string
13385
13386 var cy = this._private.cy;
13387 var entry = this._private.map.get(id);
13388 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
13389};
13390
13391elesfn$1.$id = elesfn$1.getElementById;
13392elesfn$1.poolIndex = function () {
13393 var cy = this._private.cy;
13394 var eles = cy._private.elements;
13395 var id = this[0]._private.data.id;
13396 return eles._private.map.get(id).index;
13397};
13398elesfn$1.indexOf = function (ele) {
13399 var id = ele[0]._private.data.id;
13400 return this._private.map.get(id).index;
13401};
13402elesfn$1.indexOfId = function (id) {
13403 id = '' + id; // id must be string
13404
13405 return this._private.map.get(id).index;
13406};
13407elesfn$1.json = function (obj) {
13408 var ele = this.element();
13409 var cy = this.cy();
13410 if (ele == null && obj) {
13411 return this;
13412 } // can't set to no eles
13413
13414 if (ele == null) {
13415 return undefined;
13416 } // can't get from no eles
13417
13418 var p = ele._private;
13419 if (plainObject(obj)) {
13420 // set
13421
13422 cy.startBatch();
13423 if (obj.data) {
13424 ele.data(obj.data);
13425 var _data2 = p.data;
13426 if (ele.isEdge()) {
13427 // source and target are immutable via data()
13428 var move = false;
13429 var spec = {};
13430 var src = obj.data.source;
13431 var tgt = obj.data.target;
13432 if (src != null && src != _data2.source) {
13433 spec.source = '' + src; // id must be string
13434 move = true;
13435 }
13436 if (tgt != null && tgt != _data2.target) {
13437 spec.target = '' + tgt; // id must be string
13438 move = true;
13439 }
13440 if (move) {
13441 ele = ele.move(spec);
13442 }
13443 } else {
13444 // parent is immutable via data()
13445 var newParentValSpecd = ('parent' in obj.data);
13446 var parent = obj.data.parent;
13447 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
13448 if (parent === undefined) {
13449 // can't set undefined imperatively, so use null
13450 parent = null;
13451 }
13452 if (parent != null) {
13453 parent = '' + parent; // id must be string
13454 }
13455
13456 ele = ele.move({
13457 parent: parent
13458 });
13459 }
13460 }
13461 }
13462 if (obj.position) {
13463 ele.position(obj.position);
13464 }
13465
13466 // ignore group -- immutable
13467
13468 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
13469 var obj_k = obj[k];
13470 if (obj_k != null && obj_k !== p[k]) {
13471 if (obj_k) {
13472 ele[trueFnName]();
13473 } else {
13474 ele[falseFnName]();
13475 }
13476 }
13477 };
13478 checkSwitch('removed', 'remove', 'restore');
13479 checkSwitch('selected', 'select', 'unselect');
13480 checkSwitch('selectable', 'selectify', 'unselectify');
13481 checkSwitch('locked', 'lock', 'unlock');
13482 checkSwitch('grabbable', 'grabify', 'ungrabify');
13483 checkSwitch('pannable', 'panify', 'unpanify');
13484 if (obj.classes != null) {
13485 ele.classes(obj.classes);
13486 }
13487 cy.endBatch();
13488 return this;
13489 } else if (obj === undefined) {
13490 // get
13491
13492 var json = {
13493 data: copy(p.data),
13494 position: copy(p.position),
13495 group: p.group,
13496 removed: p.removed,
13497 selected: p.selected,
13498 selectable: p.selectable,
13499 locked: p.locked,
13500 grabbable: p.grabbable,
13501 pannable: p.pannable,
13502 classes: null
13503 };
13504 json.classes = '';
13505 var i = 0;
13506 p.classes.forEach(function (cls) {
13507 return json.classes += i++ === 0 ? cls : ' ' + cls;
13508 });
13509 return json;
13510 }
13511};
13512elesfn$1.jsons = function () {
13513 var jsons = [];
13514 for (var i = 0; i < this.length; i++) {
13515 var ele = this[i];
13516 var json = ele.json();
13517 jsons.push(json);
13518 }
13519 return jsons;
13520};
13521elesfn$1.clone = function () {
13522 var cy = this.cy();
13523 var elesArr = [];
13524 for (var i = 0; i < this.length; i++) {
13525 var ele = this[i];
13526 var json = ele.json();
13527 var clone = new Element(cy, json, false); // NB no restore
13528
13529 elesArr.push(clone);
13530 }
13531 return new Collection(cy, elesArr);
13532};
13533elesfn$1.copy = elesfn$1.clone;
13534elesfn$1.restore = function () {
13535 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13536 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13537 var self = this;
13538 var cy = self.cy();
13539 var cy_p = cy._private;
13540
13541 // create arrays of nodes and edges, since we need to
13542 // restore the nodes first
13543 var nodes = [];
13544 var edges = [];
13545 var elements;
13546 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
13547 var ele = self[_i3];
13548 if (addToPool && !ele.removed()) {
13549 // don't need to handle this ele
13550 continue;
13551 }
13552
13553 // keep nodes first in the array and edges after
13554 if (ele.isNode()) {
13555 // put to front of array if node
13556 nodes.push(ele);
13557 } else {
13558 // put to end of array if edge
13559 edges.push(ele);
13560 }
13561 }
13562 elements = nodes.concat(edges);
13563 var i;
13564 var removeFromElements = function removeFromElements() {
13565 elements.splice(i, 1);
13566 i--;
13567 };
13568
13569 // now, restore each element
13570 for (i = 0; i < elements.length; i++) {
13571 var _ele2 = elements[i];
13572 var _private = _ele2._private;
13573 var _data3 = _private.data;
13574
13575 // the traversal cache should start fresh when ele is added
13576 _ele2.clearTraversalCache();
13577
13578 // set id and validate
13579 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
13580 _data3.id = uuid();
13581 } else if (number$1(_data3.id)) {
13582 _data3.id = '' + _data3.id; // now it's a string
13583 } else if (emptyString(_data3.id) || !string(_data3.id)) {
13584 error('Can not create element with invalid string ID `' + _data3.id + '`');
13585
13586 // can't create element if it has empty string as id or non-string id
13587 removeFromElements();
13588 continue;
13589 } else if (cy.hasElementWithId(_data3.id)) {
13590 error('Can not create second element with ID `' + _data3.id + '`');
13591
13592 // can't create element if one already has that id
13593 removeFromElements();
13594 continue;
13595 }
13596 var id = _data3.id; // id is finalised, now let's keep a ref
13597
13598 if (_ele2.isNode()) {
13599 // extra checks for nodes
13600 var pos = _private.position;
13601
13602 // make sure the nodes have a defined position
13603
13604 if (pos.x == null) {
13605 pos.x = 0;
13606 }
13607 if (pos.y == null) {
13608 pos.y = 0;
13609 }
13610 }
13611 if (_ele2.isEdge()) {
13612 // extra checks for edges
13613
13614 var edge = _ele2;
13615 var fields = ['source', 'target'];
13616 var fieldsLength = fields.length;
13617 var badSourceOrTarget = false;
13618 for (var j = 0; j < fieldsLength; j++) {
13619 var field = fields[j];
13620 var val = _data3[field];
13621 if (number$1(val)) {
13622 val = _data3[field] = '' + _data3[field]; // now string
13623 }
13624
13625 if (val == null || val === '') {
13626 // can't create if source or target is not defined properly
13627 error('Can not create edge `' + id + '` with unspecified ' + field);
13628 badSourceOrTarget = true;
13629 } else if (!cy.hasElementWithId(val)) {
13630 // can't create edge if one of its nodes doesn't exist
13631 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13632 badSourceOrTarget = true;
13633 }
13634 }
13635 if (badSourceOrTarget) {
13636 removeFromElements();
13637 continue;
13638 } // can't create this
13639
13640 var src = cy.getElementById(_data3.source);
13641 var tgt = cy.getElementById(_data3.target);
13642
13643 // only one edge in node if loop
13644 if (src.same(tgt)) {
13645 src._private.edges.push(edge);
13646 } else {
13647 src._private.edges.push(edge);
13648 tgt._private.edges.push(edge);
13649 }
13650 edge._private.source = src;
13651 edge._private.target = tgt;
13652 } // if is edge
13653
13654 // create mock ids / indexes maps for element so it can be used like collections
13655 _private.map = new Map$2();
13656 _private.map.set(id, {
13657 ele: _ele2,
13658 index: 0
13659 });
13660 _private.removed = false;
13661 if (addToPool) {
13662 cy.addToPool(_ele2);
13663 }
13664 } // for each element
13665
13666 // do compound node sanity checks
13667 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13668 // each node
13669 var node = nodes[_i4];
13670 var _data4 = node._private.data;
13671 if (number$1(_data4.parent)) {
13672 // then automake string
13673 _data4.parent = '' + _data4.parent;
13674 }
13675 var parentId = _data4.parent;
13676 var specifiedParent = parentId != null;
13677 if (specifiedParent || node._private.parent) {
13678 var parent = node._private.parent ? cy.collection().merge(node._private.parent) : cy.getElementById(parentId);
13679 if (parent.empty()) {
13680 // non-existant parent; just remove it
13681 _data4.parent = undefined;
13682 } else if (parent[0].removed()) {
13683 warn('Node added with missing parent, reference to parent removed');
13684 _data4.parent = undefined;
13685 node._private.parent = null;
13686 } else {
13687 var selfAsParent = false;
13688 var ancestor = parent;
13689 while (!ancestor.empty()) {
13690 if (node.same(ancestor)) {
13691 // mark self as parent and remove from data
13692 selfAsParent = true;
13693 _data4.parent = undefined; // remove parent reference
13694
13695 // exit or we loop forever
13696 break;
13697 }
13698 ancestor = ancestor.parent();
13699 }
13700 if (!selfAsParent) {
13701 // connect with children
13702 parent[0]._private.children.push(node);
13703 node._private.parent = parent[0];
13704
13705 // let the core know we have a compound graph
13706 cy_p.hasCompoundNodes = true;
13707 }
13708 } // else
13709 } // if specified parent
13710 } // for each node
13711
13712 if (elements.length > 0) {
13713 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13714 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13715 var _ele3 = restored[_i5];
13716 if (_ele3.isNode()) {
13717 continue;
13718 }
13719
13720 // adding an edge invalidates the traversal caches for the parallel edges
13721 _ele3.parallelEdges().clearTraversalCache();
13722
13723 // adding an edge invalidates the traversal cache for the connected nodes
13724 _ele3.source().clearTraversalCache();
13725 _ele3.target().clearTraversalCache();
13726 }
13727 var toUpdateStyle;
13728 if (cy_p.hasCompoundNodes) {
13729 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13730 } else {
13731 toUpdateStyle = restored;
13732 }
13733 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13734 if (notifyRenderer) {
13735 restored.emitAndNotify('add');
13736 } else if (addToPool) {
13737 restored.emit('add');
13738 }
13739 }
13740 return self; // chainability
13741};
13742
13743elesfn$1.removed = function () {
13744 var ele = this[0];
13745 return ele && ele._private.removed;
13746};
13747elesfn$1.inside = function () {
13748 var ele = this[0];
13749 return ele && !ele._private.removed;
13750};
13751elesfn$1.remove = function () {
13752 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13753 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13754 var self = this;
13755 var elesToRemove = [];
13756 var elesToRemoveIds = {};
13757 var cy = self._private.cy;
13758
13759 // add connected edges
13760 function addConnectedEdges(node) {
13761 var edges = node._private.edges;
13762 for (var i = 0; i < edges.length; i++) {
13763 add(edges[i]);
13764 }
13765 }
13766
13767 // add descendant nodes
13768 function addChildren(node) {
13769 var children = node._private.children;
13770 for (var i = 0; i < children.length; i++) {
13771 add(children[i]);
13772 }
13773 }
13774 function add(ele) {
13775 var alreadyAdded = elesToRemoveIds[ele.id()];
13776 if (removeFromPool && ele.removed() || alreadyAdded) {
13777 return;
13778 } else {
13779 elesToRemoveIds[ele.id()] = true;
13780 }
13781 if (ele.isNode()) {
13782 elesToRemove.push(ele); // nodes are removed last
13783
13784 addConnectedEdges(ele);
13785 addChildren(ele);
13786 } else {
13787 elesToRemove.unshift(ele); // edges are removed first
13788 }
13789 }
13790
13791 // make the list of elements to remove
13792 // (may be removing more than specified due to connected edges etc)
13793
13794 for (var i = 0, l = self.length; i < l; i++) {
13795 var ele = self[i];
13796 add(ele);
13797 }
13798 function removeEdgeRef(node, edge) {
13799 var connectedEdges = node._private.edges;
13800 removeFromArray(connectedEdges, edge);
13801
13802 // removing an edges invalidates the traversal cache for its nodes
13803 node.clearTraversalCache();
13804 }
13805 function removeParallelRef(pllEdge) {
13806 // removing an edge invalidates the traversal caches for the parallel edges
13807 pllEdge.clearTraversalCache();
13808 }
13809 var alteredParents = [];
13810 alteredParents.ids = {};
13811 function removeChildRef(parent, ele) {
13812 ele = ele[0];
13813 parent = parent[0];
13814 var children = parent._private.children;
13815 var pid = parent.id();
13816 removeFromArray(children, ele); // remove parent => child ref
13817
13818 ele._private.parent = null; // remove child => parent ref
13819
13820 if (!alteredParents.ids[pid]) {
13821 alteredParents.ids[pid] = true;
13822 alteredParents.push(parent);
13823 }
13824 }
13825 self.dirtyCompoundBoundsCache();
13826 if (removeFromPool) {
13827 cy.removeFromPool(elesToRemove); // remove from core pool
13828 }
13829
13830 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13831 var _ele4 = elesToRemove[_i6];
13832 if (_ele4.isEdge()) {
13833 // remove references to this edge in its connected nodes
13834 var src = _ele4.source()[0];
13835 var tgt = _ele4.target()[0];
13836 removeEdgeRef(src, _ele4);
13837 removeEdgeRef(tgt, _ele4);
13838 var pllEdges = _ele4.parallelEdges();
13839 for (var j = 0; j < pllEdges.length; j++) {
13840 var pllEdge = pllEdges[j];
13841 removeParallelRef(pllEdge);
13842 if (pllEdge.isBundledBezier()) {
13843 pllEdge.dirtyBoundingBoxCache();
13844 }
13845 }
13846 } else {
13847 // remove reference to parent
13848 var parent = _ele4.parent();
13849 if (parent.length !== 0) {
13850 removeChildRef(parent, _ele4);
13851 }
13852 }
13853 if (removeFromPool) {
13854 // mark as removed
13855 _ele4._private.removed = true;
13856 }
13857 }
13858
13859 // check to see if we have a compound graph or not
13860 var elesStillInside = cy._private.elements;
13861 cy._private.hasCompoundNodes = false;
13862 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13863 var _ele5 = elesStillInside[_i7];
13864 if (_ele5.isParent()) {
13865 cy._private.hasCompoundNodes = true;
13866 break;
13867 }
13868 }
13869 var removedElements = new Collection(this.cy(), elesToRemove);
13870 if (removedElements.size() > 0) {
13871 // must manually notify since trigger won't do this automatically once removed
13872
13873 if (notifyRenderer) {
13874 removedElements.emitAndNotify('remove');
13875 } else if (removeFromPool) {
13876 removedElements.emit('remove');
13877 }
13878 }
13879
13880 // the parents who were modified by the removal need their style updated
13881 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13882 var _ele6 = alteredParents[_i8];
13883 if (!removeFromPool || !_ele6.removed()) {
13884 _ele6.updateStyle();
13885 }
13886 }
13887 return removedElements;
13888};
13889elesfn$1.move = function (struct) {
13890 var cy = this._private.cy;
13891 var eles = this;
13892
13893 // just clean up refs, caches, etc. in the same way as when removing and then restoring
13894 // (our calls to remove/restore do not remove from the graph or make events)
13895 var notifyRenderer = false;
13896 var modifyPool = false;
13897 var toString = function toString(id) {
13898 return id == null ? id : '' + id;
13899 }; // id must be string
13900
13901 if (struct.source !== undefined || struct.target !== undefined) {
13902 var srcId = toString(struct.source);
13903 var tgtId = toString(struct.target);
13904 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13905 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13906 if (srcExists || tgtExists) {
13907 cy.batch(function () {
13908 // avoid duplicate style updates
13909 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13910 eles.emitAndNotify('moveout');
13911 for (var i = 0; i < eles.length; i++) {
13912 var ele = eles[i];
13913 var _data5 = ele._private.data;
13914 if (ele.isEdge()) {
13915 if (srcExists) {
13916 _data5.source = srcId;
13917 }
13918 if (tgtExists) {
13919 _data5.target = tgtId;
13920 }
13921 }
13922 }
13923 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13924 });
13925
13926 eles.emitAndNotify('move');
13927 }
13928 } else if (struct.parent !== undefined) {
13929 // move node to new parent
13930 var parentId = toString(struct.parent);
13931 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13932 if (parentExists) {
13933 var pidToAssign = parentId === null ? undefined : parentId;
13934 cy.batch(function () {
13935 // avoid duplicate style updates
13936 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13937 updated.emitAndNotify('moveout');
13938 for (var i = 0; i < eles.length; i++) {
13939 var ele = eles[i];
13940 var _data6 = ele._private.data;
13941 if (ele.isNode()) {
13942 _data6.parent = pidToAssign;
13943 }
13944 }
13945 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13946 });
13947
13948 eles.emitAndNotify('move');
13949 }
13950 }
13951 return this;
13952};
13953[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) {
13954 extend(elesfn$1, props);
13955});
13956
13957var corefn$9 = {
13958 add: function add(opts) {
13959 var elements;
13960 var cy = this;
13961
13962 // add the elements
13963 if (elementOrCollection(opts)) {
13964 var eles = opts;
13965 if (eles._private.cy === cy) {
13966 // same instance => just restore
13967 elements = eles.restore();
13968 } else {
13969 // otherwise, copy from json
13970 var jsons = [];
13971 for (var i = 0; i < eles.length; i++) {
13972 var ele = eles[i];
13973 jsons.push(ele.json());
13974 }
13975 elements = new Collection(cy, jsons);
13976 }
13977 }
13978
13979 // specify an array of options
13980 else if (array(opts)) {
13981 var _jsons = opts;
13982 elements = new Collection(cy, _jsons);
13983 }
13984
13985 // specify via opts.nodes and opts.edges
13986 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13987 var elesByGroup = opts;
13988 var _jsons2 = [];
13989 var grs = ['nodes', 'edges'];
13990 for (var _i = 0, il = grs.length; _i < il; _i++) {
13991 var group = grs[_i];
13992 var elesArray = elesByGroup[group];
13993 if (array(elesArray)) {
13994 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13995 var json = extend({
13996 group: group
13997 }, elesArray[j]);
13998 _jsons2.push(json);
13999 }
14000 }
14001 }
14002 elements = new Collection(cy, _jsons2);
14003 }
14004
14005 // specify options for one element
14006 else {
14007 var _json = opts;
14008 elements = new Element(cy, _json).collection();
14009 }
14010 return elements;
14011 },
14012 remove: function remove(collection) {
14013 if (elementOrCollection(collection)) ; else if (string(collection)) {
14014 var selector = collection;
14015 collection = this.$(selector);
14016 }
14017 return collection.remove();
14018 }
14019};
14020
14021/* global Float32Array */
14022
14023/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14024function generateCubicBezier(mX1, mY1, mX2, mY2) {
14025 var NEWTON_ITERATIONS = 4,
14026 NEWTON_MIN_SLOPE = 0.001,
14027 SUBDIVISION_PRECISION = 0.0000001,
14028 SUBDIVISION_MAX_ITERATIONS = 10,
14029 kSplineTableSize = 11,
14030 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
14031 float32ArraySupported = typeof Float32Array !== 'undefined';
14032
14033 /* Must contain four arguments. */
14034 if (arguments.length !== 4) {
14035 return false;
14036 }
14037
14038 /* Arguments must be numbers. */
14039 for (var i = 0; i < 4; ++i) {
14040 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
14041 return false;
14042 }
14043 }
14044
14045 /* X values must be in the [0, 1] range. */
14046 mX1 = Math.min(mX1, 1);
14047 mX2 = Math.min(mX2, 1);
14048 mX1 = Math.max(mX1, 0);
14049 mX2 = Math.max(mX2, 0);
14050 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
14051 function A(aA1, aA2) {
14052 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
14053 }
14054 function B(aA1, aA2) {
14055 return 3.0 * aA2 - 6.0 * aA1;
14056 }
14057 function C(aA1) {
14058 return 3.0 * aA1;
14059 }
14060 function calcBezier(aT, aA1, aA2) {
14061 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
14062 }
14063 function getSlope(aT, aA1, aA2) {
14064 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
14065 }
14066 function newtonRaphsonIterate(aX, aGuessT) {
14067 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
14068 var currentSlope = getSlope(aGuessT, mX1, mX2);
14069 if (currentSlope === 0.0) {
14070 return aGuessT;
14071 }
14072 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
14073 aGuessT -= currentX / currentSlope;
14074 }
14075 return aGuessT;
14076 }
14077 function calcSampleValues() {
14078 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
14079 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
14080 }
14081 }
14082 function binarySubdivide(aX, aA, aB) {
14083 var currentX,
14084 currentT,
14085 i = 0;
14086 do {
14087 currentT = aA + (aB - aA) / 2.0;
14088 currentX = calcBezier(currentT, mX1, mX2) - aX;
14089 if (currentX > 0.0) {
14090 aB = currentT;
14091 } else {
14092 aA = currentT;
14093 }
14094 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
14095 return currentT;
14096 }
14097 function getTForX(aX) {
14098 var intervalStart = 0.0,
14099 currentSample = 1,
14100 lastSample = kSplineTableSize - 1;
14101 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
14102 intervalStart += kSampleStepSize;
14103 }
14104 --currentSample;
14105 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
14106 guessForT = intervalStart + dist * kSampleStepSize,
14107 initialSlope = getSlope(guessForT, mX1, mX2);
14108 if (initialSlope >= NEWTON_MIN_SLOPE) {
14109 return newtonRaphsonIterate(aX, guessForT);
14110 } else if (initialSlope === 0.0) {
14111 return guessForT;
14112 } else {
14113 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
14114 }
14115 }
14116 var _precomputed = false;
14117 function precompute() {
14118 _precomputed = true;
14119 if (mX1 !== mY1 || mX2 !== mY2) {
14120 calcSampleValues();
14121 }
14122 }
14123 var f = function f(aX) {
14124 if (!_precomputed) {
14125 precompute();
14126 }
14127 if (mX1 === mY1 && mX2 === mY2) {
14128 return aX;
14129 }
14130 if (aX === 0) {
14131 return 0;
14132 }
14133 if (aX === 1) {
14134 return 1;
14135 }
14136 return calcBezier(getTForX(aX), mY1, mY2);
14137 };
14138 f.getControlPoints = function () {
14139 return [{
14140 x: mX1,
14141 y: mY1
14142 }, {
14143 x: mX2,
14144 y: mY2
14145 }];
14146 };
14147 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
14148 f.toString = function () {
14149 return str;
14150 };
14151 return f;
14152}
14153
14154/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14155/* 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
14156 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
14157var generateSpringRK4 = function () {
14158 function springAccelerationForState(state) {
14159 return -state.tension * state.x - state.friction * state.v;
14160 }
14161 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
14162 var state = {
14163 x: initialState.x + derivative.dx * dt,
14164 v: initialState.v + derivative.dv * dt,
14165 tension: initialState.tension,
14166 friction: initialState.friction
14167 };
14168 return {
14169 dx: state.v,
14170 dv: springAccelerationForState(state)
14171 };
14172 }
14173 function springIntegrateState(state, dt) {
14174 var a = {
14175 dx: state.v,
14176 dv: springAccelerationForState(state)
14177 },
14178 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
14179 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
14180 d = springEvaluateStateWithDerivative(state, dt, c),
14181 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
14182 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
14183 state.x = state.x + dxdt * dt;
14184 state.v = state.v + dvdt * dt;
14185 return state;
14186 }
14187 return function springRK4Factory(tension, friction, duration) {
14188 var initState = {
14189 x: -1,
14190 v: 0,
14191 tension: null,
14192 friction: null
14193 },
14194 path = [0],
14195 time_lapsed = 0,
14196 tolerance = 1 / 10000,
14197 DT = 16 / 1000,
14198 have_duration,
14199 dt,
14200 last_state;
14201 tension = parseFloat(tension) || 500;
14202 friction = parseFloat(friction) || 20;
14203 duration = duration || null;
14204 initState.tension = tension;
14205 initState.friction = friction;
14206 have_duration = duration !== null;
14207
14208 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
14209 if (have_duration) {
14210 /* Run the simulation without a duration. */
14211 time_lapsed = springRK4Factory(tension, friction);
14212 /* Compute the adjusted time delta. */
14213 dt = time_lapsed / duration * DT;
14214 } else {
14215 dt = DT;
14216 }
14217 for (;;) {
14218 /* Next/step function .*/
14219 last_state = springIntegrateState(last_state || initState, dt);
14220 /* Store the position. */
14221 path.push(1 + last_state.x);
14222 time_lapsed += 16;
14223 /* If the change threshold is reached, break. */
14224 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
14225 break;
14226 }
14227 }
14228
14229 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
14230 computed path and returns a snapshot of the position according to a given percentComplete. */
14231 return !have_duration ? time_lapsed : function (percentComplete) {
14232 return path[percentComplete * (path.length - 1) | 0];
14233 };
14234 };
14235}();
14236
14237var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
14238 var bezier = generateCubicBezier(t1, p1, t2, p2);
14239 return function (start, end, percent) {
14240 return start + (end - start) * bezier(percent);
14241 };
14242};
14243var easings = {
14244 'linear': function linear(start, end, percent) {
14245 return start + (end - start) * percent;
14246 },
14247 // default easings
14248 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
14249 'ease-in': cubicBezier(0.42, 0, 1, 1),
14250 'ease-out': cubicBezier(0, 0, 0.58, 1),
14251 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
14252 // sine
14253 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
14254 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
14255 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
14256 // quad
14257 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
14258 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
14259 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
14260 // cubic
14261 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
14262 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
14263 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
14264 // quart
14265 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
14266 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
14267 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
14268 // quint
14269 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
14270 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
14271 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
14272 // expo
14273 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
14274 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
14275 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
14276 // circ
14277 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
14278 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
14279 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
14280 // user param easings...
14281
14282 'spring': function spring(tension, friction, duration) {
14283 if (duration === 0) {
14284 // can't get a spring w/ duration 0
14285 return easings.linear; // duration 0 => jump to end so impl doesn't matter
14286 }
14287
14288 var spring = generateSpringRK4(tension, friction, duration);
14289 return function (start, end, percent) {
14290 return start + (end - start) * spring(percent);
14291 };
14292 },
14293 'cubic-bezier': cubicBezier
14294};
14295
14296function getEasedValue(type, start, end, percent, easingFn) {
14297 if (percent === 1) {
14298 return end;
14299 }
14300 if (start === end) {
14301 return end;
14302 }
14303 var val = easingFn(start, end, percent);
14304 if (type == null) {
14305 return val;
14306 }
14307 if (type.roundValue || type.color) {
14308 val = Math.round(val);
14309 }
14310 if (type.min !== undefined) {
14311 val = Math.max(val, type.min);
14312 }
14313 if (type.max !== undefined) {
14314 val = Math.min(val, type.max);
14315 }
14316 return val;
14317}
14318function getValue(prop, spec) {
14319 if (prop.pfValue != null || prop.value != null) {
14320 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
14321 return prop.pfValue;
14322 } else {
14323 return prop.value;
14324 }
14325 } else {
14326 return prop;
14327 }
14328}
14329function ease(startProp, endProp, percent, easingFn, propSpec) {
14330 var type = propSpec != null ? propSpec.type : null;
14331 if (percent < 0) {
14332 percent = 0;
14333 } else if (percent > 1) {
14334 percent = 1;
14335 }
14336 var start = getValue(startProp, propSpec);
14337 var end = getValue(endProp, propSpec);
14338 if (number$1(start) && number$1(end)) {
14339 return getEasedValue(type, start, end, percent, easingFn);
14340 } else if (array(start) && array(end)) {
14341 var easedArr = [];
14342 for (var i = 0; i < end.length; i++) {
14343 var si = start[i];
14344 var ei = end[i];
14345 if (si != null && ei != null) {
14346 var val = getEasedValue(type, si, ei, percent, easingFn);
14347 easedArr.push(val);
14348 } else {
14349 easedArr.push(ei);
14350 }
14351 }
14352 return easedArr;
14353 }
14354 return undefined;
14355}
14356
14357function step$1(self, ani, now, isCore) {
14358 var isEles = !isCore;
14359 var _p = self._private;
14360 var ani_p = ani._private;
14361 var pEasing = ani_p.easing;
14362 var startTime = ani_p.startTime;
14363 var cy = isCore ? self : self.cy();
14364 var style = cy.style();
14365 if (!ani_p.easingImpl) {
14366 if (pEasing == null) {
14367 // use default
14368 ani_p.easingImpl = easings['linear'];
14369 } else {
14370 // then define w/ name
14371 var easingVals;
14372 if (string(pEasing)) {
14373 var easingProp = style.parse('transition-timing-function', pEasing);
14374 easingVals = easingProp.value;
14375 } else {
14376 // then assume preparsed array
14377 easingVals = pEasing;
14378 }
14379 var name, args;
14380 if (string(easingVals)) {
14381 name = easingVals;
14382 args = [];
14383 } else {
14384 name = easingVals[1];
14385 args = easingVals.slice(2).map(function (n) {
14386 return +n;
14387 });
14388 }
14389 if (args.length > 0) {
14390 // create with args
14391 if (name === 'spring') {
14392 args.push(ani_p.duration); // need duration to generate spring
14393 }
14394
14395 ani_p.easingImpl = easings[name].apply(null, args);
14396 } else {
14397 // static impl by name
14398 ani_p.easingImpl = easings[name];
14399 }
14400 }
14401 }
14402 var easing = ani_p.easingImpl;
14403 var percent;
14404 if (ani_p.duration === 0) {
14405 percent = 1;
14406 } else {
14407 percent = (now - startTime) / ani_p.duration;
14408 }
14409 if (ani_p.applying) {
14410 percent = ani_p.progress;
14411 }
14412 if (percent < 0) {
14413 percent = 0;
14414 } else if (percent > 1) {
14415 percent = 1;
14416 }
14417 if (ani_p.delay == null) {
14418 // then update
14419
14420 var startPos = ani_p.startPosition;
14421 var endPos = ani_p.position;
14422 if (endPos && isEles && !self.locked()) {
14423 var newPos = {};
14424 if (valid(startPos.x, endPos.x)) {
14425 newPos.x = ease(startPos.x, endPos.x, percent, easing);
14426 }
14427 if (valid(startPos.y, endPos.y)) {
14428 newPos.y = ease(startPos.y, endPos.y, percent, easing);
14429 }
14430 self.position(newPos);
14431 }
14432 var startPan = ani_p.startPan;
14433 var endPan = ani_p.pan;
14434 var pan = _p.pan;
14435 var animatingPan = endPan != null && isCore;
14436 if (animatingPan) {
14437 if (valid(startPan.x, endPan.x)) {
14438 pan.x = ease(startPan.x, endPan.x, percent, easing);
14439 }
14440 if (valid(startPan.y, endPan.y)) {
14441 pan.y = ease(startPan.y, endPan.y, percent, easing);
14442 }
14443 self.emit('pan');
14444 }
14445 var startZoom = ani_p.startZoom;
14446 var endZoom = ani_p.zoom;
14447 var animatingZoom = endZoom != null && isCore;
14448 if (animatingZoom) {
14449 if (valid(startZoom, endZoom)) {
14450 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
14451 }
14452 self.emit('zoom');
14453 }
14454 if (animatingPan || animatingZoom) {
14455 self.emit('viewport');
14456 }
14457 var props = ani_p.style;
14458 if (props && props.length > 0 && isEles) {
14459 for (var i = 0; i < props.length; i++) {
14460 var prop = props[i];
14461 var _name = prop.name;
14462 var end = prop;
14463 var start = ani_p.startStyle[_name];
14464 var propSpec = style.properties[start.name];
14465 var easedVal = ease(start, end, percent, easing, propSpec);
14466 style.overrideBypass(self, _name, easedVal);
14467 } // for props
14468
14469 self.emit('style');
14470 } // if
14471 }
14472
14473 ani_p.progress = percent;
14474 return percent;
14475}
14476function valid(start, end) {
14477 if (start == null || end == null) {
14478 return false;
14479 }
14480 if (number$1(start) && number$1(end)) {
14481 return true;
14482 } else if (start && end) {
14483 return true;
14484 }
14485 return false;
14486}
14487
14488function startAnimation(self, ani, now, isCore) {
14489 var ani_p = ani._private;
14490 ani_p.started = true;
14491 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14492}
14493
14494function stepAll(now, cy) {
14495 var eles = cy._private.aniEles;
14496 var doneEles = [];
14497 function stepOne(ele, isCore) {
14498 var _p = ele._private;
14499 var current = _p.animation.current;
14500 var queue = _p.animation.queue;
14501 var ranAnis = false;
14502
14503 // if nothing currently animating, get something from the queue
14504 if (current.length === 0) {
14505 var next = queue.shift();
14506 if (next) {
14507 current.push(next);
14508 }
14509 }
14510 var callbacks = function callbacks(_callbacks) {
14511 for (var j = _callbacks.length - 1; j >= 0; j--) {
14512 var cb = _callbacks[j];
14513 cb();
14514 }
14515 _callbacks.splice(0, _callbacks.length);
14516 };
14517
14518 // step and remove if done
14519 for (var i = current.length - 1; i >= 0; i--) {
14520 var ani = current[i];
14521 var ani_p = ani._private;
14522 if (ani_p.stopped) {
14523 current.splice(i, 1);
14524 ani_p.hooked = false;
14525 ani_p.playing = false;
14526 ani_p.started = false;
14527 callbacks(ani_p.frames);
14528 continue;
14529 }
14530 if (!ani_p.playing && !ani_p.applying) {
14531 continue;
14532 }
14533
14534 // an apply() while playing shouldn't do anything
14535 if (ani_p.playing && ani_p.applying) {
14536 ani_p.applying = false;
14537 }
14538 if (!ani_p.started) {
14539 startAnimation(ele, ani, now);
14540 }
14541 step$1(ele, ani, now, isCore);
14542 if (ani_p.applying) {
14543 ani_p.applying = false;
14544 }
14545 callbacks(ani_p.frames);
14546 if (ani_p.step != null) {
14547 ani_p.step(now);
14548 }
14549 if (ani.completed()) {
14550 current.splice(i, 1);
14551 ani_p.hooked = false;
14552 ani_p.playing = false;
14553 ani_p.started = false;
14554 callbacks(ani_p.completes);
14555 }
14556 ranAnis = true;
14557 }
14558 if (!isCore && current.length === 0 && queue.length === 0) {
14559 doneEles.push(ele);
14560 }
14561 return ranAnis;
14562 } // stepElement
14563
14564 // handle all eles
14565 var ranEleAni = false;
14566 for (var e = 0; e < eles.length; e++) {
14567 var ele = eles[e];
14568 var handledThisEle = stepOne(ele);
14569 ranEleAni = ranEleAni || handledThisEle;
14570 } // each element
14571
14572 var ranCoreAni = stepOne(cy, true);
14573
14574 // notify renderer
14575 if (ranEleAni || ranCoreAni) {
14576 if (eles.length > 0) {
14577 cy.notify('draw', eles);
14578 } else {
14579 cy.notify('draw');
14580 }
14581 }
14582
14583 // remove elements from list of currently animating if its queues are empty
14584 eles.unmerge(doneEles);
14585 cy.emit('step');
14586} // stepAll
14587
14588var corefn$8 = {
14589 // pull in animation functions
14590 animate: define.animate(),
14591 animation: define.animation(),
14592 animated: define.animated(),
14593 clearQueue: define.clearQueue(),
14594 delay: define.delay(),
14595 delayAnimation: define.delayAnimation(),
14596 stop: define.stop(),
14597 addToAnimationPool: function addToAnimationPool(eles) {
14598 var cy = this;
14599 if (!cy.styleEnabled()) {
14600 return;
14601 } // save cycles when no style used
14602
14603 cy._private.aniEles.merge(eles);
14604 },
14605 stopAnimationLoop: function stopAnimationLoop() {
14606 this._private.animationsRunning = false;
14607 },
14608 startAnimationLoop: function startAnimationLoop() {
14609 var cy = this;
14610 cy._private.animationsRunning = true;
14611 if (!cy.styleEnabled()) {
14612 return;
14613 } // save cycles when no style used
14614
14615 // NB the animation loop will exec in headless environments if style enabled
14616 // and explicit cy.destroy() is necessary to stop the loop
14617
14618 function headlessStep() {
14619 if (!cy._private.animationsRunning) {
14620 return;
14621 }
14622 requestAnimationFrame(function animationStep(now) {
14623 stepAll(now, cy);
14624 headlessStep();
14625 });
14626 }
14627 var renderer = cy.renderer();
14628 if (renderer && renderer.beforeRender) {
14629 // let the renderer schedule animations
14630 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14631 stepAll(now, cy);
14632 }, renderer.beforeRenderPriorities.animations);
14633 } else {
14634 // manage the animation loop ourselves
14635 headlessStep(); // first call
14636 }
14637 }
14638};
14639
14640var emitterOptions = {
14641 qualifierCompare: function qualifierCompare(selector1, selector2) {
14642 if (selector1 == null || selector2 == null) {
14643 return selector1 == null && selector2 == null;
14644 } else {
14645 return selector1.sameText(selector2);
14646 }
14647 },
14648 eventMatches: function eventMatches(cy, listener, eventObj) {
14649 var selector = listener.qualifier;
14650 if (selector != null) {
14651 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14652 }
14653 return true;
14654 },
14655 addEventFields: function addEventFields(cy, evt) {
14656 evt.cy = cy;
14657 evt.target = cy;
14658 },
14659 callbackContext: function callbackContext(cy, listener, eventObj) {
14660 return listener.qualifier != null ? eventObj.target : cy;
14661 }
14662};
14663var argSelector = function argSelector(arg) {
14664 if (string(arg)) {
14665 return new Selector(arg);
14666 } else {
14667 return arg;
14668 }
14669};
14670var elesfn = {
14671 createEmitter: function createEmitter() {
14672 var _p = this._private;
14673 if (!_p.emitter) {
14674 _p.emitter = new Emitter(emitterOptions, this);
14675 }
14676 return this;
14677 },
14678 emitter: function emitter() {
14679 return this._private.emitter;
14680 },
14681 on: function on(events, selector, callback) {
14682 this.emitter().on(events, argSelector(selector), callback);
14683 return this;
14684 },
14685 removeListener: function removeListener(events, selector, callback) {
14686 this.emitter().removeListener(events, argSelector(selector), callback);
14687 return this;
14688 },
14689 removeAllListeners: function removeAllListeners() {
14690 this.emitter().removeAllListeners();
14691 return this;
14692 },
14693 one: function one(events, selector, callback) {
14694 this.emitter().one(events, argSelector(selector), callback);
14695 return this;
14696 },
14697 once: function once(events, selector, callback) {
14698 this.emitter().one(events, argSelector(selector), callback);
14699 return this;
14700 },
14701 emit: function emit(events, extraParams) {
14702 this.emitter().emit(events, extraParams);
14703 return this;
14704 },
14705 emitAndNotify: function emitAndNotify(event, eles) {
14706 this.emit(event);
14707 this.notify(event, eles);
14708 return this;
14709 }
14710};
14711define.eventAliasesOn(elesfn);
14712
14713var corefn$7 = {
14714 png: function png(options) {
14715 var renderer = this._private.renderer;
14716 options = options || {};
14717 return renderer.png(options);
14718 },
14719 jpg: function jpg(options) {
14720 var renderer = this._private.renderer;
14721 options = options || {};
14722 options.bg = options.bg || '#fff';
14723 return renderer.jpg(options);
14724 }
14725};
14726corefn$7.jpeg = corefn$7.jpg;
14727
14728var corefn$6 = {
14729 layout: function layout(options) {
14730 var cy = this;
14731 if (options == null) {
14732 error('Layout options must be specified to make a layout');
14733 return;
14734 }
14735 if (options.name == null) {
14736 error('A `name` must be specified to make a layout');
14737 return;
14738 }
14739 var name = options.name;
14740 var Layout = cy.extension('layout', name);
14741 if (Layout == null) {
14742 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14743 return;
14744 }
14745 var eles;
14746 if (string(options.eles)) {
14747 eles = cy.$(options.eles);
14748 } else {
14749 eles = options.eles != null ? options.eles : cy.$();
14750 }
14751 var layout = new Layout(extend({}, options, {
14752 cy: cy,
14753 eles: eles
14754 }));
14755 return layout;
14756 }
14757};
14758corefn$6.createLayout = corefn$6.makeLayout = corefn$6.layout;
14759
14760var corefn$5 = {
14761 notify: function notify(eventName, eventEles) {
14762 var _p = this._private;
14763 if (this.batching()) {
14764 _p.batchNotifications = _p.batchNotifications || {};
14765 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14766 if (eventEles != null) {
14767 eles.merge(eventEles);
14768 }
14769 return; // notifications are disabled during batching
14770 }
14771
14772 if (!_p.notificationsEnabled) {
14773 return;
14774 } // exit on disabled
14775
14776 var renderer = this.renderer();
14777
14778 // exit if destroy() called on core or renderer in between frames #1499 #1528
14779 if (this.destroyed() || !renderer) {
14780 return;
14781 }
14782 renderer.notify(eventName, eventEles);
14783 },
14784 notifications: function notifications(bool) {
14785 var p = this._private;
14786 if (bool === undefined) {
14787 return p.notificationsEnabled;
14788 } else {
14789 p.notificationsEnabled = bool ? true : false;
14790 }
14791 return this;
14792 },
14793 noNotifications: function noNotifications(callback) {
14794 this.notifications(false);
14795 callback();
14796 this.notifications(true);
14797 },
14798 batching: function batching() {
14799 return this._private.batchCount > 0;
14800 },
14801 startBatch: function startBatch() {
14802 var _p = this._private;
14803 if (_p.batchCount == null) {
14804 _p.batchCount = 0;
14805 }
14806 if (_p.batchCount === 0) {
14807 _p.batchStyleEles = this.collection();
14808 _p.batchNotifications = {};
14809 }
14810 _p.batchCount++;
14811 return this;
14812 },
14813 endBatch: function endBatch() {
14814 var _p = this._private;
14815 if (_p.batchCount === 0) {
14816 return this;
14817 }
14818 _p.batchCount--;
14819 if (_p.batchCount === 0) {
14820 // update style for dirty eles
14821 _p.batchStyleEles.updateStyle();
14822 var renderer = this.renderer();
14823
14824 // notify the renderer of queued eles and event types
14825 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14826 var eles = _p.batchNotifications[eventName];
14827 if (eles.empty()) {
14828 renderer.notify(eventName);
14829 } else {
14830 renderer.notify(eventName, eles);
14831 }
14832 });
14833 }
14834 return this;
14835 },
14836 batch: function batch(callback) {
14837 this.startBatch();
14838 callback();
14839 this.endBatch();
14840 return this;
14841 },
14842 // for backwards compatibility
14843 batchData: function batchData(map) {
14844 var cy = this;
14845 return this.batch(function () {
14846 var ids = Object.keys(map);
14847 for (var i = 0; i < ids.length; i++) {
14848 var id = ids[i];
14849 var data = map[id];
14850 var ele = cy.getElementById(id);
14851 ele.data(data);
14852 }
14853 });
14854 }
14855};
14856
14857var rendererDefaults = defaults$g({
14858 hideEdgesOnViewport: false,
14859 textureOnViewport: false,
14860 motionBlur: false,
14861 motionBlurOpacity: 0.05,
14862 pixelRatio: undefined,
14863 desktopTapThreshold: 4,
14864 touchTapThreshold: 8,
14865 wheelSensitivity: 1,
14866 debug: false,
14867 showFps: false
14868});
14869var corefn$4 = {
14870 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14871 var r = this._private.renderer;
14872 r.renderTo(context, zoom, pan, pxRatio);
14873 return this;
14874 },
14875 renderer: function renderer() {
14876 return this._private.renderer;
14877 },
14878 forceRender: function forceRender() {
14879 this.notify('draw');
14880 return this;
14881 },
14882 resize: function resize() {
14883 this.invalidateSize();
14884 this.emitAndNotify('resize');
14885 return this;
14886 },
14887 initRenderer: function initRenderer(options) {
14888 var cy = this;
14889 var RendererProto = cy.extension('renderer', options.name);
14890 if (RendererProto == null) {
14891 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14892 return;
14893 }
14894 if (options.wheelSensitivity !== undefined) {
14895 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.");
14896 }
14897 var rOpts = rendererDefaults(options);
14898 rOpts.cy = cy;
14899 cy._private.renderer = new RendererProto(rOpts);
14900 this.notify('init');
14901 },
14902 destroyRenderer: function destroyRenderer() {
14903 var cy = this;
14904 cy.notify('destroy'); // destroy the renderer
14905
14906 var domEle = cy.container();
14907 if (domEle) {
14908 domEle._cyreg = null;
14909 while (domEle.childNodes.length > 0) {
14910 domEle.removeChild(domEle.childNodes[0]);
14911 }
14912 }
14913 cy._private.renderer = null; // to be extra safe, remove the ref
14914 cy.mutableElements().forEach(function (ele) {
14915 var _p = ele._private;
14916 _p.rscratch = {};
14917 _p.rstyle = {};
14918 _p.animation.current = [];
14919 _p.animation.queue = [];
14920 });
14921 },
14922 onRender: function onRender(fn) {
14923 return this.on('render', fn);
14924 },
14925 offRender: function offRender(fn) {
14926 return this.off('render', fn);
14927 }
14928};
14929corefn$4.invalidateDimensions = corefn$4.resize;
14930
14931var corefn$3 = {
14932 // get a collection
14933 // - empty collection on no args
14934 // - collection of elements in the graph on selector arg
14935 // - guarantee a returned collection when elements or collection specified
14936 collection: function collection(eles, opts) {
14937 if (string(eles)) {
14938 return this.$(eles);
14939 } else if (elementOrCollection(eles)) {
14940 return eles.collection();
14941 } else if (array(eles)) {
14942 if (!opts) {
14943 opts = {};
14944 }
14945 return new Collection(this, eles, opts.unique, opts.removed);
14946 }
14947 return new Collection(this);
14948 },
14949 nodes: function nodes(selector) {
14950 var nodes = this.$(function (ele) {
14951 return ele.isNode();
14952 });
14953 if (selector) {
14954 return nodes.filter(selector);
14955 }
14956 return nodes;
14957 },
14958 edges: function edges(selector) {
14959 var edges = this.$(function (ele) {
14960 return ele.isEdge();
14961 });
14962 if (selector) {
14963 return edges.filter(selector);
14964 }
14965 return edges;
14966 },
14967 // search the graph like jQuery
14968 $: function $(selector) {
14969 var eles = this._private.elements;
14970 if (selector) {
14971 return eles.filter(selector);
14972 } else {
14973 return eles.spawnSelf();
14974 }
14975 },
14976 mutableElements: function mutableElements() {
14977 return this._private.elements;
14978 }
14979};
14980
14981// aliases
14982corefn$3.elements = corefn$3.filter = corefn$3.$;
14983
14984var styfn$8 = {};
14985
14986// keys for style blocks, e.g. ttfftt
14987var TRUE = 't';
14988var FALSE = 'f';
14989
14990// (potentially expensive calculation)
14991// apply the style to the element based on
14992// - its bypass
14993// - what selectors match it
14994styfn$8.apply = function (eles) {
14995 var self = this;
14996 var _p = self._private;
14997 var cy = _p.cy;
14998 var updatedEles = cy.collection();
14999 for (var ie = 0; ie < eles.length; ie++) {
15000 var ele = eles[ie];
15001 var cxtMeta = self.getContextMeta(ele);
15002 if (cxtMeta.empty) {
15003 continue;
15004 }
15005 var cxtStyle = self.getContextStyle(cxtMeta);
15006 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
15007 if (ele._private.appliedInitStyle) {
15008 self.updateTransitions(ele, app.diffProps);
15009 } else {
15010 ele._private.appliedInitStyle = true;
15011 }
15012 var hintsDiff = self.updateStyleHints(ele);
15013 if (hintsDiff) {
15014 updatedEles.push(ele);
15015 }
15016 } // for elements
15017
15018 return updatedEles;
15019};
15020styfn$8.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
15021 var self = this;
15022 var cache = self._private.propDiffs = self._private.propDiffs || {};
15023 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
15024 var cachedVal = cache[dualCxtKey];
15025 if (cachedVal) {
15026 return cachedVal;
15027 }
15028 var diffProps = [];
15029 var addedProp = {};
15030 for (var i = 0; i < self.length; i++) {
15031 var cxt = self[i];
15032 var oldHasCxt = oldCxtKey[i] === TRUE;
15033 var newHasCxt = newCxtKey[i] === TRUE;
15034 var cxtHasDiffed = oldHasCxt !== newHasCxt;
15035 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
15036 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
15037 var props = void 0;
15038 if (cxtHasDiffed && cxtHasMappedProps) {
15039 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
15040 } else if (cxtHasDiffed) {
15041 props = cxt.properties; // need to check them all
15042 } else if (cxtHasMappedProps) {
15043 props = cxt.mappedProperties; // only need to check mapped
15044 }
15045
15046 for (var j = 0; j < props.length; j++) {
15047 var prop = props[j];
15048 var name = prop.name;
15049
15050 // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
15051 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
15052 // is cached)
15053 var laterCxtOverrides = false;
15054 for (var k = i + 1; k < self.length; k++) {
15055 var laterCxt = self[k];
15056 var hasLaterCxt = newCxtKey[k] === TRUE;
15057 if (!hasLaterCxt) {
15058 continue;
15059 } // can't override unless the context is active
15060
15061 laterCxtOverrides = laterCxt.properties[prop.name] != null;
15062 if (laterCxtOverrides) {
15063 break;
15064 } // exit early as long as one later context overrides
15065 }
15066
15067 if (!addedProp[name] && !laterCxtOverrides) {
15068 addedProp[name] = true;
15069 diffProps.push(name);
15070 }
15071 } // for props
15072 } // if
15073 } // for contexts
15074
15075 cache[dualCxtKey] = diffProps;
15076 return diffProps;
15077};
15078styfn$8.getContextMeta = function (ele) {
15079 var self = this;
15080 var cxtKey = '';
15081 var diffProps;
15082 var prevKey = ele._private.styleCxtKey || '';
15083
15084 // get the cxt key
15085 for (var i = 0; i < self.length; i++) {
15086 var context = self[i];
15087 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
15088
15089 if (contextSelectorMatches) {
15090 cxtKey += TRUE;
15091 } else {
15092 cxtKey += FALSE;
15093 }
15094 } // for context
15095
15096 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
15097 ele._private.styleCxtKey = cxtKey;
15098 return {
15099 key: cxtKey,
15100 diffPropNames: diffProps,
15101 empty: diffProps.length === 0
15102 };
15103};
15104
15105// gets a computed ele style object based on matched contexts
15106styfn$8.getContextStyle = function (cxtMeta) {
15107 var cxtKey = cxtMeta.key;
15108 var self = this;
15109 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {};
15110
15111 // if already computed style, returned cached copy
15112 if (cxtStyles[cxtKey]) {
15113 return cxtStyles[cxtKey];
15114 }
15115 var style = {
15116 _private: {
15117 key: cxtKey
15118 }
15119 };
15120 for (var i = 0; i < self.length; i++) {
15121 var cxt = self[i];
15122 var hasCxt = cxtKey[i] === TRUE;
15123 if (!hasCxt) {
15124 continue;
15125 }
15126 for (var j = 0; j < cxt.properties.length; j++) {
15127 var prop = cxt.properties[j];
15128 style[prop.name] = prop;
15129 }
15130 }
15131 cxtStyles[cxtKey] = style;
15132 return style;
15133};
15134styfn$8.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
15135 var self = this;
15136 var diffProps = cxtMeta.diffPropNames;
15137 var retDiffProps = {};
15138 var types = self.types;
15139 for (var i = 0; i < diffProps.length; i++) {
15140 var diffPropName = diffProps[i];
15141 var cxtProp = cxtStyle[diffPropName];
15142 var eleProp = ele.pstyle(diffPropName);
15143 if (!cxtProp) {
15144 // no context prop means delete
15145 if (!eleProp) {
15146 continue; // no existing prop means nothing needs to be removed
15147 // nb affects initial application on mapped values like control-point-distances
15148 } else if (eleProp.bypass) {
15149 cxtProp = {
15150 name: diffPropName,
15151 deleteBypassed: true
15152 };
15153 } else {
15154 cxtProp = {
15155 name: diffPropName,
15156 "delete": true
15157 };
15158 }
15159 }
15160
15161 // save cycles when the context prop doesn't need to be applied
15162 if (eleProp === cxtProp) {
15163 continue;
15164 }
15165
15166 // save cycles when a mapped context prop doesn't need to be applied
15167 if (cxtProp.mapped === types.fn // context prop is function mapper
15168 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
15169 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
15170 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
15171 ) {
15172 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
15173 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
15174 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
15175
15176 if (fnValue === mapping.prevFnValue) {
15177 continue;
15178 }
15179 }
15180 var retDiffProp = retDiffProps[diffPropName] = {
15181 prev: eleProp
15182 };
15183 self.applyParsedProperty(ele, cxtProp);
15184 retDiffProp.next = ele.pstyle(diffPropName);
15185 if (retDiffProp.next && retDiffProp.next.bypass) {
15186 retDiffProp.next = retDiffProp.next.bypassed;
15187 }
15188 }
15189 return {
15190 diffProps: retDiffProps
15191 };
15192};
15193styfn$8.updateStyleHints = function (ele) {
15194 var _p = ele._private;
15195 var self = this;
15196 var propNames = self.propertyGroupNames;
15197 var propGrKeys = self.propertyGroupKeys;
15198 var propHash = function propHash(ele, propNames, seedKey) {
15199 return self.getPropertiesHash(ele, propNames, seedKey);
15200 };
15201 var oldStyleKey = _p.styleKey;
15202 if (ele.removed()) {
15203 return false;
15204 }
15205 var isNode = _p.group === 'nodes';
15206
15207 // get the style key hashes per prop group
15208 // but lazily -- only use non-default prop values to reduce the number of hashes
15209 //
15210
15211 var overriddenStyles = ele._private.style;
15212 propNames = Object.keys(overriddenStyles);
15213 for (var i = 0; i < propGrKeys.length; i++) {
15214 var grKey = propGrKeys[i];
15215 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15216 }
15217 var updateGrKey1 = function updateGrKey1(val, grKey) {
15218 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
15219 };
15220 var updateGrKey2 = function updateGrKey2(val, grKey) {
15221 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
15222 };
15223 var updateGrKey = function updateGrKey(val, grKey) {
15224 updateGrKey1(val, grKey);
15225 updateGrKey2(val, grKey);
15226 };
15227 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
15228 for (var j = 0; j < strVal.length; j++) {
15229 var ch = strVal.charCodeAt(j);
15230 updateGrKey1(ch, grKey);
15231 updateGrKey2(ch, grKey);
15232 }
15233 };
15234
15235 // - hashing works on 32 bit ints b/c we use bitwise ops
15236 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
15237 // - raise up small numbers so more significant digits are seen by hashing
15238 // - make small numbers larger than a normal value to avoid collisions
15239 // - works in practice and it's relatively cheap
15240 var N = 2000000000;
15241 var cleanNum = function cleanNum(val) {
15242 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
15243 };
15244 for (var _i = 0; _i < propNames.length; _i++) {
15245 var name = propNames[_i];
15246 var parsedProp = overriddenStyles[name];
15247 if (parsedProp == null) {
15248 continue;
15249 }
15250 var propInfo = this.properties[name];
15251 var type = propInfo.type;
15252 var _grKey = propInfo.groupKey;
15253 var normalizedNumberVal = void 0;
15254 if (propInfo.hashOverride != null) {
15255 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
15256 } else if (parsedProp.pfValue != null) {
15257 normalizedNumberVal = parsedProp.pfValue;
15258 }
15259
15260 // might not be a number if it allows enums
15261 var numberVal = propInfo.enums == null ? parsedProp.value : null;
15262 var haveNormNum = normalizedNumberVal != null;
15263 var haveUnitedNum = numberVal != null;
15264 var haveNum = haveNormNum || haveUnitedNum;
15265 var units = parsedProp.units;
15266
15267 // numbers are cheaper to hash than strings
15268 // 1 hash op vs n hash ops (for length n string)
15269 if (type.number && haveNum && !type.multiple) {
15270 var v = haveNormNum ? normalizedNumberVal : numberVal;
15271 updateGrKey(cleanNum(v), _grKey);
15272 if (!haveNormNum && units != null) {
15273 updateGrKeyWStr(units, _grKey);
15274 }
15275 } else {
15276 updateGrKeyWStr(parsedProp.strValue, _grKey);
15277 }
15278 }
15279
15280 // overall style key
15281 //
15282
15283 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15284 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
15285 var _grKey2 = propGrKeys[_i2];
15286 var grHash = _p.styleKeys[_grKey2];
15287 hash[0] = hashInt(grHash[0], hash[0]);
15288 hash[1] = hashIntAlt(grHash[1], hash[1]);
15289 }
15290 _p.styleKey = combineHashes(hash[0], hash[1]);
15291
15292 // label dims
15293 //
15294
15295 var sk = _p.styleKeys;
15296 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
15297 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
15298 _p.labelKey = combineHashesArray(labelKeys);
15299 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
15300 if (!isNode) {
15301 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
15302 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
15303 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
15304 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
15305 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
15306 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
15307 }
15308
15309 // node
15310 //
15311
15312 if (isNode) {
15313 var _p$styleKeys = _p.styleKeys,
15314 nodeBody = _p$styleKeys.nodeBody,
15315 nodeBorder = _p$styleKeys.nodeBorder,
15316 nodeOutline = _p$styleKeys.nodeOutline,
15317 backgroundImage = _p$styleKeys.backgroundImage,
15318 compound = _p$styleKeys.compound,
15319 pie = _p$styleKeys.pie;
15320 var nodeKeys = [nodeBody, nodeBorder, nodeOutline, backgroundImage, compound, pie].filter(function (k) {
15321 return k != null;
15322 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
15323 _p.nodeKey = combineHashesArray(nodeKeys);
15324 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
15325 }
15326 return oldStyleKey !== _p.styleKey;
15327};
15328styfn$8.clearStyleHints = function (ele) {
15329 var _p = ele._private;
15330 _p.styleCxtKey = '';
15331 _p.styleKeys = {};
15332 _p.styleKey = null;
15333 _p.labelKey = null;
15334 _p.labelStyleKey = null;
15335 _p.sourceLabelKey = null;
15336 _p.sourceLabelStyleKey = null;
15337 _p.targetLabelKey = null;
15338 _p.targetLabelStyleKey = null;
15339 _p.nodeKey = null;
15340 _p.hasPie = null;
15341};
15342
15343// apply a property to the style (for internal use)
15344// returns whether application was successful
15345//
15346// now, this function flattens the property, and here's how:
15347//
15348// for parsedProp:{ bypass: true, deleteBypass: true }
15349// no property is generated, instead the bypass property in the
15350// element's style is replaced by what's pointed to by the `bypassed`
15351// field in the bypass property (i.e. restoring the property the
15352// bypass was overriding)
15353//
15354// for parsedProp:{ mapped: truthy }
15355// the generated flattenedProp:{ mapping: prop }
15356//
15357// for parsedProp:{ bypass: true }
15358// the generated flattenedProp:{ bypassed: parsedProp }
15359styfn$8.applyParsedProperty = function (ele, parsedProp) {
15360 var self = this;
15361 var prop = parsedProp;
15362 var style = ele._private.style;
15363 var flatProp;
15364 var types = self.types;
15365 var type = self.properties[prop.name].type;
15366 var propIsBypass = prop.bypass;
15367 var origProp = style[prop.name];
15368 var origPropIsBypass = origProp && origProp.bypass;
15369 var _p = ele._private;
15370 var flatPropMapping = 'mapping';
15371 var getVal = function getVal(p) {
15372 if (p == null) {
15373 return null;
15374 } else if (p.pfValue != null) {
15375 return p.pfValue;
15376 } else {
15377 return p.value;
15378 }
15379 };
15380 var checkTriggers = function checkTriggers() {
15381 var fromVal = getVal(origProp);
15382 var toVal = getVal(prop);
15383 self.checkTriggers(ele, prop.name, fromVal, toVal);
15384 };
15385
15386 // edge sanity checks to prevent the client from making serious mistakes
15387 if (parsedProp.name === 'curve-style' && ele.isEdge() && (
15388 // loops must be bundled beziers
15389 parsedProp.value !== 'bezier' && ele.isLoop() ||
15390 // edges connected to compound nodes can not be haystacks
15391 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15392 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15393 }
15394 if (prop["delete"]) {
15395 // delete the property and use the default value on falsey value
15396 style[prop.name] = undefined;
15397 checkTriggers();
15398 return true;
15399 }
15400 if (prop.deleteBypassed) {
15401 // delete the property that the
15402 if (!origProp) {
15403 checkTriggers();
15404 return true; // can't delete if no prop
15405 } else if (origProp.bypass) {
15406 // delete bypassed
15407 origProp.bypassed = undefined;
15408 checkTriggers();
15409 return true;
15410 } else {
15411 return false; // we're unsuccessful deleting the bypassed
15412 }
15413 }
15414
15415 // check if we need to delete the current bypass
15416 if (prop.deleteBypass) {
15417 // then this property is just here to indicate we need to delete
15418 if (!origProp) {
15419 checkTriggers();
15420 return true; // property is already not defined
15421 } else if (origProp.bypass) {
15422 // then replace the bypass property with the original
15423 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15424 style[prop.name] = origProp.bypassed;
15425 checkTriggers();
15426 return true;
15427 } else {
15428 return false; // we're unsuccessful deleting the bypass
15429 }
15430 }
15431
15432 var printMappingErr = function printMappingErr() {
15433 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');
15434 };
15435
15436 // put the property in the style objects
15437 switch (prop.mapped) {
15438 // flatten the property if mapped
15439 case types.mapData:
15440 {
15441 // flatten the field (e.g. data.foo.bar)
15442 var fields = prop.field.split('.');
15443 var fieldVal = _p.data;
15444 for (var i = 0; i < fields.length && fieldVal; i++) {
15445 var field = fields[i];
15446 fieldVal = fieldVal[field];
15447 }
15448 if (fieldVal == null) {
15449 printMappingErr();
15450 return false;
15451 }
15452 var percent;
15453 if (!number$1(fieldVal)) {
15454 // then don't apply and fall back on the existing style
15455 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15456 return false;
15457 } else {
15458 var fieldWidth = prop.fieldMax - prop.fieldMin;
15459 if (fieldWidth === 0) {
15460 // safety check -- not strictly necessary as no props of zero range should be passed here
15461 percent = 0;
15462 } else {
15463 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15464 }
15465 }
15466
15467 // make sure to bound percent value
15468 if (percent < 0) {
15469 percent = 0;
15470 } else if (percent > 1) {
15471 percent = 1;
15472 }
15473 if (type.color) {
15474 var r1 = prop.valueMin[0];
15475 var r2 = prop.valueMax[0];
15476 var g1 = prop.valueMin[1];
15477 var g2 = prop.valueMax[1];
15478 var b1 = prop.valueMin[2];
15479 var b2 = prop.valueMax[2];
15480 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15481 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15482 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)];
15483 flatProp = {
15484 // colours are simple, so just create the flat property instead of expensive string parsing
15485 bypass: prop.bypass,
15486 // we're a bypass if the mapping property is a bypass
15487 name: prop.name,
15488 value: clr,
15489 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15490 };
15491 } else if (type.number) {
15492 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15493 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15494 } else {
15495 return false; // can only map to colours and numbers
15496 }
15497
15498 if (!flatProp) {
15499 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15500 printMappingErr();
15501 return false;
15502 }
15503 flatProp.mapping = prop; // keep a reference to the mapping
15504 prop = flatProp; // the flattened (mapped) property is the one we want
15505
15506 break;
15507 }
15508
15509 // direct mapping
15510 case types.data:
15511 {
15512 // flatten the field (e.g. data.foo.bar)
15513 var _fields = prop.field.split('.');
15514 var _fieldVal = _p.data;
15515 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15516 var _field = _fields[_i3];
15517 _fieldVal = _fieldVal[_field];
15518 }
15519 if (_fieldVal != null) {
15520 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15521 }
15522 if (!flatProp) {
15523 // if we can't flatten the property, then don't apply and fall back on the existing style
15524 printMappingErr();
15525 return false;
15526 }
15527 flatProp.mapping = prop; // keep a reference to the mapping
15528 prop = flatProp; // the flattened (mapped) property is the one we want
15529
15530 break;
15531 }
15532 case types.fn:
15533 {
15534 var fn = prop.value;
15535 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15536
15537 prop.prevFnValue = fnRetVal;
15538 if (fnRetVal == null) {
15539 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15540 return false;
15541 }
15542 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15543 if (!flatProp) {
15544 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15545 return false;
15546 }
15547 flatProp.mapping = copy(prop); // keep a reference to the mapping
15548 prop = flatProp; // the flattened (mapped) property is the one we want
15549
15550 break;
15551 }
15552 case undefined:
15553 break;
15554 // just set the property
15555
15556 default:
15557 return false;
15558 // not a valid mapping
15559 }
15560
15561 // if the property is a bypass property, then link the resultant property to the original one
15562 if (propIsBypass) {
15563 if (origPropIsBypass) {
15564 // then this bypass overrides the existing one
15565 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15566 } else {
15567 // then link the orig prop to the new bypass
15568 prop.bypassed = origProp;
15569 }
15570 style[prop.name] = prop; // and set
15571 } else {
15572 // prop is not bypass
15573 if (origPropIsBypass) {
15574 // then keep the orig prop (since it's a bypass) and link to the new prop
15575 origProp.bypassed = prop;
15576 } else {
15577 // then just replace the old prop with the new one
15578 style[prop.name] = prop;
15579 }
15580 }
15581 checkTriggers();
15582 return true;
15583};
15584styfn$8.cleanElements = function (eles, keepBypasses) {
15585 for (var i = 0; i < eles.length; i++) {
15586 var ele = eles[i];
15587 this.clearStyleHints(ele);
15588 ele.dirtyCompoundBoundsCache();
15589 ele.dirtyBoundingBoxCache();
15590 if (!keepBypasses) {
15591 ele._private.style = {};
15592 } else {
15593 var style = ele._private.style;
15594 var propNames = Object.keys(style);
15595 for (var j = 0; j < propNames.length; j++) {
15596 var propName = propNames[j];
15597 var eleProp = style[propName];
15598 if (eleProp != null) {
15599 if (eleProp.bypass) {
15600 eleProp.bypassed = null;
15601 } else {
15602 style[propName] = null;
15603 }
15604 }
15605 }
15606 }
15607 }
15608};
15609
15610// updates the visual style for all elements (useful for manual style modification after init)
15611styfn$8.update = function () {
15612 var cy = this._private.cy;
15613 var eles = cy.mutableElements();
15614 eles.updateStyle();
15615};
15616
15617// diffProps : { name => { prev, next } }
15618styfn$8.updateTransitions = function (ele, diffProps) {
15619 var self = this;
15620 var _p = ele._private;
15621 var props = ele.pstyle('transition-property').value;
15622 var duration = ele.pstyle('transition-duration').pfValue;
15623 var delay = ele.pstyle('transition-delay').pfValue;
15624 if (props.length > 0 && duration > 0) {
15625 var style = {};
15626
15627 // build up the style to animate towards
15628 var anyPrev = false;
15629 for (var i = 0; i < props.length; i++) {
15630 var prop = props[i];
15631 var styProp = ele.pstyle(prop);
15632 var diffProp = diffProps[prop];
15633 if (!diffProp) {
15634 continue;
15635 }
15636 var prevProp = diffProp.prev;
15637 var fromProp = prevProp;
15638 var toProp = diffProp.next != null ? diffProp.next : styProp;
15639 var diff = false;
15640 var initVal = void 0;
15641 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15642
15643 if (!fromProp) {
15644 continue;
15645 }
15646
15647 // consider px values
15648 if (number$1(fromProp.pfValue) && number$1(toProp.pfValue)) {
15649 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15650 initVal = fromProp.pfValue + initDt * diff;
15651
15652 // consider numerical values
15653 } else if (number$1(fromProp.value) && number$1(toProp.value)) {
15654 diff = toProp.value - fromProp.value; // nonzero is truthy
15655 initVal = fromProp.value + initDt * diff;
15656
15657 // consider colour values
15658 } else if (array(fromProp.value) && array(toProp.value)) {
15659 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15660 initVal = fromProp.strValue;
15661 }
15662
15663 // the previous value is good for an animation only if it's different
15664 if (diff) {
15665 style[prop] = toProp.strValue; // to val
15666 this.applyBypass(ele, prop, initVal); // from val
15667 anyPrev = true;
15668 }
15669 } // end if props allow ani
15670
15671 // can't transition if there's nothing previous to transition from
15672 if (!anyPrev) {
15673 return;
15674 }
15675 _p.transitioning = true;
15676 new Promise$1(function (resolve) {
15677 if (delay > 0) {
15678 ele.delayAnimation(delay).play().promise().then(resolve);
15679 } else {
15680 resolve();
15681 }
15682 }).then(function () {
15683 return ele.animation({
15684 style: style,
15685 duration: duration,
15686 easing: ele.pstyle('transition-timing-function').value,
15687 queue: false
15688 }).play().promise();
15689 }).then(function () {
15690 // if( !isBypass ){
15691 self.removeBypasses(ele, props);
15692 ele.emitAndNotify('style');
15693 // }
15694
15695 _p.transitioning = false;
15696 });
15697 } else if (_p.transitioning) {
15698 this.removeBypasses(ele, props);
15699 ele.emitAndNotify('style');
15700 _p.transitioning = false;
15701 }
15702};
15703styfn$8.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15704 var prop = this.properties[name];
15705 var triggerCheck = getTrigger(prop);
15706 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15707 onTrigger(prop);
15708 }
15709};
15710styfn$8.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15711 var _this = this;
15712 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15713 return prop.triggersZOrder;
15714 }, function () {
15715 _this._private.cy.notify('zorder', ele);
15716 });
15717};
15718styfn$8.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15719 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15720 return prop.triggersBounds;
15721 }, function (prop) {
15722 ele.dirtyCompoundBoundsCache();
15723 ele.dirtyBoundingBoxCache();
15724
15725 // if the prop change makes the bb of pll bezier edges invalid,
15726 // then dirty the pll edge bb cache as well
15727 if (
15728 // only for beziers -- so performance of other edges isn't affected
15729 prop.triggersBoundsOfParallelBeziers && name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) {
15730 ele.parallelEdges().forEach(function (pllEdge) {
15731 if (pllEdge.isBundledBezier()) {
15732 pllEdge.dirtyBoundingBoxCache();
15733 }
15734 });
15735 }
15736 if (prop.triggersBoundsOfConnectedEdges && name === 'display' && (fromValue === 'none' || toValue === 'none')) {
15737 ele.connectedEdges().forEach(function (edge) {
15738 edge.dirtyBoundingBoxCache();
15739 });
15740 }
15741 });
15742};
15743styfn$8.checkTriggers = function (ele, name, fromValue, toValue) {
15744 ele.dirtyStyleCache();
15745 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15746 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15747};
15748
15749var styfn$7 = {};
15750
15751// bypasses are applied to an existing style on an element, and just tacked on temporarily
15752// returns true iff application was successful for at least 1 specified property
15753styfn$7.applyBypass = function (eles, name, value, updateTransitions) {
15754 var self = this;
15755 var props = [];
15756 var isBypass = true;
15757
15758 // put all the properties (can specify one or many) in an array after parsing them
15759 if (name === '*' || name === '**') {
15760 // apply to all property names
15761
15762 if (value !== undefined) {
15763 for (var i = 0; i < self.properties.length; i++) {
15764 var prop = self.properties[i];
15765 var _name = prop.name;
15766 var parsedProp = this.parse(_name, value, true);
15767 if (parsedProp) {
15768 props.push(parsedProp);
15769 }
15770 }
15771 }
15772 } else if (string(name)) {
15773 // then parse the single property
15774 var _parsedProp = this.parse(name, value, true);
15775 if (_parsedProp) {
15776 props.push(_parsedProp);
15777 }
15778 } else if (plainObject(name)) {
15779 // then parse each property
15780 var specifiedProps = name;
15781 updateTransitions = value;
15782 var names = Object.keys(specifiedProps);
15783 for (var _i = 0; _i < names.length; _i++) {
15784 var _name2 = names[_i];
15785 var _value = specifiedProps[_name2];
15786 if (_value === undefined) {
15787 // try camel case name too
15788 _value = specifiedProps[dash2camel(_name2)];
15789 }
15790 if (_value !== undefined) {
15791 var _parsedProp2 = this.parse(_name2, _value, true);
15792 if (_parsedProp2) {
15793 props.push(_parsedProp2);
15794 }
15795 }
15796 }
15797 } else {
15798 // can't do anything without well defined properties
15799 return false;
15800 }
15801
15802 // we've failed if there are no valid properties
15803 if (props.length === 0) {
15804 return false;
15805 }
15806
15807 // now, apply the bypass properties on the elements
15808 var ret = false; // return true if at least one succesful bypass applied
15809 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15810 // for each ele
15811 var ele = eles[_i2];
15812 var diffProps = {};
15813 var diffProp = void 0;
15814 for (var j = 0; j < props.length; j++) {
15815 // for each prop
15816 var _prop = props[j];
15817 if (updateTransitions) {
15818 var prevProp = ele.pstyle(_prop.name);
15819 diffProp = diffProps[_prop.name] = {
15820 prev: prevProp
15821 };
15822 }
15823 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15824 if (updateTransitions) {
15825 diffProp.next = ele.pstyle(_prop.name);
15826 }
15827 } // for props
15828
15829 if (ret) {
15830 this.updateStyleHints(ele);
15831 }
15832 if (updateTransitions) {
15833 this.updateTransitions(ele, diffProps, isBypass);
15834 }
15835 } // for eles
15836
15837 return ret;
15838};
15839
15840// only useful in specific cases like animation
15841styfn$7.overrideBypass = function (eles, name, value) {
15842 name = camel2dash(name);
15843 for (var i = 0; i < eles.length; i++) {
15844 var ele = eles[i];
15845 var prop = ele._private.style[name];
15846 var type = this.properties[name].type;
15847 var isColor = type.color;
15848 var isMulti = type.mutiple;
15849 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15850 if (!prop || !prop.bypass) {
15851 // need a bypass if one doesn't exist
15852 this.applyBypass(ele, name, value);
15853 } else {
15854 prop.value = value;
15855 if (prop.pfValue != null) {
15856 prop.pfValue = value;
15857 }
15858 if (isColor) {
15859 prop.strValue = 'rgb(' + value.join(',') + ')';
15860 } else if (isMulti) {
15861 prop.strValue = value.join(' ');
15862 } else {
15863 prop.strValue = '' + value;
15864 }
15865 this.updateStyleHints(ele);
15866 }
15867 this.checkTriggers(ele, name, oldValue, value);
15868 }
15869};
15870styfn$7.removeAllBypasses = function (eles, updateTransitions) {
15871 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15872};
15873styfn$7.removeBypasses = function (eles, props, updateTransitions) {
15874 var isBypass = true;
15875 for (var j = 0; j < eles.length; j++) {
15876 var ele = eles[j];
15877 var diffProps = {};
15878 for (var i = 0; i < props.length; i++) {
15879 var name = props[i];
15880 var prop = this.properties[name];
15881 var prevProp = ele.pstyle(prop.name);
15882 if (!prevProp || !prevProp.bypass) {
15883 // if a bypass doesn't exist for the prop, nothing needs to be removed
15884 continue;
15885 }
15886 var value = ''; // empty => remove bypass
15887 var parsedProp = this.parse(name, value, true);
15888 var diffProp = diffProps[prop.name] = {
15889 prev: prevProp
15890 };
15891 this.applyParsedProperty(ele, parsedProp);
15892 diffProp.next = ele.pstyle(prop.name);
15893 } // for props
15894
15895 this.updateStyleHints(ele);
15896 if (updateTransitions) {
15897 this.updateTransitions(ele, diffProps, isBypass);
15898 }
15899 } // for eles
15900};
15901
15902var styfn$6 = {};
15903
15904// gets what an em size corresponds to in pixels relative to a dom element
15905styfn$6.getEmSizeInPixels = function () {
15906 var px = this.containerCss('font-size');
15907 if (px != null) {
15908 return parseFloat(px);
15909 } else {
15910 return 1; // for headless
15911 }
15912};
15913
15914// gets css property from the core container
15915styfn$6.containerCss = function (propName) {
15916 var cy = this._private.cy;
15917 var domElement = cy.container();
15918 var containerWindow = cy.window();
15919 if (containerWindow && domElement && containerWindow.getComputedStyle) {
15920 return containerWindow.getComputedStyle(domElement).getPropertyValue(propName);
15921 }
15922};
15923
15924var styfn$5 = {};
15925
15926// gets the rendered style for an element
15927styfn$5.getRenderedStyle = function (ele, prop) {
15928 if (prop) {
15929 return this.getStylePropertyValue(ele, prop, true);
15930 } else {
15931 return this.getRawStyle(ele, true);
15932 }
15933};
15934
15935// gets the raw style for an element
15936styfn$5.getRawStyle = function (ele, isRenderedVal) {
15937 var self = this;
15938 ele = ele[0]; // insure it's an element
15939
15940 if (ele) {
15941 var rstyle = {};
15942 for (var i = 0; i < self.properties.length; i++) {
15943 var prop = self.properties[i];
15944 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15945 if (val != null) {
15946 rstyle[prop.name] = val;
15947 rstyle[dash2camel(prop.name)] = val;
15948 }
15949 }
15950 return rstyle;
15951 }
15952};
15953styfn$5.getIndexedStyle = function (ele, property, subproperty, index) {
15954 var pstyle = ele.pstyle(property)[subproperty][index];
15955 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15956};
15957styfn$5.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15958 var self = this;
15959 ele = ele[0]; // insure it's an element
15960
15961 if (ele) {
15962 var prop = self.properties[propName];
15963 if (prop.alias) {
15964 prop = prop.pointsTo;
15965 }
15966 var type = prop.type;
15967 var styleProp = ele.pstyle(prop.name);
15968 if (styleProp) {
15969 var value = styleProp.value,
15970 units = styleProp.units,
15971 strValue = styleProp.strValue;
15972 if (isRenderedVal && type.number && value != null && number$1(value)) {
15973 var zoom = ele.cy().zoom();
15974 var getRenderedValue = function getRenderedValue(val) {
15975 return val * zoom;
15976 };
15977 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15978 return getRenderedValue(val) + units;
15979 };
15980 var isArrayValue = array(value);
15981 var haveUnits = isArrayValue ? units.every(function (u) {
15982 return u != null;
15983 }) : units != null;
15984 if (haveUnits) {
15985 if (isArrayValue) {
15986 return value.map(function (v, i) {
15987 return getValueStringWithUnits(v, units[i]);
15988 }).join(' ');
15989 } else {
15990 return getValueStringWithUnits(value, units);
15991 }
15992 } else {
15993 if (isArrayValue) {
15994 return value.map(function (v) {
15995 return string(v) ? v : '' + getRenderedValue(v);
15996 }).join(' ');
15997 } else {
15998 return '' + getRenderedValue(value);
15999 }
16000 }
16001 } else if (strValue != null) {
16002 return strValue;
16003 }
16004 }
16005 return null;
16006 }
16007};
16008styfn$5.getAnimationStartStyle = function (ele, aniProps) {
16009 var rstyle = {};
16010 for (var i = 0; i < aniProps.length; i++) {
16011 var aniProp = aniProps[i];
16012 var name = aniProp.name;
16013 var styleProp = ele.pstyle(name);
16014 if (styleProp !== undefined) {
16015 // then make a prop of it
16016 if (plainObject(styleProp)) {
16017 styleProp = this.parse(name, styleProp.strValue);
16018 } else {
16019 styleProp = this.parse(name, styleProp);
16020 }
16021 }
16022 if (styleProp) {
16023 rstyle[name] = styleProp;
16024 }
16025 }
16026 return rstyle;
16027};
16028styfn$5.getPropsList = function (propsObj) {
16029 var self = this;
16030 var rstyle = [];
16031 var style = propsObj;
16032 var props = self.properties;
16033 if (style) {
16034 var names = Object.keys(style);
16035 for (var i = 0; i < names.length; i++) {
16036 var name = names[i];
16037 var val = style[name];
16038 var prop = props[name] || props[camel2dash(name)];
16039 var styleProp = this.parse(prop.name, val);
16040 if (styleProp) {
16041 rstyle.push(styleProp);
16042 }
16043 }
16044 }
16045 return rstyle;
16046};
16047styfn$5.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
16048 var hash = seed.slice();
16049 var name, val, strVal, chVal;
16050 var i, j;
16051 for (i = 0; i < propNames.length; i++) {
16052 name = propNames[i];
16053 val = ele.pstyle(name, false);
16054 if (val == null) {
16055 continue;
16056 } else if (val.pfValue != null) {
16057 hash[0] = hashInt(chVal, hash[0]);
16058 hash[1] = hashIntAlt(chVal, hash[1]);
16059 } else {
16060 strVal = val.strValue;
16061 for (j = 0; j < strVal.length; j++) {
16062 chVal = strVal.charCodeAt(j);
16063 hash[0] = hashInt(chVal, hash[0]);
16064 hash[1] = hashIntAlt(chVal, hash[1]);
16065 }
16066 }
16067 }
16068 return hash;
16069};
16070styfn$5.getPropertiesHash = styfn$5.getNonDefaultPropertiesHash;
16071
16072var styfn$4 = {};
16073styfn$4.appendFromJson = function (json) {
16074 var style = this;
16075 for (var i = 0; i < json.length; i++) {
16076 var context = json[i];
16077 var selector = context.selector;
16078 var props = context.style || context.css;
16079 var names = Object.keys(props);
16080 style.selector(selector); // apply selector
16081
16082 for (var j = 0; j < names.length; j++) {
16083 var name = names[j];
16084 var value = props[name];
16085 style.css(name, value); // apply property
16086 }
16087 }
16088
16089 return style;
16090};
16091
16092// accessible cy.style() function
16093styfn$4.fromJson = function (json) {
16094 var style = this;
16095 style.resetToDefault();
16096 style.appendFromJson(json);
16097 return style;
16098};
16099
16100// get json from cy.style() api
16101styfn$4.json = function () {
16102 var json = [];
16103 for (var i = this.defaultLength; i < this.length; i++) {
16104 var cxt = this[i];
16105 var selector = cxt.selector;
16106 var props = cxt.properties;
16107 var css = {};
16108 for (var j = 0; j < props.length; j++) {
16109 var prop = props[j];
16110 css[prop.name] = prop.strValue;
16111 }
16112 json.push({
16113 selector: !selector ? 'core' : selector.toString(),
16114 style: css
16115 });
16116 }
16117 return json;
16118};
16119
16120var styfn$3 = {};
16121styfn$3.appendFromString = function (string) {
16122 var self = this;
16123 var style = this;
16124 var remaining = '' + string;
16125 var selAndBlockStr;
16126 var blockRem;
16127 var propAndValStr;
16128
16129 // remove comments from the style string
16130 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
16131 function removeSelAndBlockFromRemaining() {
16132 // remove the parsed selector and block from the remaining text to parse
16133 if (remaining.length > selAndBlockStr.length) {
16134 remaining = remaining.substr(selAndBlockStr.length);
16135 } else {
16136 remaining = '';
16137 }
16138 }
16139 function removePropAndValFromRem() {
16140 // remove the parsed property and value from the remaining block text to parse
16141 if (blockRem.length > propAndValStr.length) {
16142 blockRem = blockRem.substr(propAndValStr.length);
16143 } else {
16144 blockRem = '';
16145 }
16146 }
16147 for (;;) {
16148 var nothingLeftToParse = remaining.match(/^\s*$/);
16149 if (nothingLeftToParse) {
16150 break;
16151 }
16152 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
16153 if (!selAndBlock) {
16154 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
16155 break;
16156 }
16157 selAndBlockStr = selAndBlock[0];
16158
16159 // parse the selector
16160 var selectorStr = selAndBlock[1];
16161 if (selectorStr !== 'core') {
16162 var selector = new Selector(selectorStr);
16163 if (selector.invalid) {
16164 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr);
16165
16166 // skip this selector and block
16167 removeSelAndBlockFromRemaining();
16168 continue;
16169 }
16170 }
16171
16172 // parse the block of properties and values
16173 var blockStr = selAndBlock[2];
16174 var invalidBlock = false;
16175 blockRem = blockStr;
16176 var props = [];
16177 for (;;) {
16178 var _nothingLeftToParse = blockRem.match(/^\s*$/);
16179 if (_nothingLeftToParse) {
16180 break;
16181 }
16182 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)(?:\s*;|\s*$)/);
16183 if (!propAndVal) {
16184 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
16185 invalidBlock = true;
16186 break;
16187 }
16188 propAndValStr = propAndVal[0];
16189 var propStr = propAndVal[1];
16190 var valStr = propAndVal[2];
16191 var prop = self.properties[propStr];
16192 if (!prop) {
16193 warn('Skipping property: Invalid property name in: ' + propAndValStr);
16194
16195 // skip this property in the block
16196 removePropAndValFromRem();
16197 continue;
16198 }
16199 var parsedProp = style.parse(propStr, valStr);
16200 if (!parsedProp) {
16201 warn('Skipping property: Invalid property definition in: ' + propAndValStr);
16202
16203 // skip this property in the block
16204 removePropAndValFromRem();
16205 continue;
16206 }
16207 props.push({
16208 name: propStr,
16209 val: valStr
16210 });
16211 removePropAndValFromRem();
16212 }
16213 if (invalidBlock) {
16214 removeSelAndBlockFromRemaining();
16215 break;
16216 }
16217
16218 // put the parsed block in the style
16219 style.selector(selectorStr);
16220 for (var i = 0; i < props.length; i++) {
16221 var _prop = props[i];
16222 style.css(_prop.name, _prop.val);
16223 }
16224 removeSelAndBlockFromRemaining();
16225 }
16226 return style;
16227};
16228styfn$3.fromString = function (string) {
16229 var style = this;
16230 style.resetToDefault();
16231 style.appendFromString(string);
16232 return style;
16233};
16234
16235var styfn$2 = {};
16236(function () {
16237 var number$1 = number;
16238 var rgba = rgbaNoBackRefs;
16239 var hsla = hslaNoBackRefs;
16240 var hex3$1 = hex3;
16241 var hex6$1 = hex6;
16242 var data = function data(prefix) {
16243 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
16244 };
16245 var mapData = function mapData(prefix) {
16246 var mapArg = number$1 + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
16247 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number$1 + ')\\s*\\,\\s*(' + number$1 + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
16248 };
16249 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$'];
16250
16251 // each visual style property has a type and needs to be validated according to it
16252 styfn$2.types = {
16253 time: {
16254 number: true,
16255 min: 0,
16256 units: 's|ms',
16257 implicitUnits: 'ms'
16258 },
16259 percent: {
16260 number: true,
16261 min: 0,
16262 max: 100,
16263 units: '%',
16264 implicitUnits: '%'
16265 },
16266 percentages: {
16267 number: true,
16268 min: 0,
16269 max: 100,
16270 units: '%',
16271 implicitUnits: '%',
16272 multiple: true
16273 },
16274 zeroOneNumber: {
16275 number: true,
16276 min: 0,
16277 max: 1,
16278 unitless: true
16279 },
16280 zeroOneNumbers: {
16281 number: true,
16282 min: 0,
16283 max: 1,
16284 unitless: true,
16285 multiple: true
16286 },
16287 nOneOneNumber: {
16288 number: true,
16289 min: -1,
16290 max: 1,
16291 unitless: true
16292 },
16293 nonNegativeInt: {
16294 number: true,
16295 min: 0,
16296 integer: true,
16297 unitless: true
16298 },
16299 nonNegativeNumber: {
16300 number: true,
16301 min: 0,
16302 unitless: true
16303 },
16304 position: {
16305 enums: ['parent', 'origin']
16306 },
16307 nodeSize: {
16308 number: true,
16309 min: 0,
16310 enums: ['label']
16311 },
16312 number: {
16313 number: true,
16314 unitless: true
16315 },
16316 numbers: {
16317 number: true,
16318 unitless: true,
16319 multiple: true
16320 },
16321 positiveNumber: {
16322 number: true,
16323 unitless: true,
16324 min: 0,
16325 strictMin: true
16326 },
16327 size: {
16328 number: true,
16329 min: 0
16330 },
16331 bidirectionalSize: {
16332 number: true
16333 },
16334 // allows negative
16335 bidirectionalSizeMaybePercent: {
16336 number: true,
16337 allowPercent: true
16338 },
16339 // allows negative
16340 bidirectionalSizes: {
16341 number: true,
16342 multiple: true
16343 },
16344 // allows negative
16345 sizeMaybePercent: {
16346 number: true,
16347 min: 0,
16348 allowPercent: true
16349 },
16350 axisDirection: {
16351 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16352 },
16353 paddingRelativeTo: {
16354 enums: ['width', 'height', 'average', 'min', 'max']
16355 },
16356 bgWH: {
16357 number: true,
16358 min: 0,
16359 allowPercent: true,
16360 enums: ['auto'],
16361 multiple: true
16362 },
16363 bgPos: {
16364 number: true,
16365 allowPercent: true,
16366 multiple: true
16367 },
16368 bgRelativeTo: {
16369 enums: ['inner', 'include-padding'],
16370 multiple: true
16371 },
16372 bgRepeat: {
16373 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16374 multiple: true
16375 },
16376 bgFit: {
16377 enums: ['none', 'contain', 'cover'],
16378 multiple: true
16379 },
16380 bgCrossOrigin: {
16381 enums: ['anonymous', 'use-credentials', 'null'],
16382 multiple: true
16383 },
16384 bgClip: {
16385 enums: ['none', 'node'],
16386 multiple: true
16387 },
16388 bgContainment: {
16389 enums: ['inside', 'over'],
16390 multiple: true
16391 },
16392 color: {
16393 color: true
16394 },
16395 colors: {
16396 color: true,
16397 multiple: true
16398 },
16399 fill: {
16400 enums: ['solid', 'linear-gradient', 'radial-gradient']
16401 },
16402 bool: {
16403 enums: ['yes', 'no']
16404 },
16405 bools: {
16406 enums: ['yes', 'no'],
16407 multiple: true
16408 },
16409 lineStyle: {
16410 enums: ['solid', 'dotted', 'dashed']
16411 },
16412 lineCap: {
16413 enums: ['butt', 'round', 'square']
16414 },
16415 linePosition: {
16416 enums: ['center', 'inside', 'outside']
16417 },
16418 lineJoin: {
16419 enums: ['round', 'bevel', 'miter']
16420 },
16421 borderStyle: {
16422 enums: ['solid', 'dotted', 'dashed', 'double']
16423 },
16424 curveStyle: {
16425 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'straight-triangle', 'taxi', 'round-segments', 'round-taxi']
16426 },
16427 radiusType: {
16428 enums: ['arc-radius', 'influence-radius'],
16429 multiple: true
16430 },
16431 fontFamily: {
16432 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16433 },
16434 fontStyle: {
16435 enums: ['italic', 'normal', 'oblique']
16436 },
16437 fontWeight: {
16438 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16439 },
16440 textDecoration: {
16441 enums: ['none', 'underline', 'overline', 'line-through']
16442 },
16443 textTransform: {
16444 enums: ['none', 'uppercase', 'lowercase']
16445 },
16446 textWrap: {
16447 enums: ['none', 'wrap', 'ellipsis']
16448 },
16449 textOverflowWrap: {
16450 enums: ['whitespace', 'anywhere']
16451 },
16452 textBackgroundShape: {
16453 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16454 },
16455 nodeShape: {
16456 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', 'right-rhomboid', 'polygon']
16457 },
16458 overlayShape: {
16459 enums: ['roundrectangle', 'round-rectangle', 'ellipse']
16460 },
16461 cornerRadius: {
16462 number: true,
16463 min: 0,
16464 units: 'px|em',
16465 implicitUnits: 'px',
16466 enums: ['auto']
16467 },
16468 compoundIncludeLabels: {
16469 enums: ['include', 'exclude']
16470 },
16471 arrowShape: {
16472 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16473 },
16474 arrowFill: {
16475 enums: ['filled', 'hollow']
16476 },
16477 arrowWidth: {
16478 number: true,
16479 units: '%|px|em',
16480 implicitUnits: 'px',
16481 enums: ['match-line']
16482 },
16483 display: {
16484 enums: ['element', 'none']
16485 },
16486 visibility: {
16487 enums: ['hidden', 'visible']
16488 },
16489 zCompoundDepth: {
16490 enums: ['bottom', 'orphan', 'auto', 'top']
16491 },
16492 zIndexCompare: {
16493 enums: ['auto', 'manual']
16494 },
16495 valign: {
16496 enums: ['top', 'center', 'bottom']
16497 },
16498 halign: {
16499 enums: ['left', 'center', 'right']
16500 },
16501 justification: {
16502 enums: ['left', 'center', 'right', 'auto']
16503 },
16504 text: {
16505 string: true
16506 },
16507 data: {
16508 mapping: true,
16509 regex: data('data')
16510 },
16511 layoutData: {
16512 mapping: true,
16513 regex: data('layoutData')
16514 },
16515 scratch: {
16516 mapping: true,
16517 regex: data('scratch')
16518 },
16519 mapData: {
16520 mapping: true,
16521 regex: mapData('mapData')
16522 },
16523 mapLayoutData: {
16524 mapping: true,
16525 regex: mapData('mapLayoutData')
16526 },
16527 mapScratch: {
16528 mapping: true,
16529 regex: mapData('mapScratch')
16530 },
16531 fn: {
16532 mapping: true,
16533 fn: true
16534 },
16535 url: {
16536 regexes: urlRegexes,
16537 singleRegexMatchValue: true
16538 },
16539 urls: {
16540 regexes: urlRegexes,
16541 singleRegexMatchValue: true,
16542 multiple: true
16543 },
16544 propList: {
16545 propList: true
16546 },
16547 angle: {
16548 number: true,
16549 units: 'deg|rad',
16550 implicitUnits: 'rad'
16551 },
16552 textRotation: {
16553 number: true,
16554 units: 'deg|rad',
16555 implicitUnits: 'rad',
16556 enums: ['none', 'autorotate']
16557 },
16558 polygonPointList: {
16559 number: true,
16560 multiple: true,
16561 evenMultiple: true,
16562 min: -1,
16563 max: 1,
16564 unitless: true
16565 },
16566 edgeDistances: {
16567 enums: ['intersection', 'node-position', 'endpoints']
16568 },
16569 edgeEndpoint: {
16570 number: true,
16571 multiple: true,
16572 units: '%|px|em|deg|rad',
16573 implicitUnits: 'px',
16574 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16575 singleEnum: true,
16576 validate: function validate(valArr, unitsArr) {
16577 switch (valArr.length) {
16578 case 2:
16579 // can be % or px only
16580 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16581 case 1:
16582 // can be enum, deg, or rad only
16583 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16584 default:
16585 return false;
16586 }
16587 }
16588 },
16589 easing: {
16590 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*\\)$'],
16591 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']
16592 },
16593 gradientDirection: {
16594 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
16595 ]
16596 },
16597
16598 boundsExpansion: {
16599 number: true,
16600 multiple: true,
16601 min: 0,
16602 validate: function validate(valArr) {
16603 var length = valArr.length;
16604 return length === 1 || length === 2 || length === 4;
16605 }
16606 }
16607 };
16608 var diff = {
16609 zeroNonZero: function zeroNonZero(val1, val2) {
16610 if ((val1 == null || val2 == null) && val1 !== val2) {
16611 return true; // null cases could represent any value
16612 }
16613 if (val1 == 0 && val2 != 0) {
16614 return true;
16615 } else if (val1 != 0 && val2 == 0) {
16616 return true;
16617 } else {
16618 return false;
16619 }
16620 },
16621 any: function any(val1, val2) {
16622 return val1 != val2;
16623 },
16624 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16625 var empty1 = emptyString(str1);
16626 var empty2 = emptyString(str2);
16627 return empty1 && !empty2 || !empty1 && empty2;
16628 }
16629 };
16630
16631 // define visual style properties
16632 //
16633 // - n.b. adding a new group of props may require updates to updateStyleHints()
16634 // - adding new props to an existing group gets handled automatically
16635
16636 var t = styfn$2.types;
16637 var mainLabel = [{
16638 name: 'label',
16639 type: t.text,
16640 triggersBounds: diff.any,
16641 triggersZOrder: diff.emptyNonEmpty
16642 }, {
16643 name: 'text-rotation',
16644 type: t.textRotation,
16645 triggersBounds: diff.any
16646 }, {
16647 name: 'text-margin-x',
16648 type: t.bidirectionalSize,
16649 triggersBounds: diff.any
16650 }, {
16651 name: 'text-margin-y',
16652 type: t.bidirectionalSize,
16653 triggersBounds: diff.any
16654 }];
16655 var sourceLabel = [{
16656 name: 'source-label',
16657 type: t.text,
16658 triggersBounds: diff.any
16659 }, {
16660 name: 'source-text-rotation',
16661 type: t.textRotation,
16662 triggersBounds: diff.any
16663 }, {
16664 name: 'source-text-margin-x',
16665 type: t.bidirectionalSize,
16666 triggersBounds: diff.any
16667 }, {
16668 name: 'source-text-margin-y',
16669 type: t.bidirectionalSize,
16670 triggersBounds: diff.any
16671 }, {
16672 name: 'source-text-offset',
16673 type: t.size,
16674 triggersBounds: diff.any
16675 }];
16676 var targetLabel = [{
16677 name: 'target-label',
16678 type: t.text,
16679 triggersBounds: diff.any
16680 }, {
16681 name: 'target-text-rotation',
16682 type: t.textRotation,
16683 triggersBounds: diff.any
16684 }, {
16685 name: 'target-text-margin-x',
16686 type: t.bidirectionalSize,
16687 triggersBounds: diff.any
16688 }, {
16689 name: 'target-text-margin-y',
16690 type: t.bidirectionalSize,
16691 triggersBounds: diff.any
16692 }, {
16693 name: 'target-text-offset',
16694 type: t.size,
16695 triggersBounds: diff.any
16696 }];
16697 var labelDimensions = [{
16698 name: 'font-family',
16699 type: t.fontFamily,
16700 triggersBounds: diff.any
16701 }, {
16702 name: 'font-style',
16703 type: t.fontStyle,
16704 triggersBounds: diff.any
16705 }, {
16706 name: 'font-weight',
16707 type: t.fontWeight,
16708 triggersBounds: diff.any
16709 }, {
16710 name: 'font-size',
16711 type: t.size,
16712 triggersBounds: diff.any
16713 }, {
16714 name: 'text-transform',
16715 type: t.textTransform,
16716 triggersBounds: diff.any
16717 }, {
16718 name: 'text-wrap',
16719 type: t.textWrap,
16720 triggersBounds: diff.any
16721 }, {
16722 name: 'text-overflow-wrap',
16723 type: t.textOverflowWrap,
16724 triggersBounds: diff.any
16725 }, {
16726 name: 'text-max-width',
16727 type: t.size,
16728 triggersBounds: diff.any
16729 }, {
16730 name: 'text-outline-width',
16731 type: t.size,
16732 triggersBounds: diff.any
16733 }, {
16734 name: 'line-height',
16735 type: t.positiveNumber,
16736 triggersBounds: diff.any
16737 }];
16738 var commonLabel = [{
16739 name: 'text-valign',
16740 type: t.valign,
16741 triggersBounds: diff.any
16742 }, {
16743 name: 'text-halign',
16744 type: t.halign,
16745 triggersBounds: diff.any
16746 }, {
16747 name: 'color',
16748 type: t.color
16749 }, {
16750 name: 'text-outline-color',
16751 type: t.color
16752 }, {
16753 name: 'text-outline-opacity',
16754 type: t.zeroOneNumber
16755 }, {
16756 name: 'text-background-color',
16757 type: t.color
16758 }, {
16759 name: 'text-background-opacity',
16760 type: t.zeroOneNumber
16761 }, {
16762 name: 'text-background-padding',
16763 type: t.size,
16764 triggersBounds: diff.any
16765 }, {
16766 name: 'text-border-opacity',
16767 type: t.zeroOneNumber
16768 }, {
16769 name: 'text-border-color',
16770 type: t.color
16771 }, {
16772 name: 'text-border-width',
16773 type: t.size,
16774 triggersBounds: diff.any
16775 }, {
16776 name: 'text-border-style',
16777 type: t.borderStyle,
16778 triggersBounds: diff.any
16779 }, {
16780 name: 'text-background-shape',
16781 type: t.textBackgroundShape,
16782 triggersBounds: diff.any
16783 }, {
16784 name: 'text-justification',
16785 type: t.justification
16786 }];
16787 var behavior = [{
16788 name: 'events',
16789 type: t.bool,
16790 triggersZOrder: diff.any
16791 }, {
16792 name: 'text-events',
16793 type: t.bool,
16794 triggersZOrder: diff.any
16795 }];
16796 var visibility = [{
16797 name: 'display',
16798 type: t.display,
16799 triggersZOrder: diff.any,
16800 triggersBounds: diff.any,
16801 triggersBoundsOfConnectedEdges: true
16802 }, {
16803 name: 'visibility',
16804 type: t.visibility,
16805 triggersZOrder: diff.any
16806 }, {
16807 name: 'opacity',
16808 type: t.zeroOneNumber,
16809 triggersZOrder: diff.zeroNonZero
16810 }, {
16811 name: 'text-opacity',
16812 type: t.zeroOneNumber
16813 }, {
16814 name: 'min-zoomed-font-size',
16815 type: t.size
16816 }, {
16817 name: 'z-compound-depth',
16818 type: t.zCompoundDepth,
16819 triggersZOrder: diff.any
16820 }, {
16821 name: 'z-index-compare',
16822 type: t.zIndexCompare,
16823 triggersZOrder: diff.any
16824 }, {
16825 name: 'z-index',
16826 type: t.number,
16827 triggersZOrder: diff.any
16828 }];
16829 var overlay = [{
16830 name: 'overlay-padding',
16831 type: t.size,
16832 triggersBounds: diff.any
16833 }, {
16834 name: 'overlay-color',
16835 type: t.color
16836 }, {
16837 name: 'overlay-opacity',
16838 type: t.zeroOneNumber,
16839 triggersBounds: diff.zeroNonZero
16840 }, {
16841 name: 'overlay-shape',
16842 type: t.overlayShape,
16843 triggersBounds: diff.any
16844 }, {
16845 name: 'overlay-corner-radius',
16846 type: t.cornerRadius
16847 }];
16848 var underlay = [{
16849 name: 'underlay-padding',
16850 type: t.size,
16851 triggersBounds: diff.any
16852 }, {
16853 name: 'underlay-color',
16854 type: t.color
16855 }, {
16856 name: 'underlay-opacity',
16857 type: t.zeroOneNumber,
16858 triggersBounds: diff.zeroNonZero
16859 }, {
16860 name: 'underlay-shape',
16861 type: t.overlayShape,
16862 triggersBounds: diff.any
16863 }, {
16864 name: 'underlay-corner-radius',
16865 type: t.cornerRadius
16866 }];
16867 var transition = [{
16868 name: 'transition-property',
16869 type: t.propList
16870 }, {
16871 name: 'transition-duration',
16872 type: t.time
16873 }, {
16874 name: 'transition-delay',
16875 type: t.time
16876 }, {
16877 name: 'transition-timing-function',
16878 type: t.easing
16879 }];
16880 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16881 if (parsedProp.value === 'label') {
16882 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16883 } else {
16884 return parsedProp.pfValue;
16885 }
16886 };
16887 var nodeBody = [{
16888 name: 'height',
16889 type: t.nodeSize,
16890 triggersBounds: diff.any,
16891 hashOverride: nodeSizeHashOverride
16892 }, {
16893 name: 'width',
16894 type: t.nodeSize,
16895 triggersBounds: diff.any,
16896 hashOverride: nodeSizeHashOverride
16897 }, {
16898 name: 'shape',
16899 type: t.nodeShape,
16900 triggersBounds: diff.any
16901 }, {
16902 name: 'shape-polygon-points',
16903 type: t.polygonPointList,
16904 triggersBounds: diff.any
16905 }, {
16906 name: 'corner-radius',
16907 type: t.cornerRadius
16908 }, {
16909 name: 'background-color',
16910 type: t.color
16911 }, {
16912 name: 'background-fill',
16913 type: t.fill
16914 }, {
16915 name: 'background-opacity',
16916 type: t.zeroOneNumber
16917 }, {
16918 name: 'background-blacken',
16919 type: t.nOneOneNumber
16920 }, {
16921 name: 'background-gradient-stop-colors',
16922 type: t.colors
16923 }, {
16924 name: 'background-gradient-stop-positions',
16925 type: t.percentages
16926 }, {
16927 name: 'background-gradient-direction',
16928 type: t.gradientDirection
16929 }, {
16930 name: 'padding',
16931 type: t.sizeMaybePercent,
16932 triggersBounds: diff.any
16933 }, {
16934 name: 'padding-relative-to',
16935 type: t.paddingRelativeTo,
16936 triggersBounds: diff.any
16937 }, {
16938 name: 'bounds-expansion',
16939 type: t.boundsExpansion,
16940 triggersBounds: diff.any
16941 }];
16942 var nodeBorder = [{
16943 name: 'border-color',
16944 type: t.color
16945 }, {
16946 name: 'border-opacity',
16947 type: t.zeroOneNumber
16948 }, {
16949 name: 'border-width',
16950 type: t.size,
16951 triggersBounds: diff.any
16952 }, {
16953 name: 'border-style',
16954 type: t.borderStyle
16955 }, {
16956 name: 'border-cap',
16957 type: t.lineCap
16958 }, {
16959 name: 'border-join',
16960 type: t.lineJoin
16961 }, {
16962 name: 'border-dash-pattern',
16963 type: t.numbers
16964 }, {
16965 name: 'border-dash-offset',
16966 type: t.number
16967 }, {
16968 name: 'border-position',
16969 type: t.linePosition
16970 }];
16971 var nodeOutline = [{
16972 name: 'outline-color',
16973 type: t.color
16974 }, {
16975 name: 'outline-opacity',
16976 type: t.zeroOneNumber
16977 }, {
16978 name: 'outline-width',
16979 type: t.size,
16980 triggersBounds: diff.any
16981 }, {
16982 name: 'outline-style',
16983 type: t.borderStyle
16984 }, {
16985 name: 'outline-offset',
16986 type: t.size,
16987 triggersBounds: diff.any
16988 }];
16989 var backgroundImage = [{
16990 name: 'background-image',
16991 type: t.urls
16992 }, {
16993 name: 'background-image-crossorigin',
16994 type: t.bgCrossOrigin
16995 }, {
16996 name: 'background-image-opacity',
16997 type: t.zeroOneNumbers
16998 }, {
16999 name: 'background-image-containment',
17000 type: t.bgContainment
17001 }, {
17002 name: 'background-image-smoothing',
17003 type: t.bools
17004 }, {
17005 name: 'background-position-x',
17006 type: t.bgPos
17007 }, {
17008 name: 'background-position-y',
17009 type: t.bgPos
17010 }, {
17011 name: 'background-width-relative-to',
17012 type: t.bgRelativeTo
17013 }, {
17014 name: 'background-height-relative-to',
17015 type: t.bgRelativeTo
17016 }, {
17017 name: 'background-repeat',
17018 type: t.bgRepeat
17019 }, {
17020 name: 'background-fit',
17021 type: t.bgFit
17022 }, {
17023 name: 'background-clip',
17024 type: t.bgClip
17025 }, {
17026 name: 'background-width',
17027 type: t.bgWH
17028 }, {
17029 name: 'background-height',
17030 type: t.bgWH
17031 }, {
17032 name: 'background-offset-x',
17033 type: t.bgPos
17034 }, {
17035 name: 'background-offset-y',
17036 type: t.bgPos
17037 }];
17038 var compound = [{
17039 name: 'position',
17040 type: t.position,
17041 triggersBounds: diff.any
17042 }, {
17043 name: 'compound-sizing-wrt-labels',
17044 type: t.compoundIncludeLabels,
17045 triggersBounds: diff.any
17046 }, {
17047 name: 'min-width',
17048 type: t.size,
17049 triggersBounds: diff.any
17050 }, {
17051 name: 'min-width-bias-left',
17052 type: t.sizeMaybePercent,
17053 triggersBounds: diff.any
17054 }, {
17055 name: 'min-width-bias-right',
17056 type: t.sizeMaybePercent,
17057 triggersBounds: diff.any
17058 }, {
17059 name: 'min-height',
17060 type: t.size,
17061 triggersBounds: diff.any
17062 }, {
17063 name: 'min-height-bias-top',
17064 type: t.sizeMaybePercent,
17065 triggersBounds: diff.any
17066 }, {
17067 name: 'min-height-bias-bottom',
17068 type: t.sizeMaybePercent,
17069 triggersBounds: diff.any
17070 }];
17071 var edgeLine = [{
17072 name: 'line-style',
17073 type: t.lineStyle
17074 }, {
17075 name: 'line-color',
17076 type: t.color
17077 }, {
17078 name: 'line-fill',
17079 type: t.fill
17080 }, {
17081 name: 'line-cap',
17082 type: t.lineCap
17083 }, {
17084 name: 'line-opacity',
17085 type: t.zeroOneNumber
17086 }, {
17087 name: 'line-dash-pattern',
17088 type: t.numbers
17089 }, {
17090 name: 'line-dash-offset',
17091 type: t.number
17092 }, {
17093 name: 'line-outline-width',
17094 type: t.size
17095 }, {
17096 name: 'line-outline-color',
17097 type: t.color
17098 }, {
17099 name: 'line-gradient-stop-colors',
17100 type: t.colors
17101 }, {
17102 name: 'line-gradient-stop-positions',
17103 type: t.percentages
17104 }, {
17105 name: 'curve-style',
17106 type: t.curveStyle,
17107 triggersBounds: diff.any,
17108 triggersBoundsOfParallelBeziers: true
17109 }, {
17110 name: 'haystack-radius',
17111 type: t.zeroOneNumber,
17112 triggersBounds: diff.any
17113 }, {
17114 name: 'source-endpoint',
17115 type: t.edgeEndpoint,
17116 triggersBounds: diff.any
17117 }, {
17118 name: 'target-endpoint',
17119 type: t.edgeEndpoint,
17120 triggersBounds: diff.any
17121 }, {
17122 name: 'control-point-step-size',
17123 type: t.size,
17124 triggersBounds: diff.any
17125 }, {
17126 name: 'control-point-distances',
17127 type: t.bidirectionalSizes,
17128 triggersBounds: diff.any
17129 }, {
17130 name: 'control-point-weights',
17131 type: t.numbers,
17132 triggersBounds: diff.any
17133 }, {
17134 name: 'segment-distances',
17135 type: t.bidirectionalSizes,
17136 triggersBounds: diff.any
17137 }, {
17138 name: 'segment-weights',
17139 type: t.numbers,
17140 triggersBounds: diff.any
17141 }, {
17142 name: 'segment-radii',
17143 type: t.numbers,
17144 triggersBounds: diff.any
17145 }, {
17146 name: 'radius-type',
17147 type: t.radiusType,
17148 triggersBounds: diff.any
17149 }, {
17150 name: 'taxi-turn',
17151 type: t.bidirectionalSizeMaybePercent,
17152 triggersBounds: diff.any
17153 }, {
17154 name: 'taxi-turn-min-distance',
17155 type: t.size,
17156 triggersBounds: diff.any
17157 }, {
17158 name: 'taxi-direction',
17159 type: t.axisDirection,
17160 triggersBounds: diff.any
17161 }, {
17162 name: 'taxi-radius',
17163 type: t.number,
17164 triggersBounds: diff.any
17165 }, {
17166 name: 'edge-distances',
17167 type: t.edgeDistances,
17168 triggersBounds: diff.any
17169 }, {
17170 name: 'arrow-scale',
17171 type: t.positiveNumber,
17172 triggersBounds: diff.any
17173 }, {
17174 name: 'loop-direction',
17175 type: t.angle,
17176 triggersBounds: diff.any
17177 }, {
17178 name: 'loop-sweep',
17179 type: t.angle,
17180 triggersBounds: diff.any
17181 }, {
17182 name: 'source-distance-from-node',
17183 type: t.size,
17184 triggersBounds: diff.any
17185 }, {
17186 name: 'target-distance-from-node',
17187 type: t.size,
17188 triggersBounds: diff.any
17189 }];
17190 var ghost = [{
17191 name: 'ghost',
17192 type: t.bool,
17193 triggersBounds: diff.any
17194 }, {
17195 name: 'ghost-offset-x',
17196 type: t.bidirectionalSize,
17197 triggersBounds: diff.any
17198 }, {
17199 name: 'ghost-offset-y',
17200 type: t.bidirectionalSize,
17201 triggersBounds: diff.any
17202 }, {
17203 name: 'ghost-opacity',
17204 type: t.zeroOneNumber
17205 }];
17206 var core = [{
17207 name: 'selection-box-color',
17208 type: t.color
17209 }, {
17210 name: 'selection-box-opacity',
17211 type: t.zeroOneNumber
17212 }, {
17213 name: 'selection-box-border-color',
17214 type: t.color
17215 }, {
17216 name: 'selection-box-border-width',
17217 type: t.size
17218 }, {
17219 name: 'active-bg-color',
17220 type: t.color
17221 }, {
17222 name: 'active-bg-opacity',
17223 type: t.zeroOneNumber
17224 }, {
17225 name: 'active-bg-size',
17226 type: t.size
17227 }, {
17228 name: 'outside-texture-bg-color',
17229 type: t.color
17230 }, {
17231 name: 'outside-texture-bg-opacity',
17232 type: t.zeroOneNumber
17233 }];
17234
17235 // pie backgrounds for nodes
17236 var pie = [];
17237 styfn$2.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
17238 pie.push({
17239 name: 'pie-size',
17240 type: t.sizeMaybePercent
17241 });
17242 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
17243 pie.push({
17244 name: 'pie-' + i + '-background-color',
17245 type: t.color
17246 });
17247 pie.push({
17248 name: 'pie-' + i + '-background-size',
17249 type: t.percent
17250 });
17251 pie.push({
17252 name: 'pie-' + i + '-background-opacity',
17253 type: t.zeroOneNumber
17254 });
17255 }
17256
17257 // edge arrows
17258 var edgeArrow = [];
17259 var arrowPrefixes = styfn$2.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
17260 [{
17261 name: 'arrow-shape',
17262 type: t.arrowShape,
17263 triggersBounds: diff.any
17264 }, {
17265 name: 'arrow-color',
17266 type: t.color
17267 }, {
17268 name: 'arrow-fill',
17269 type: t.arrowFill
17270 }, {
17271 name: 'arrow-width',
17272 type: t.arrowWidth
17273 }].forEach(function (prop) {
17274 arrowPrefixes.forEach(function (prefix) {
17275 var name = prefix + '-' + prop.name;
17276 var type = prop.type,
17277 triggersBounds = prop.triggersBounds;
17278 edgeArrow.push({
17279 name: name,
17280 type: type,
17281 triggersBounds: triggersBounds
17282 });
17283 });
17284 }, {});
17285 var props = styfn$2.properties = [].concat(behavior, transition, visibility, overlay, underlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, nodeOutline, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
17286 var propGroups = styfn$2.propertyGroups = {
17287 // common to all eles
17288 behavior: behavior,
17289 transition: transition,
17290 visibility: visibility,
17291 overlay: overlay,
17292 underlay: underlay,
17293 ghost: ghost,
17294 // labels
17295 commonLabel: commonLabel,
17296 labelDimensions: labelDimensions,
17297 mainLabel: mainLabel,
17298 sourceLabel: sourceLabel,
17299 targetLabel: targetLabel,
17300 // node props
17301 nodeBody: nodeBody,
17302 nodeBorder: nodeBorder,
17303 nodeOutline: nodeOutline,
17304 backgroundImage: backgroundImage,
17305 pie: pie,
17306 compound: compound,
17307 // edge props
17308 edgeLine: edgeLine,
17309 edgeArrow: edgeArrow,
17310 core: core
17311 };
17312 var propGroupNames = styfn$2.propertyGroupNames = {};
17313 var propGroupKeys = styfn$2.propertyGroupKeys = Object.keys(propGroups);
17314 propGroupKeys.forEach(function (key) {
17315 propGroupNames[key] = propGroups[key].map(function (prop) {
17316 return prop.name;
17317 });
17318 propGroups[key].forEach(function (prop) {
17319 return prop.groupKey = key;
17320 });
17321 });
17322
17323 // define aliases
17324 var aliases = styfn$2.aliases = [{
17325 name: 'content',
17326 pointsTo: 'label'
17327 }, {
17328 name: 'control-point-distance',
17329 pointsTo: 'control-point-distances'
17330 }, {
17331 name: 'control-point-weight',
17332 pointsTo: 'control-point-weights'
17333 }, {
17334 name: 'segment-distance',
17335 pointsTo: 'segment-distances'
17336 }, {
17337 name: 'segment-weight',
17338 pointsTo: 'segment-weights'
17339 }, {
17340 name: 'segment-radius',
17341 pointsTo: 'segment-radii'
17342 }, {
17343 name: 'edge-text-rotation',
17344 pointsTo: 'text-rotation'
17345 }, {
17346 name: 'padding-left',
17347 pointsTo: 'padding'
17348 }, {
17349 name: 'padding-right',
17350 pointsTo: 'padding'
17351 }, {
17352 name: 'padding-top',
17353 pointsTo: 'padding'
17354 }, {
17355 name: 'padding-bottom',
17356 pointsTo: 'padding'
17357 }];
17358
17359 // list of property names
17360 styfn$2.propertyNames = props.map(function (p) {
17361 return p.name;
17362 });
17363
17364 // allow access of properties by name ( e.g. style.properties.height )
17365 for (var _i = 0; _i < props.length; _i++) {
17366 var prop = props[_i];
17367 props[prop.name] = prop; // allow lookup by name
17368 }
17369
17370 // map aliases
17371 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17372 var alias = aliases[_i2];
17373 var pointsToProp = props[alias.pointsTo];
17374 var aliasProp = {
17375 name: alias.name,
17376 alias: true,
17377 pointsTo: pointsToProp
17378 };
17379
17380 // add alias prop for parsing
17381 props.push(aliasProp);
17382 props[alias.name] = aliasProp; // allow lookup by name
17383 }
17384})();
17385
17386styfn$2.getDefaultProperty = function (name) {
17387 return this.getDefaultProperties()[name];
17388};
17389styfn$2.getDefaultProperties = function () {
17390 var _p = this._private;
17391 if (_p.defaultProperties != null) {
17392 return _p.defaultProperties;
17393 }
17394 var rawProps = extend({
17395 // core props
17396 'selection-box-color': '#ddd',
17397 'selection-box-opacity': 0.65,
17398 'selection-box-border-color': '#aaa',
17399 'selection-box-border-width': 1,
17400 'active-bg-color': 'black',
17401 'active-bg-opacity': 0.15,
17402 'active-bg-size': 30,
17403 'outside-texture-bg-color': '#000',
17404 'outside-texture-bg-opacity': 0.125,
17405 // common node/edge props
17406 'events': 'yes',
17407 'text-events': 'no',
17408 'text-valign': 'top',
17409 'text-halign': 'center',
17410 'text-justification': 'auto',
17411 'line-height': 1,
17412 'color': '#000',
17413 'text-outline-color': '#000',
17414 'text-outline-width': 0,
17415 'text-outline-opacity': 1,
17416 'text-opacity': 1,
17417 'text-decoration': 'none',
17418 'text-transform': 'none',
17419 'text-wrap': 'none',
17420 'text-overflow-wrap': 'whitespace',
17421 'text-max-width': 9999,
17422 'text-background-color': '#000',
17423 'text-background-opacity': 0,
17424 'text-background-shape': 'rectangle',
17425 'text-background-padding': 0,
17426 'text-border-opacity': 0,
17427 'text-border-width': 0,
17428 'text-border-style': 'solid',
17429 'text-border-color': '#000',
17430 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17431 'font-style': 'normal',
17432 'font-weight': 'normal',
17433 'font-size': 16,
17434 'min-zoomed-font-size': 0,
17435 'text-rotation': 'none',
17436 'source-text-rotation': 'none',
17437 'target-text-rotation': 'none',
17438 'visibility': 'visible',
17439 'display': 'element',
17440 'opacity': 1,
17441 'z-compound-depth': 'auto',
17442 'z-index-compare': 'auto',
17443 'z-index': 0,
17444 'label': '',
17445 'text-margin-x': 0,
17446 'text-margin-y': 0,
17447 'source-label': '',
17448 'source-text-offset': 0,
17449 'source-text-margin-x': 0,
17450 'source-text-margin-y': 0,
17451 'target-label': '',
17452 'target-text-offset': 0,
17453 'target-text-margin-x': 0,
17454 'target-text-margin-y': 0,
17455 'overlay-opacity': 0,
17456 'overlay-color': '#000',
17457 'overlay-padding': 10,
17458 'overlay-shape': 'round-rectangle',
17459 'overlay-corner-radius': 'auto',
17460 'underlay-opacity': 0,
17461 'underlay-color': '#000',
17462 'underlay-padding': 10,
17463 'underlay-shape': 'round-rectangle',
17464 'underlay-corner-radius': 'auto',
17465 'transition-property': 'none',
17466 'transition-duration': 0,
17467 'transition-delay': 0,
17468 'transition-timing-function': 'linear',
17469 // node props
17470 'background-blacken': 0,
17471 'background-color': '#999',
17472 'background-fill': 'solid',
17473 'background-opacity': 1,
17474 'background-image': 'none',
17475 'background-image-crossorigin': 'anonymous',
17476 'background-image-opacity': 1,
17477 'background-image-containment': 'inside',
17478 'background-image-smoothing': 'yes',
17479 'background-position-x': '50%',
17480 'background-position-y': '50%',
17481 'background-offset-x': 0,
17482 'background-offset-y': 0,
17483 'background-width-relative-to': 'include-padding',
17484 'background-height-relative-to': 'include-padding',
17485 'background-repeat': 'no-repeat',
17486 'background-fit': 'none',
17487 'background-clip': 'node',
17488 'background-width': 'auto',
17489 'background-height': 'auto',
17490 'border-color': '#000',
17491 'border-opacity': 1,
17492 'border-width': 0,
17493 'border-style': 'solid',
17494 'border-dash-pattern': [4, 2],
17495 'border-dash-offset': 0,
17496 'border-cap': 'butt',
17497 'border-join': 'miter',
17498 'border-position': 'center',
17499 'outline-color': '#999',
17500 'outline-opacity': 1,
17501 'outline-width': 0,
17502 'outline-offset': 0,
17503 'outline-style': 'solid',
17504 'height': 30,
17505 'width': 30,
17506 'shape': 'ellipse',
17507 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17508 'corner-radius': 'auto',
17509 'bounds-expansion': 0,
17510 // node gradient
17511 'background-gradient-direction': 'to-bottom',
17512 'background-gradient-stop-colors': '#999',
17513 'background-gradient-stop-positions': '0%',
17514 // ghost props
17515 'ghost': 'no',
17516 'ghost-offset-y': 0,
17517 'ghost-offset-x': 0,
17518 'ghost-opacity': 0,
17519 // compound props
17520 'padding': 0,
17521 'padding-relative-to': 'width',
17522 'position': 'origin',
17523 'compound-sizing-wrt-labels': 'include',
17524 'min-width': 0,
17525 'min-width-bias-left': 0,
17526 'min-width-bias-right': 0,
17527 'min-height': 0,
17528 'min-height-bias-top': 0,
17529 'min-height-bias-bottom': 0
17530 }, {
17531 // node pie bg
17532 'pie-size': '100%'
17533 }, [{
17534 name: 'pie-{{i}}-background-color',
17535 value: 'black'
17536 }, {
17537 name: 'pie-{{i}}-background-size',
17538 value: '0%'
17539 }, {
17540 name: 'pie-{{i}}-background-opacity',
17541 value: 1
17542 }].reduce(function (css, prop) {
17543 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
17544 var name = prop.name.replace('{{i}}', i);
17545 var val = prop.value;
17546 css[name] = val;
17547 }
17548 return css;
17549 }, {}), {
17550 // edge props
17551 'line-style': 'solid',
17552 'line-color': '#999',
17553 'line-fill': 'solid',
17554 'line-cap': 'butt',
17555 'line-opacity': 1,
17556 'line-outline-width': 0,
17557 'line-outline-color': '#000',
17558 'line-gradient-stop-colors': '#999',
17559 'line-gradient-stop-positions': '0%',
17560 'control-point-step-size': 40,
17561 'control-point-weights': 0.5,
17562 'segment-weights': 0.5,
17563 'segment-distances': 20,
17564 'segment-radii': 15,
17565 'radius-type': 'arc-radius',
17566 'taxi-turn': '50%',
17567 'taxi-radius': 15,
17568 'taxi-turn-min-distance': 10,
17569 'taxi-direction': 'auto',
17570 'edge-distances': 'intersection',
17571 'curve-style': 'haystack',
17572 'haystack-radius': 0,
17573 'arrow-scale': 1,
17574 'loop-direction': '-45deg',
17575 'loop-sweep': '-90deg',
17576 'source-distance-from-node': 0,
17577 'target-distance-from-node': 0,
17578 'source-endpoint': 'outside-to-node',
17579 'target-endpoint': 'outside-to-node',
17580 'line-dash-pattern': [6, 3],
17581 'line-dash-offset': 0
17582 }, [{
17583 name: 'arrow-shape',
17584 value: 'none'
17585 }, {
17586 name: 'arrow-color',
17587 value: '#999'
17588 }, {
17589 name: 'arrow-fill',
17590 value: 'filled'
17591 }, {
17592 name: 'arrow-width',
17593 value: 1
17594 }].reduce(function (css, prop) {
17595 styfn$2.arrowPrefixes.forEach(function (prefix) {
17596 var name = prefix + '-' + prop.name;
17597 var val = prop.value;
17598 css[name] = val;
17599 });
17600 return css;
17601 }, {}));
17602 var parsedProps = {};
17603 for (var i = 0; i < this.properties.length; i++) {
17604 var prop = this.properties[i];
17605 if (prop.pointsTo) {
17606 continue;
17607 }
17608 var name = prop.name;
17609 var val = rawProps[name];
17610 var parsedProp = this.parse(name, val);
17611 parsedProps[name] = parsedProp;
17612 }
17613 _p.defaultProperties = parsedProps;
17614 return _p.defaultProperties;
17615};
17616styfn$2.addDefaultStylesheet = function () {
17617 this.selector(':parent').css({
17618 'shape': 'rectangle',
17619 'padding': 10,
17620 'background-color': '#eee',
17621 'border-color': '#ccc',
17622 'border-width': 1
17623 }).selector('edge').css({
17624 'width': 3
17625 }).selector(':loop').css({
17626 'curve-style': 'bezier'
17627 }).selector('edge:compound').css({
17628 'curve-style': 'bezier',
17629 'source-endpoint': 'outside-to-line',
17630 'target-endpoint': 'outside-to-line'
17631 }).selector(':selected').css({
17632 'background-color': '#0169D9',
17633 'line-color': '#0169D9',
17634 'source-arrow-color': '#0169D9',
17635 'target-arrow-color': '#0169D9',
17636 'mid-source-arrow-color': '#0169D9',
17637 'mid-target-arrow-color': '#0169D9'
17638 }).selector(':parent:selected').css({
17639 'background-color': '#CCE1F9',
17640 'border-color': '#aec8e5'
17641 }).selector(':active').css({
17642 'overlay-color': 'black',
17643 'overlay-padding': 10,
17644 'overlay-opacity': 0.25
17645 });
17646 this.defaultLength = this.length;
17647};
17648
17649var styfn$1 = {};
17650
17651// a caching layer for property parsing
17652styfn$1.parse = function (name, value, propIsBypass, propIsFlat) {
17653 var self = this;
17654
17655 // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17656 if (fn$6(value)) {
17657 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17658 }
17659 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17660 var bypassKey = propIsBypass ? 't' : 'f';
17661 var valueKey = '' + value;
17662 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17663 var propCache = self.propCache = self.propCache || [];
17664 var ret;
17665 if (!(ret = propCache[argHash])) {
17666 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17667 }
17668
17669 // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17670 // - mappings can't be shared b/c mappings are per-element
17671 if (propIsBypass || propIsFlat === 'mapping') {
17672 // need a copy since props are mutated later in their lifecycles
17673 ret = copy(ret);
17674 if (ret) {
17675 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17676 }
17677 }
17678
17679 return ret;
17680};
17681styfn$1.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17682 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17683 if (!prop && value != null) {
17684 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17685 }
17686 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17687 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17688 }
17689 return prop;
17690};
17691
17692// parse a property; return null on invalid; return parsed property otherwise
17693// fields :
17694// - name : the name of the property
17695// - value : the parsed, native-typed value of the property
17696// - strValue : a string value that represents the property value in valid css
17697// - bypass : true iff the property is a bypass property
17698styfn$1.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17699 var self = this;
17700 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17701
17702 var property = self.properties[name];
17703 var passedValue = value;
17704 var types = self.types;
17705 if (!property) {
17706 return null;
17707 } // return null on property of unknown name
17708 if (value === undefined) {
17709 return null;
17710 } // can't assign undefined
17711
17712 // the property may be an alias
17713 if (property.alias) {
17714 property = property.pointsTo;
17715 name = property.name;
17716 }
17717 var valueIsString = string(value);
17718 if (valueIsString) {
17719 // trim the value to make parsing easier
17720 value = value.trim();
17721 }
17722 var type = property.type;
17723 if (!type) {
17724 return null;
17725 } // no type, no luck
17726
17727 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17728 if (propIsBypass && (value === '' || value === null)) {
17729 return {
17730 name: name,
17731 value: value,
17732 bypass: true,
17733 deleteBypass: true
17734 };
17735 }
17736
17737 // check if value is a function used as a mapper
17738 if (fn$6(value)) {
17739 return {
17740 name: name,
17741 value: value,
17742 strValue: 'fn',
17743 mapped: types.fn,
17744 bypass: propIsBypass
17745 };
17746 }
17747
17748 // check if value is mapped
17749 var data, mapData;
17750 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))) {
17751 if (propIsBypass) {
17752 return false;
17753 } // mappers not allowed in bypass
17754
17755 var mapped = types.data;
17756 return {
17757 name: name,
17758 value: data,
17759 strValue: '' + value,
17760 mapped: mapped,
17761 field: data[1],
17762 bypass: propIsBypass
17763 };
17764 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17765 if (propIsBypass) {
17766 return false;
17767 } // mappers not allowed in bypass
17768 if (type.multiple) {
17769 return false;
17770 } // impossible to map to num
17771
17772 var _mapped = types.mapData;
17773
17774 // we can map only if the type is a colour or a number
17775 if (!(type.color || type.number)) {
17776 return false;
17777 }
17778 var valueMin = this.parse(name, mapData[4]); // parse to validate
17779 if (!valueMin || valueMin.mapped) {
17780 return false;
17781 } // can't be invalid or mapped
17782
17783 var valueMax = this.parse(name, mapData[5]); // parse to validate
17784 if (!valueMax || valueMax.mapped) {
17785 return false;
17786 } // can't be invalid or mapped
17787
17788 // check if valueMin and valueMax are the same
17789 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17790 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17791 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17792 } else if (type.color) {
17793 var c1 = valueMin.value;
17794 var c2 = valueMax.value;
17795 var same = c1[0] === c2[0] // red
17796 && c1[1] === c2[1] // green
17797 && c1[2] === c2[2] // blue
17798 && (
17799 // optional alpha
17800 c1[3] === c2[3] // same alpha outright
17801 || (c1[3] == null || c1[3] === 1 // full opacity for colour 1?
17802 ) && (c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17803 );
17804
17805 if (same) {
17806 return false;
17807 } // can't make a mapper without a range
17808 }
17809
17810 return {
17811 name: name,
17812 value: mapData,
17813 strValue: '' + value,
17814 mapped: _mapped,
17815 field: mapData[1],
17816 fieldMin: parseFloat(mapData[2]),
17817 // min & max are numeric
17818 fieldMax: parseFloat(mapData[3]),
17819 valueMin: valueMin.value,
17820 valueMax: valueMax.value,
17821 bypass: propIsBypass
17822 };
17823 }
17824 if (type.multiple && propIsFlat !== 'multiple') {
17825 var vals;
17826 if (valueIsString) {
17827 vals = value.split(/\s+/);
17828 } else if (array(value)) {
17829 vals = value;
17830 } else {
17831 vals = [value];
17832 }
17833 if (type.evenMultiple && vals.length % 2 !== 0) {
17834 return null;
17835 }
17836 var valArr = [];
17837 var unitsArr = [];
17838 var pfValArr = [];
17839 var strVal = '';
17840 var hasEnum = false;
17841 for (var i = 0; i < vals.length; i++) {
17842 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17843 hasEnum = hasEnum || string(p.value);
17844 valArr.push(p.value);
17845 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17846 unitsArr.push(p.units);
17847 strVal += (i > 0 ? ' ' : '') + p.strValue;
17848 }
17849 if (type.validate && !type.validate(valArr, unitsArr)) {
17850 return null;
17851 }
17852 if (type.singleEnum && hasEnum) {
17853 if (valArr.length === 1 && string(valArr[0])) {
17854 return {
17855 name: name,
17856 value: valArr[0],
17857 strValue: valArr[0],
17858 bypass: propIsBypass
17859 };
17860 } else {
17861 return null;
17862 }
17863 }
17864 return {
17865 name: name,
17866 value: valArr,
17867 pfValue: pfValArr,
17868 strValue: strVal,
17869 bypass: propIsBypass,
17870 units: unitsArr
17871 };
17872 }
17873
17874 // several types also allow enums
17875 var checkEnums = function checkEnums() {
17876 for (var _i = 0; _i < type.enums.length; _i++) {
17877 var en = type.enums[_i];
17878 if (en === value) {
17879 return {
17880 name: name,
17881 value: value,
17882 strValue: '' + value,
17883 bypass: propIsBypass
17884 };
17885 }
17886 }
17887 return null;
17888 };
17889
17890 // check the type and return the appropriate object
17891 if (type.number) {
17892 var units;
17893 var implicitUnits = 'px'; // not set => px
17894
17895 if (type.units) {
17896 // use specified units if set
17897 units = type.units;
17898 }
17899 if (type.implicitUnits) {
17900 implicitUnits = type.implicitUnits;
17901 }
17902 if (!type.unitless) {
17903 if (valueIsString) {
17904 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17905 if (units) {
17906 unitsRegex = units;
17907 } // only allow explicit units if so set
17908 var match = value.match('^(' + number + ')(' + unitsRegex + ')?' + '$');
17909 if (match) {
17910 value = match[1];
17911 units = match[2] || implicitUnits;
17912 }
17913 } else if (!units || type.implicitUnits) {
17914 units = implicitUnits; // implicitly px if unspecified
17915 }
17916 }
17917
17918 value = parseFloat(value);
17919
17920 // if not a number and enums not allowed, then the value is invalid
17921 if (isNaN(value) && type.enums === undefined) {
17922 return null;
17923 }
17924
17925 // check if this number type also accepts special keywords in place of numbers
17926 // (i.e. `left`, `auto`, etc)
17927 if (isNaN(value) && type.enums !== undefined) {
17928 value = passedValue;
17929 return checkEnums();
17930 }
17931
17932 // check if value must be an integer
17933 if (type.integer && !integer(value)) {
17934 return null;
17935 }
17936
17937 // check value is within range
17938 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17939 return null;
17940 }
17941 var ret = {
17942 name: name,
17943 value: value,
17944 strValue: '' + value + (units ? units : ''),
17945 units: units,
17946 bypass: propIsBypass
17947 };
17948
17949 // normalise value in pixels
17950 if (type.unitless || units !== 'px' && units !== 'em') {
17951 ret.pfValue = value;
17952 } else {
17953 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17954 }
17955
17956 // normalise value in ms
17957 if (units === 'ms' || units === 's') {
17958 ret.pfValue = units === 'ms' ? value : 1000 * value;
17959 }
17960
17961 // normalise value in rad
17962 if (units === 'deg' || units === 'rad') {
17963 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17964 }
17965
17966 // normalize value in %
17967 if (units === '%') {
17968 ret.pfValue = value / 100;
17969 }
17970 return ret;
17971 } else if (type.propList) {
17972 var props = [];
17973 var propsStr = '' + value;
17974 if (propsStr === 'none') ; else {
17975 // go over each prop
17976
17977 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17978 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17979 var propName = propsSplit[_i2].trim();
17980 if (self.properties[propName]) {
17981 props.push(propName);
17982 } else {
17983 warn('`' + propName + '` is not a valid property name');
17984 }
17985 }
17986 if (props.length === 0) {
17987 return null;
17988 }
17989 }
17990 return {
17991 name: name,
17992 value: props,
17993 strValue: props.length === 0 ? 'none' : props.join(' '),
17994 bypass: propIsBypass
17995 };
17996 } else if (type.color) {
17997 var tuple = color2tuple(value);
17998 if (!tuple) {
17999 return null;
18000 }
18001 return {
18002 name: name,
18003 value: tuple,
18004 pfValue: tuple,
18005 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
18006 // n.b. no spaces b/c of multiple support
18007 bypass: propIsBypass
18008 };
18009 } else if (type.regex || type.regexes) {
18010 // first check enums
18011 if (type.enums) {
18012 var enumProp = checkEnums();
18013 if (enumProp) {
18014 return enumProp;
18015 }
18016 }
18017 var regexes = type.regexes ? type.regexes : [type.regex];
18018 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
18019 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
18020 var m = regex.exec(value);
18021 if (m) {
18022 // regex matches
18023 return {
18024 name: name,
18025 value: type.singleRegexMatchValue ? m[1] : m,
18026 strValue: '' + value,
18027 bypass: propIsBypass
18028 };
18029 }
18030 }
18031 return null; // didn't match any
18032 } else if (type.string) {
18033 // just return
18034 return {
18035 name: name,
18036 value: '' + value,
18037 strValue: '' + value,
18038 bypass: propIsBypass
18039 };
18040 } else if (type.enums) {
18041 // check enums last because it's a combo type in others
18042 return checkEnums();
18043 } else {
18044 return null; // not a type we can handle
18045 }
18046};
18047
18048var Style = function Style(cy) {
18049 if (!(this instanceof Style)) {
18050 return new Style(cy);
18051 }
18052 if (!core(cy)) {
18053 error('A style must have a core reference');
18054 return;
18055 }
18056 this._private = {
18057 cy: cy,
18058 coreStyle: {}
18059 };
18060 this.length = 0;
18061 this.resetToDefault();
18062};
18063var styfn = Style.prototype;
18064styfn.instanceString = function () {
18065 return 'style';
18066};
18067
18068// remove all contexts
18069styfn.clear = function () {
18070 var _p = this._private;
18071 var cy = _p.cy;
18072 var eles = cy.elements();
18073 for (var i = 0; i < this.length; i++) {
18074 this[i] = undefined;
18075 }
18076 this.length = 0;
18077 _p.contextStyles = {};
18078 _p.propDiffs = {};
18079 this.cleanElements(eles, true);
18080 eles.forEach(function (ele) {
18081 var ele_p = ele[0]._private;
18082 ele_p.styleDirty = true;
18083 ele_p.appliedInitStyle = false;
18084 });
18085 return this; // chaining
18086};
18087
18088styfn.resetToDefault = function () {
18089 this.clear();
18090 this.addDefaultStylesheet();
18091 return this;
18092};
18093
18094// builds a style object for the 'core' selector
18095styfn.core = function (propName) {
18096 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
18097};
18098
18099// create a new context from the specified selector string and switch to that context
18100styfn.selector = function (selectorStr) {
18101 // 'core' is a special case and does not need a selector
18102 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
18103 var i = this.length++; // new context means new index
18104 this[i] = {
18105 selector: selector,
18106 properties: [],
18107 mappedProperties: [],
18108 index: i
18109 };
18110 return this; // chaining
18111};
18112
18113// add one or many css rules to the current context
18114styfn.css = function () {
18115 var self = this;
18116 var args = arguments;
18117 if (args.length === 1) {
18118 var map = args[0];
18119 for (var i = 0; i < self.properties.length; i++) {
18120 var prop = self.properties[i];
18121 var mapVal = map[prop.name];
18122 if (mapVal === undefined) {
18123 mapVal = map[dash2camel(prop.name)];
18124 }
18125 if (mapVal !== undefined) {
18126 this.cssRule(prop.name, mapVal);
18127 }
18128 }
18129 } else if (args.length === 2) {
18130 this.cssRule(args[0], args[1]);
18131 }
18132
18133 // do nothing if args are invalid
18134
18135 return this; // chaining
18136};
18137
18138styfn.style = styfn.css;
18139
18140// add a single css rule to the current context
18141styfn.cssRule = function (name, value) {
18142 // name-value pair
18143 var property = this.parse(name, value);
18144
18145 // add property to current context if valid
18146 if (property) {
18147 var i = this.length - 1;
18148 this[i].properties.push(property);
18149 this[i].properties[property.name] = property; // allow access by name as well
18150
18151 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
18152 this._private.hasPie = true;
18153 }
18154 if (property.mapped) {
18155 this[i].mappedProperties.push(property);
18156 }
18157
18158 // add to core style if necessary
18159 var currentSelectorIsCore = !this[i].selector;
18160 if (currentSelectorIsCore) {
18161 this._private.coreStyle[property.name] = property;
18162 }
18163 }
18164 return this; // chaining
18165};
18166
18167styfn.append = function (style) {
18168 if (stylesheet(style)) {
18169 style.appendToStyle(this);
18170 } else if (array(style)) {
18171 this.appendFromJson(style);
18172 } else if (string(style)) {
18173 this.appendFromString(style);
18174 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
18175
18176 return this;
18177};
18178
18179// static function
18180Style.fromJson = function (cy, json) {
18181 var style = new Style(cy);
18182 style.fromJson(json);
18183 return style;
18184};
18185Style.fromString = function (cy, string) {
18186 return new Style(cy).fromString(string);
18187};
18188[styfn$8, styfn$7, styfn$6, styfn$5, styfn$4, styfn$3, styfn$2, styfn$1].forEach(function (props) {
18189 extend(styfn, props);
18190});
18191Style.types = styfn.types;
18192Style.properties = styfn.properties;
18193Style.propertyGroups = styfn.propertyGroups;
18194Style.propertyGroupNames = styfn.propertyGroupNames;
18195Style.propertyGroupKeys = styfn.propertyGroupKeys;
18196
18197var corefn$2 = {
18198 style: function style(newStyle) {
18199 if (newStyle) {
18200 var s = this.setStyle(newStyle);
18201 s.update();
18202 }
18203 return this._private.style;
18204 },
18205 setStyle: function setStyle(style) {
18206 var _p = this._private;
18207 if (stylesheet(style)) {
18208 _p.style = style.generateStyle(this);
18209 } else if (array(style)) {
18210 _p.style = Style.fromJson(this, style);
18211 } else if (string(style)) {
18212 _p.style = Style.fromString(this, style);
18213 } else {
18214 _p.style = Style(this);
18215 }
18216 return _p.style;
18217 },
18218 // e.g. cy.data() changed => recalc ele mappers
18219 updateStyle: function updateStyle() {
18220 this.mutableElements().updateStyle(); // just send to all eles
18221 }
18222};
18223
18224var defaultSelectionType = 'single';
18225var corefn$1 = {
18226 autolock: function autolock(bool) {
18227 if (bool !== undefined) {
18228 this._private.autolock = bool ? true : false;
18229 } else {
18230 return this._private.autolock;
18231 }
18232 return this; // chaining
18233 },
18234
18235 autoungrabify: function autoungrabify(bool) {
18236 if (bool !== undefined) {
18237 this._private.autoungrabify = bool ? true : false;
18238 } else {
18239 return this._private.autoungrabify;
18240 }
18241 return this; // chaining
18242 },
18243
18244 autounselectify: function autounselectify(bool) {
18245 if (bool !== undefined) {
18246 this._private.autounselectify = bool ? true : false;
18247 } else {
18248 return this._private.autounselectify;
18249 }
18250 return this; // chaining
18251 },
18252
18253 selectionType: function selectionType(selType) {
18254 var _p = this._private;
18255 if (_p.selectionType == null) {
18256 _p.selectionType = defaultSelectionType;
18257 }
18258 if (selType !== undefined) {
18259 if (selType === 'additive' || selType === 'single') {
18260 _p.selectionType = selType;
18261 }
18262 } else {
18263 return _p.selectionType;
18264 }
18265 return this;
18266 },
18267 panningEnabled: function panningEnabled(bool) {
18268 if (bool !== undefined) {
18269 this._private.panningEnabled = bool ? true : false;
18270 } else {
18271 return this._private.panningEnabled;
18272 }
18273 return this; // chaining
18274 },
18275
18276 userPanningEnabled: function userPanningEnabled(bool) {
18277 if (bool !== undefined) {
18278 this._private.userPanningEnabled = bool ? true : false;
18279 } else {
18280 return this._private.userPanningEnabled;
18281 }
18282 return this; // chaining
18283 },
18284
18285 zoomingEnabled: function zoomingEnabled(bool) {
18286 if (bool !== undefined) {
18287 this._private.zoomingEnabled = bool ? true : false;
18288 } else {
18289 return this._private.zoomingEnabled;
18290 }
18291 return this; // chaining
18292 },
18293
18294 userZoomingEnabled: function userZoomingEnabled(bool) {
18295 if (bool !== undefined) {
18296 this._private.userZoomingEnabled = bool ? true : false;
18297 } else {
18298 return this._private.userZoomingEnabled;
18299 }
18300 return this; // chaining
18301 },
18302
18303 boxSelectionEnabled: function boxSelectionEnabled(bool) {
18304 if (bool !== undefined) {
18305 this._private.boxSelectionEnabled = bool ? true : false;
18306 } else {
18307 return this._private.boxSelectionEnabled;
18308 }
18309 return this; // chaining
18310 },
18311
18312 pan: function pan() {
18313 var args = arguments;
18314 var pan = this._private.pan;
18315 var dim, val, dims, x, y;
18316 switch (args.length) {
18317 case 0:
18318 // .pan()
18319 return pan;
18320 case 1:
18321 if (string(args[0])) {
18322 // .pan('x')
18323 dim = args[0];
18324 return pan[dim];
18325 } else if (plainObject(args[0])) {
18326 // .pan({ x: 0, y: 100 })
18327 if (!this._private.panningEnabled) {
18328 return this;
18329 }
18330 dims = args[0];
18331 x = dims.x;
18332 y = dims.y;
18333 if (number$1(x)) {
18334 pan.x = x;
18335 }
18336 if (number$1(y)) {
18337 pan.y = y;
18338 }
18339 this.emit('pan viewport');
18340 }
18341 break;
18342 case 2:
18343 // .pan('x', 100)
18344 if (!this._private.panningEnabled) {
18345 return this;
18346 }
18347 dim = args[0];
18348 val = args[1];
18349 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18350 pan[dim] = val;
18351 }
18352 this.emit('pan viewport');
18353 break;
18354 // invalid
18355 }
18356
18357 this.notify('viewport');
18358 return this; // chaining
18359 },
18360
18361 panBy: function panBy(arg0, arg1) {
18362 var args = arguments;
18363 var pan = this._private.pan;
18364 var dim, val, dims, x, y;
18365 if (!this._private.panningEnabled) {
18366 return this;
18367 }
18368 switch (args.length) {
18369 case 1:
18370 if (plainObject(arg0)) {
18371 // .panBy({ x: 0, y: 100 })
18372 dims = args[0];
18373 x = dims.x;
18374 y = dims.y;
18375 if (number$1(x)) {
18376 pan.x += x;
18377 }
18378 if (number$1(y)) {
18379 pan.y += y;
18380 }
18381 this.emit('pan viewport');
18382 }
18383 break;
18384 case 2:
18385 // .panBy('x', 100)
18386 dim = arg0;
18387 val = arg1;
18388 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18389 pan[dim] += val;
18390 }
18391 this.emit('pan viewport');
18392 break;
18393 // invalid
18394 }
18395
18396 this.notify('viewport');
18397 return this; // chaining
18398 },
18399
18400 fit: function fit(elements, padding) {
18401 var viewportState = this.getFitViewport(elements, padding);
18402 if (viewportState) {
18403 var _p = this._private;
18404 _p.zoom = viewportState.zoom;
18405 _p.pan = viewportState.pan;
18406 this.emit('pan zoom viewport');
18407 this.notify('viewport');
18408 }
18409 return this; // chaining
18410 },
18411
18412 getFitViewport: function getFitViewport(elements, padding) {
18413 if (number$1(elements) && padding === undefined) {
18414 // elements is optional
18415 padding = elements;
18416 elements = undefined;
18417 }
18418 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18419 return;
18420 }
18421 var bb;
18422 if (string(elements)) {
18423 var sel = elements;
18424 elements = this.$(sel);
18425 } else if (boundingBox(elements)) {
18426 // assume bb
18427 var bbe = elements;
18428 bb = {
18429 x1: bbe.x1,
18430 y1: bbe.y1,
18431 x2: bbe.x2,
18432 y2: bbe.y2
18433 };
18434 bb.w = bb.x2 - bb.x1;
18435 bb.h = bb.y2 - bb.y1;
18436 } else if (!elementOrCollection(elements)) {
18437 elements = this.mutableElements();
18438 }
18439 if (elementOrCollection(elements) && elements.empty()) {
18440 return;
18441 } // can't fit to nothing
18442
18443 bb = bb || elements.boundingBox();
18444 var w = this.width();
18445 var h = this.height();
18446 var zoom;
18447 padding = number$1(padding) ? padding : 0;
18448 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18449 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h);
18450
18451 // crop zoom
18452 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18453 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18454 var pan = {
18455 // now pan to middle
18456 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18457 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18458 };
18459 return {
18460 zoom: zoom,
18461 pan: pan
18462 };
18463 }
18464 return;
18465 },
18466 zoomRange: function zoomRange(min, max) {
18467 var _p = this._private;
18468 if (max == null) {
18469 var opts = min;
18470 min = opts.min;
18471 max = opts.max;
18472 }
18473 if (number$1(min) && number$1(max) && min <= max) {
18474 _p.minZoom = min;
18475 _p.maxZoom = max;
18476 } else if (number$1(min) && max === undefined && min <= _p.maxZoom) {
18477 _p.minZoom = min;
18478 } else if (number$1(max) && min === undefined && max >= _p.minZoom) {
18479 _p.maxZoom = max;
18480 }
18481 return this;
18482 },
18483 minZoom: function minZoom(zoom) {
18484 if (zoom === undefined) {
18485 return this._private.minZoom;
18486 } else {
18487 return this.zoomRange({
18488 min: zoom
18489 });
18490 }
18491 },
18492 maxZoom: function maxZoom(zoom) {
18493 if (zoom === undefined) {
18494 return this._private.maxZoom;
18495 } else {
18496 return this.zoomRange({
18497 max: zoom
18498 });
18499 }
18500 },
18501 getZoomedViewport: function getZoomedViewport(params) {
18502 var _p = this._private;
18503 var currentPan = _p.pan;
18504 var currentZoom = _p.zoom;
18505 var pos; // in rendered px
18506 var zoom;
18507 var bail = false;
18508 if (!_p.zoomingEnabled) {
18509 // zooming disabled
18510 bail = true;
18511 }
18512 if (number$1(params)) {
18513 // then set the zoom
18514 zoom = params;
18515 } else if (plainObject(params)) {
18516 // then zoom about a point
18517 zoom = params.level;
18518 if (params.position != null) {
18519 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18520 } else if (params.renderedPosition != null) {
18521 pos = params.renderedPosition;
18522 }
18523 if (pos != null && !_p.panningEnabled) {
18524 // panning disabled
18525 bail = true;
18526 }
18527 }
18528
18529 // crop zoom
18530 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18531 zoom = zoom < _p.minZoom ? _p.minZoom : zoom;
18532
18533 // can't zoom with invalid params
18534 if (bail || !number$1(zoom) || zoom === currentZoom || pos != null && (!number$1(pos.x) || !number$1(pos.y))) {
18535 return null;
18536 }
18537 if (pos != null) {
18538 // set zoom about position
18539 var pan1 = currentPan;
18540 var zoom1 = currentZoom;
18541 var zoom2 = zoom;
18542 var pan2 = {
18543 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18544 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18545 };
18546 return {
18547 zoomed: true,
18548 panned: true,
18549 zoom: zoom2,
18550 pan: pan2
18551 };
18552 } else {
18553 // just set the zoom
18554 return {
18555 zoomed: true,
18556 panned: false,
18557 zoom: zoom,
18558 pan: currentPan
18559 };
18560 }
18561 },
18562 zoom: function zoom(params) {
18563 if (params === undefined) {
18564 // get
18565 return this._private.zoom;
18566 } else {
18567 // set
18568 var vp = this.getZoomedViewport(params);
18569 var _p = this._private;
18570 if (vp == null || !vp.zoomed) {
18571 return this;
18572 }
18573 _p.zoom = vp.zoom;
18574 if (vp.panned) {
18575 _p.pan.x = vp.pan.x;
18576 _p.pan.y = vp.pan.y;
18577 }
18578 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18579 this.notify('viewport');
18580 return this; // chaining
18581 }
18582 },
18583
18584 viewport: function viewport(opts) {
18585 var _p = this._private;
18586 var zoomDefd = true;
18587 var panDefd = true;
18588 var events = []; // to trigger
18589 var zoomFailed = false;
18590 var panFailed = false;
18591 if (!opts) {
18592 return this;
18593 }
18594 if (!number$1(opts.zoom)) {
18595 zoomDefd = false;
18596 }
18597 if (!plainObject(opts.pan)) {
18598 panDefd = false;
18599 }
18600 if (!zoomDefd && !panDefd) {
18601 return this;
18602 }
18603 if (zoomDefd) {
18604 var z = opts.zoom;
18605 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18606 zoomFailed = true;
18607 } else {
18608 _p.zoom = z;
18609 events.push('zoom');
18610 }
18611 }
18612 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18613 var p = opts.pan;
18614 if (number$1(p.x)) {
18615 _p.pan.x = p.x;
18616 panFailed = false;
18617 }
18618 if (number$1(p.y)) {
18619 _p.pan.y = p.y;
18620 panFailed = false;
18621 }
18622 if (!panFailed) {
18623 events.push('pan');
18624 }
18625 }
18626 if (events.length > 0) {
18627 events.push('viewport');
18628 this.emit(events.join(' '));
18629 this.notify('viewport');
18630 }
18631 return this; // chaining
18632 },
18633
18634 center: function center(elements) {
18635 var pan = this.getCenterPan(elements);
18636 if (pan) {
18637 this._private.pan = pan;
18638 this.emit('pan viewport');
18639 this.notify('viewport');
18640 }
18641 return this; // chaining
18642 },
18643
18644 getCenterPan: function getCenterPan(elements, zoom) {
18645 if (!this._private.panningEnabled) {
18646 return;
18647 }
18648 if (string(elements)) {
18649 var selector = elements;
18650 elements = this.mutableElements().filter(selector);
18651 } else if (!elementOrCollection(elements)) {
18652 elements = this.mutableElements();
18653 }
18654 if (elements.length === 0) {
18655 return;
18656 } // can't centre pan to nothing
18657
18658 var bb = elements.boundingBox();
18659 var w = this.width();
18660 var h = this.height();
18661 zoom = zoom === undefined ? this._private.zoom : zoom;
18662 var pan = {
18663 // middle
18664 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18665 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18666 };
18667 return pan;
18668 },
18669 reset: function reset() {
18670 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18671 return this;
18672 }
18673 this.viewport({
18674 pan: {
18675 x: 0,
18676 y: 0
18677 },
18678 zoom: 1
18679 });
18680 return this; // chaining
18681 },
18682
18683 invalidateSize: function invalidateSize() {
18684 this._private.sizeCache = null;
18685 },
18686 size: function size() {
18687 var _p = this._private;
18688 var container = _p.container;
18689 var cy = this;
18690 return _p.sizeCache = _p.sizeCache || (container ? function () {
18691 var style = cy.window().getComputedStyle(container);
18692 var val = function val(name) {
18693 return parseFloat(style.getPropertyValue(name));
18694 };
18695 return {
18696 width: container.clientWidth - val('padding-left') - val('padding-right'),
18697 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18698 };
18699 }() : {
18700 // fallback if no container (not 0 b/c can be used for dividing etc)
18701 width: 1,
18702 height: 1
18703 });
18704 },
18705 width: function width() {
18706 return this.size().width;
18707 },
18708 height: function height() {
18709 return this.size().height;
18710 },
18711 extent: function extent() {
18712 var pan = this._private.pan;
18713 var zoom = this._private.zoom;
18714 var rb = this.renderedExtent();
18715 var b = {
18716 x1: (rb.x1 - pan.x) / zoom,
18717 x2: (rb.x2 - pan.x) / zoom,
18718 y1: (rb.y1 - pan.y) / zoom,
18719 y2: (rb.y2 - pan.y) / zoom
18720 };
18721 b.w = b.x2 - b.x1;
18722 b.h = b.y2 - b.y1;
18723 return b;
18724 },
18725 renderedExtent: function renderedExtent() {
18726 var width = this.width();
18727 var height = this.height();
18728 return {
18729 x1: 0,
18730 y1: 0,
18731 x2: width,
18732 y2: height,
18733 w: width,
18734 h: height
18735 };
18736 },
18737 multiClickDebounceTime: function multiClickDebounceTime(_int) {
18738 if (_int) this._private.multiClickDebounceTime = _int;else return this._private.multiClickDebounceTime;
18739 return this; // chaining
18740 }
18741};
18742
18743// aliases
18744corefn$1.centre = corefn$1.center;
18745
18746// backwards compatibility
18747corefn$1.autolockNodes = corefn$1.autolock;
18748corefn$1.autoungrabifyNodes = corefn$1.autoungrabify;
18749
18750var fn = {
18751 data: define.data({
18752 field: 'data',
18753 bindingEvent: 'data',
18754 allowBinding: true,
18755 allowSetting: true,
18756 settingEvent: 'data',
18757 settingTriggersEvent: true,
18758 triggerFnName: 'trigger',
18759 allowGetting: true,
18760 updateStyle: true
18761 }),
18762 removeData: define.removeData({
18763 field: 'data',
18764 event: 'data',
18765 triggerFnName: 'trigger',
18766 triggerEvent: true,
18767 updateStyle: true
18768 }),
18769 scratch: define.data({
18770 field: 'scratch',
18771 bindingEvent: 'scratch',
18772 allowBinding: true,
18773 allowSetting: true,
18774 settingEvent: 'scratch',
18775 settingTriggersEvent: true,
18776 triggerFnName: 'trigger',
18777 allowGetting: true,
18778 updateStyle: true
18779 }),
18780 removeScratch: define.removeData({
18781 field: 'scratch',
18782 event: 'scratch',
18783 triggerFnName: 'trigger',
18784 triggerEvent: true,
18785 updateStyle: true
18786 })
18787};
18788
18789// aliases
18790fn.attr = fn.data;
18791fn.removeAttr = fn.removeData;
18792
18793var Core = function Core(opts) {
18794 var cy = this;
18795 opts = extend({}, opts);
18796 var container = opts.container;
18797
18798 // allow for passing a wrapped jquery object
18799 // e.g. cytoscape({ container: $('#cy') })
18800 if (container && !htmlElement(container) && htmlElement(container[0])) {
18801 container = container[0];
18802 }
18803 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18804 reg = reg || {};
18805 if (reg && reg.cy) {
18806 reg.cy.destroy();
18807 reg = {}; // old instance => replace reg completely
18808 }
18809
18810 var readies = reg.readies = reg.readies || [];
18811 if (container) {
18812 container._cyreg = reg;
18813 } // make sure container assoc'd reg points to this cy
18814 reg.cy = cy;
18815 var head = _window !== undefined && container !== undefined && !opts.headless;
18816 var options = opts;
18817 options.layout = extend({
18818 name: head ? 'grid' : 'null'
18819 }, options.layout);
18820 options.renderer = extend({
18821 name: head ? 'canvas' : 'null'
18822 }, options.renderer);
18823 var defVal = function defVal(def, val, altVal) {
18824 if (val !== undefined) {
18825 return val;
18826 } else if (altVal !== undefined) {
18827 return altVal;
18828 } else {
18829 return def;
18830 }
18831 };
18832 var _p = this._private = {
18833 container: container,
18834 // html dom ele container
18835 ready: false,
18836 // whether ready has been triggered
18837 options: options,
18838 // cached options
18839 elements: new Collection(this),
18840 // elements in the graph
18841 listeners: [],
18842 // list of listeners
18843 aniEles: new Collection(this),
18844 // elements being animated
18845 data: options.data || {},
18846 // data for the core
18847 scratch: {},
18848 // scratch object for core
18849 layout: null,
18850 renderer: null,
18851 destroyed: false,
18852 // whether destroy was called
18853 notificationsEnabled: true,
18854 // whether notifications are sent to the renderer
18855 minZoom: 1e-50,
18856 maxZoom: 1e50,
18857 zoomingEnabled: defVal(true, options.zoomingEnabled),
18858 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18859 panningEnabled: defVal(true, options.panningEnabled),
18860 userPanningEnabled: defVal(true, options.userPanningEnabled),
18861 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18862 autolock: defVal(false, options.autolock, options.autolockNodes),
18863 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18864 autounselectify: defVal(false, options.autounselectify),
18865 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18866 zoom: number$1(options.zoom) ? options.zoom : 1,
18867 pan: {
18868 x: plainObject(options.pan) && number$1(options.pan.x) ? options.pan.x : 0,
18869 y: plainObject(options.pan) && number$1(options.pan.y) ? options.pan.y : 0
18870 },
18871 animation: {
18872 // object for currently-running animations
18873 current: [],
18874 queue: []
18875 },
18876 hasCompoundNodes: false,
18877 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
18878 };
18879 this.createEmitter();
18880
18881 // set selection type
18882 this.selectionType(options.selectionType);
18883
18884 // init zoom bounds
18885 this.zoomRange({
18886 min: options.minZoom,
18887 max: options.maxZoom
18888 });
18889 var loadExtData = function loadExtData(extData, next) {
18890 var anyIsPromise = extData.some(promise);
18891 if (anyIsPromise) {
18892 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18893 } else {
18894 next(extData); // exec synchronously for convenience
18895 }
18896 };
18897
18898 // start with the default stylesheet so we have something before loading an external stylesheet
18899 if (_p.styleEnabled) {
18900 cy.setStyle([]);
18901 }
18902
18903 // create the renderer
18904 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18905 cy.initRenderer(rendererOptions);
18906 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18907 cy.notifications(false);
18908
18909 // remove old elements
18910 var oldEles = cy.mutableElements();
18911 if (oldEles.length > 0) {
18912 oldEles.remove();
18913 }
18914 if (elements != null) {
18915 if (plainObject(elements) || array(elements)) {
18916 cy.add(elements);
18917 }
18918 }
18919 cy.one('layoutready', function (e) {
18920 cy.notifications(true);
18921 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18922
18923 cy.one('load', onload);
18924 cy.emitAndNotify('load');
18925 }).one('layoutstop', function () {
18926 cy.one('done', ondone);
18927 cy.emit('done');
18928 });
18929 var layoutOpts = extend({}, cy._private.options.layout);
18930 layoutOpts.eles = cy.elements();
18931 cy.layout(layoutOpts).run();
18932 };
18933 loadExtData([options.style, options.elements], function (thens) {
18934 var initStyle = thens[0];
18935 var initEles = thens[1];
18936
18937 // init style
18938 if (_p.styleEnabled) {
18939 cy.style().append(initStyle);
18940 }
18941
18942 // initial load
18943 setElesAndLayout(initEles, function () {
18944 // onready
18945 cy.startAnimationLoop();
18946 _p.ready = true;
18947
18948 // if a ready callback is specified as an option, the bind it
18949 if (fn$6(options.ready)) {
18950 cy.on('ready', options.ready);
18951 }
18952
18953 // bind all the ready handlers registered before creating this instance
18954 for (var i = 0; i < readies.length; i++) {
18955 var fn = readies[i];
18956 cy.on('ready', fn);
18957 }
18958 if (reg) {
18959 reg.readies = [];
18960 } // 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
18961
18962 cy.emit('ready');
18963 }, options.done);
18964 });
18965};
18966var corefn = Core.prototype; // short alias
18967
18968extend(corefn, {
18969 instanceString: function instanceString() {
18970 return 'core';
18971 },
18972 isReady: function isReady() {
18973 return this._private.ready;
18974 },
18975 destroyed: function destroyed() {
18976 return this._private.destroyed;
18977 },
18978 ready: function ready(fn) {
18979 if (this.isReady()) {
18980 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18981 } else {
18982 this.on('ready', fn);
18983 }
18984 return this;
18985 },
18986 destroy: function destroy() {
18987 var cy = this;
18988 if (cy.destroyed()) return;
18989 cy.stopAnimationLoop();
18990 cy.destroyRenderer();
18991 this.emit('destroy');
18992 cy._private.destroyed = true;
18993 return cy;
18994 },
18995 hasElementWithId: function hasElementWithId(id) {
18996 return this._private.elements.hasElementWithId(id);
18997 },
18998 getElementById: function getElementById(id) {
18999 return this._private.elements.getElementById(id);
19000 },
19001 hasCompoundNodes: function hasCompoundNodes() {
19002 return this._private.hasCompoundNodes;
19003 },
19004 headless: function headless() {
19005 return this._private.renderer.isHeadless();
19006 },
19007 styleEnabled: function styleEnabled() {
19008 return this._private.styleEnabled;
19009 },
19010 addToPool: function addToPool(eles) {
19011 this._private.elements.merge(eles);
19012 return this; // chaining
19013 },
19014
19015 removeFromPool: function removeFromPool(eles) {
19016 this._private.elements.unmerge(eles);
19017 return this;
19018 },
19019 container: function container() {
19020 return this._private.container || null;
19021 },
19022 window: function window() {
19023 var container = this._private.container;
19024 if (container == null) return _window;
19025 var ownerDocument = this._private.container.ownerDocument;
19026 if (ownerDocument === undefined || ownerDocument == null) {
19027 return _window;
19028 }
19029 return ownerDocument.defaultView || _window;
19030 },
19031 mount: function mount(container) {
19032 if (container == null) {
19033 return;
19034 }
19035 var cy = this;
19036 var _p = cy._private;
19037 var options = _p.options;
19038 if (!htmlElement(container) && htmlElement(container[0])) {
19039 container = container[0];
19040 }
19041 cy.stopAnimationLoop();
19042 cy.destroyRenderer();
19043 _p.container = container;
19044 _p.styleEnabled = true;
19045 cy.invalidateSize();
19046 cy.initRenderer(extend({}, options, options.renderer, {
19047 // allow custom renderer name to be re-used, otherwise use canvas
19048 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
19049 }));
19050 cy.startAnimationLoop();
19051 cy.style(options.style);
19052 cy.emit('mount');
19053 return cy;
19054 },
19055 unmount: function unmount() {
19056 var cy = this;
19057 cy.stopAnimationLoop();
19058 cy.destroyRenderer();
19059 cy.initRenderer({
19060 name: 'null'
19061 });
19062 cy.emit('unmount');
19063 return cy;
19064 },
19065 options: function options() {
19066 return copy(this._private.options);
19067 },
19068 json: function json(obj) {
19069 var cy = this;
19070 var _p = cy._private;
19071 var eles = cy.mutableElements();
19072 var getFreshRef = function getFreshRef(ele) {
19073 return cy.getElementById(ele.id());
19074 };
19075 if (plainObject(obj)) {
19076 // set
19077
19078 cy.startBatch();
19079 if (obj.elements) {
19080 var idInJson = {};
19081 var updateEles = function updateEles(jsons, gr) {
19082 var toAdd = [];
19083 var toMod = [];
19084 for (var i = 0; i < jsons.length; i++) {
19085 var json = jsons[i];
19086 if (!json.data.id) {
19087 warn('cy.json() cannot handle elements without an ID attribute');
19088 continue;
19089 }
19090 var id = '' + json.data.id; // id must be string
19091 var ele = cy.getElementById(id);
19092 idInJson[id] = true;
19093 if (ele.length !== 0) {
19094 // existing element should be updated
19095 toMod.push({
19096 ele: ele,
19097 json: json
19098 });
19099 } else {
19100 // otherwise should be added
19101 if (gr) {
19102 json.group = gr;
19103 toAdd.push(json);
19104 } else {
19105 toAdd.push(json);
19106 }
19107 }
19108 }
19109 cy.add(toAdd);
19110 for (var _i = 0; _i < toMod.length; _i++) {
19111 var _toMod$_i = toMod[_i],
19112 _ele = _toMod$_i.ele,
19113 _json = _toMod$_i.json;
19114 _ele.json(_json);
19115 }
19116 };
19117 if (array(obj.elements)) {
19118 // elements: []
19119 updateEles(obj.elements);
19120 } else {
19121 // elements: { nodes: [], edges: [] }
19122 var grs = ['nodes', 'edges'];
19123 for (var i = 0; i < grs.length; i++) {
19124 var gr = grs[i];
19125 var elements = obj.elements[gr];
19126 if (array(elements)) {
19127 updateEles(elements, gr);
19128 }
19129 }
19130 }
19131 var parentsToRemove = cy.collection();
19132 eles.filter(function (ele) {
19133 return !idInJson[ele.id()];
19134 }).forEach(function (ele) {
19135 if (ele.isParent()) {
19136 parentsToRemove.merge(ele);
19137 } else {
19138 ele.remove();
19139 }
19140 });
19141
19142 // so that children are not removed w/parent
19143 parentsToRemove.forEach(function (ele) {
19144 return ele.children().move({
19145 parent: null
19146 });
19147 });
19148
19149 // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
19150 parentsToRemove.forEach(function (ele) {
19151 return getFreshRef(ele).remove();
19152 });
19153 }
19154 if (obj.style) {
19155 cy.style(obj.style);
19156 }
19157 if (obj.zoom != null && obj.zoom !== _p.zoom) {
19158 cy.zoom(obj.zoom);
19159 }
19160 if (obj.pan) {
19161 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
19162 cy.pan(obj.pan);
19163 }
19164 }
19165 if (obj.data) {
19166 cy.data(obj.data);
19167 }
19168 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify', 'multiClickDebounceTime'];
19169 for (var _i2 = 0; _i2 < fields.length; _i2++) {
19170 var f = fields[_i2];
19171 if (obj[f] != null) {
19172 cy[f](obj[f]);
19173 }
19174 }
19175 cy.endBatch();
19176 return this; // chaining
19177 } else {
19178 // get
19179 var flat = !!obj;
19180 var json = {};
19181 if (flat) {
19182 json.elements = this.elements().map(function (ele) {
19183 return ele.json();
19184 });
19185 } else {
19186 json.elements = {};
19187 eles.forEach(function (ele) {
19188 var group = ele.group();
19189 if (!json.elements[group]) {
19190 json.elements[group] = [];
19191 }
19192 json.elements[group].push(ele.json());
19193 });
19194 }
19195 if (this._private.styleEnabled) {
19196 json.style = cy.style().json();
19197 }
19198 json.data = copy(cy.data());
19199 var options = _p.options;
19200 json.zoomingEnabled = _p.zoomingEnabled;
19201 json.userZoomingEnabled = _p.userZoomingEnabled;
19202 json.zoom = _p.zoom;
19203 json.minZoom = _p.minZoom;
19204 json.maxZoom = _p.maxZoom;
19205 json.panningEnabled = _p.panningEnabled;
19206 json.userPanningEnabled = _p.userPanningEnabled;
19207 json.pan = copy(_p.pan);
19208 json.boxSelectionEnabled = _p.boxSelectionEnabled;
19209 json.renderer = copy(options.renderer);
19210 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
19211 json.textureOnViewport = options.textureOnViewport;
19212 json.wheelSensitivity = options.wheelSensitivity;
19213 json.motionBlur = options.motionBlur;
19214 json.multiClickDebounceTime = options.multiClickDebounceTime;
19215 return json;
19216 }
19217 }
19218});
19219corefn.$id = corefn.getElementById;
19220[corefn$9, corefn$8, elesfn, corefn$7, corefn$6, corefn$5, corefn$4, corefn$3, corefn$2, corefn$1, fn].forEach(function (props) {
19221 extend(corefn, props);
19222});
19223
19224/* eslint-disable no-unused-vars */
19225var defaults$7 = {
19226 fit: true,
19227 // whether to fit the viewport to the graph
19228 directed: false,
19229 // whether the tree is directed downwards (or edges can point in any direction if false)
19230 padding: 30,
19231 // padding on fit
19232 circle: false,
19233 // put depths in concentric circles if true, put depths top down if false
19234 grid: false,
19235 // whether to create an even grid into which the DAG is placed (circle:false only)
19236 spacingFactor: 1.75,
19237 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19238 boundingBox: undefined,
19239 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19240 avoidOverlap: true,
19241 // prevents node overlap, may overflow boundingBox if not enough space
19242 nodeDimensionsIncludeLabels: false,
19243 // Excludes the label when calculating node bounding boxes for the layout algorithm
19244 roots: undefined,
19245 // the roots of the trees
19246 depthSort: undefined,
19247 // a sorting function to order nodes at equal depth. e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19248 animate: false,
19249 // whether to transition the node positions
19250 animationDuration: 500,
19251 // duration of animation in ms if enabled
19252 animationEasing: undefined,
19253 // easing of animation if enabled,
19254 animateFilter: function animateFilter(node, i) {
19255 return true;
19256 },
19257 // 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
19258 ready: undefined,
19259 // callback on layoutready
19260 stop: undefined,
19261 // callback on layoutstop
19262 transform: function transform(node, position) {
19263 return position;
19264 } // transform a given node position. Useful for changing flow direction in discrete layouts
19265};
19266
19267var deprecatedOptionDefaults = {
19268 maximal: false,
19269 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only); setting acyclic to true sets maximal to true also
19270 acyclic: false // whether the tree is acyclic and thus a node could be shifted (due to the maximal option) multiple times without causing an infinite loop; setting to true sets maximal to true also; if you are uncertain whether a tree is acyclic, set to false to avoid potential infinite loops
19271};
19272
19273/* eslint-enable */
19274
19275var getInfo = function getInfo(ele) {
19276 return ele.scratch('breadthfirst');
19277};
19278var setInfo = function setInfo(ele, obj) {
19279 return ele.scratch('breadthfirst', obj);
19280};
19281function BreadthFirstLayout(options) {
19282 this.options = extend({}, defaults$7, deprecatedOptionDefaults, options);
19283}
19284BreadthFirstLayout.prototype.run = function () {
19285 var params = this.options;
19286 var options = params;
19287 var cy = params.cy;
19288 var eles = options.eles;
19289 var nodes = eles.nodes().filter(function (n) {
19290 return !n.isParent();
19291 });
19292 var graph = eles;
19293 var directed = options.directed;
19294 var maximal = options.acyclic || options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code; also, setting acyclic to true sets maximal to true
19295
19296 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19297 x1: 0,
19298 y1: 0,
19299 w: cy.width(),
19300 h: cy.height()
19301 });
19302 var roots;
19303 if (elementOrCollection(options.roots)) {
19304 roots = options.roots;
19305 } else if (array(options.roots)) {
19306 var rootsArray = [];
19307 for (var i = 0; i < options.roots.length; i++) {
19308 var id = options.roots[i];
19309 var ele = cy.getElementById(id);
19310 rootsArray.push(ele);
19311 }
19312 roots = cy.collection(rootsArray);
19313 } else if (string(options.roots)) {
19314 roots = cy.$(options.roots);
19315 } else {
19316 if (directed) {
19317 roots = nodes.roots();
19318 } else {
19319 var components = eles.components();
19320 roots = cy.collection();
19321 var _loop = function _loop(_i) {
19322 var comp = components[_i];
19323 var maxDegree = comp.maxDegree(false);
19324 var compRoots = comp.filter(function (ele) {
19325 return ele.degree(false) === maxDegree;
19326 });
19327 roots = roots.add(compRoots);
19328 };
19329 for (var _i = 0; _i < components.length; _i++) {
19330 _loop(_i);
19331 }
19332 }
19333 }
19334 var depths = [];
19335 var foundByBfs = {};
19336 var addToDepth = function addToDepth(ele, d) {
19337 if (depths[d] == null) {
19338 depths[d] = [];
19339 }
19340 var i = depths[d].length;
19341 depths[d].push(ele);
19342 setInfo(ele, {
19343 index: i,
19344 depth: d
19345 });
19346 };
19347 var changeDepth = function changeDepth(ele, newDepth) {
19348 var _getInfo = getInfo(ele),
19349 depth = _getInfo.depth,
19350 index = _getInfo.index;
19351 depths[depth][index] = null;
19352 addToDepth(ele, newDepth);
19353 };
19354
19355 // find the depths of the nodes
19356 graph.bfs({
19357 roots: roots,
19358 directed: options.directed,
19359 visit: function visit(node, edge, pNode, i, depth) {
19360 var ele = node[0];
19361 var id = ele.id();
19362 addToDepth(ele, depth);
19363 foundByBfs[id] = true;
19364 }
19365 });
19366
19367 // check for nodes not found by bfs
19368 var orphanNodes = [];
19369 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19370 var _ele = nodes[_i2];
19371 if (foundByBfs[_ele.id()]) {
19372 continue;
19373 } else {
19374 orphanNodes.push(_ele);
19375 }
19376 }
19377
19378 // assign the nodes a depth and index
19379
19380 var assignDepthsAt = function assignDepthsAt(i) {
19381 var eles = depths[i];
19382 for (var j = 0; j < eles.length; j++) {
19383 var _ele2 = eles[j];
19384 if (_ele2 == null) {
19385 eles.splice(j, 1);
19386 j--;
19387 continue;
19388 }
19389 setInfo(_ele2, {
19390 depth: i,
19391 index: j
19392 });
19393 }
19394 };
19395 var assignDepths = function assignDepths() {
19396 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19397 assignDepthsAt(_i3);
19398 }
19399 };
19400 var adjustMaximally = function adjustMaximally(ele, shifted) {
19401 var eInfo = getInfo(ele);
19402 var incomers = ele.incomers().filter(function (el) {
19403 return el.isNode() && eles.has(el);
19404 });
19405 var maxDepth = -1;
19406 var id = ele.id();
19407 for (var k = 0; k < incomers.length; k++) {
19408 var incmr = incomers[k];
19409 var iInfo = getInfo(incmr);
19410 maxDepth = Math.max(maxDepth, iInfo.depth);
19411 }
19412 if (eInfo.depth <= maxDepth) {
19413 if (!options.acyclic && shifted[id]) {
19414 return null;
19415 }
19416 var newDepth = maxDepth + 1;
19417 changeDepth(ele, newDepth);
19418 shifted[id] = newDepth;
19419 return true;
19420 }
19421 return false;
19422 };
19423
19424 // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19425 if (directed && maximal) {
19426 var Q = [];
19427 var shifted = {};
19428 var enqueue = function enqueue(n) {
19429 return Q.push(n);
19430 };
19431 var dequeue = function dequeue() {
19432 return Q.shift();
19433 };
19434 nodes.forEach(function (n) {
19435 return Q.push(n);
19436 });
19437 while (Q.length > 0) {
19438 var _ele3 = dequeue();
19439 var didShift = adjustMaximally(_ele3, shifted);
19440 if (didShift) {
19441 _ele3.outgoers().filter(function (el) {
19442 return el.isNode() && eles.has(el);
19443 }).forEach(enqueue);
19444 } else if (didShift === null) {
19445 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19446 break; // exit on failure
19447 }
19448 }
19449 }
19450
19451 assignDepths(); // clear holes
19452
19453 // find min distance we need to leave between nodes
19454 var minDistance = 0;
19455 if (options.avoidOverlap) {
19456 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19457 var n = nodes[_i4];
19458 var nbb = n.layoutDimensions(options);
19459 var w = nbb.w;
19460 var h = nbb.h;
19461 minDistance = Math.max(minDistance, w, h);
19462 }
19463 }
19464
19465 // get the weighted percent for an element based on its connectivity to other levels
19466 var cachedWeightedPercent = {};
19467 var getWeightedPercent = function getWeightedPercent(ele) {
19468 if (cachedWeightedPercent[ele.id()]) {
19469 return cachedWeightedPercent[ele.id()];
19470 }
19471 var eleDepth = getInfo(ele).depth;
19472 var neighbors = ele.neighborhood();
19473 var percent = 0;
19474 var samples = 0;
19475 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19476 var neighbor = neighbors[_i5];
19477 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19478 continue;
19479 }
19480 var bf = getInfo(neighbor);
19481 if (bf == null) {
19482 continue;
19483 }
19484 var index = bf.index;
19485 var depth = bf.depth;
19486
19487 // unassigned neighbours shouldn't affect the ordering
19488 if (index == null || depth == null) {
19489 continue;
19490 }
19491 var nDepth = depths[depth].length;
19492 if (depth < eleDepth) {
19493 // only get influenced by elements above
19494 percent += index / nDepth;
19495 samples++;
19496 }
19497 }
19498 samples = Math.max(1, samples);
19499 percent = percent / samples;
19500 if (samples === 0) {
19501 // put lone nodes at the start
19502 percent = 0;
19503 }
19504 cachedWeightedPercent[ele.id()] = percent;
19505 return percent;
19506 };
19507
19508 // rearrange the indices in each depth level based on connectivity
19509
19510 var sortFn = function sortFn(a, b) {
19511 var apct = getWeightedPercent(a);
19512 var bpct = getWeightedPercent(b);
19513 var diff = apct - bpct;
19514 if (diff === 0) {
19515 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19516 } else {
19517 return diff;
19518 }
19519 };
19520 if (options.depthSort !== undefined) {
19521 sortFn = options.depthSort;
19522 }
19523
19524 // sort each level to make connected nodes closer
19525 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19526 depths[_i6].sort(sortFn);
19527 assignDepthsAt(_i6);
19528 }
19529
19530 // assign orphan nodes to a new top-level depth
19531 var orphanDepth = [];
19532 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19533 orphanDepth.push(orphanNodes[_i7]);
19534 }
19535 depths.unshift(orphanDepth);
19536 assignDepths();
19537 var biggestDepthSize = 0;
19538 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19539 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19540 }
19541 var center = {
19542 x: bb.x1 + bb.w / 2,
19543 y: bb.x1 + bb.h / 2
19544 };
19545 var maxDepthSize = depths.reduce(function (max, eles) {
19546 return Math.max(max, eles.length);
19547 }, 0);
19548 var getPosition = function getPosition(ele) {
19549 var _getInfo2 = getInfo(ele),
19550 depth = _getInfo2.depth,
19551 index = _getInfo2.index;
19552 var depthSize = depths[depth].length;
19553 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19554 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19555 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19556 radiusStepSize = Math.max(radiusStepSize, minDistance);
19557 if (!options.circle) {
19558 var epos = {
19559 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19560 y: (depth + 1) * distanceY
19561 };
19562 return epos;
19563 } else {
19564 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19565 var theta = 2 * Math.PI / depths[depth].length * index;
19566 if (depth === 0 && depths[0].length === 1) {
19567 radius = 1;
19568 }
19569 return {
19570 x: center.x + radius * Math.cos(theta),
19571 y: center.y + radius * Math.sin(theta)
19572 };
19573 }
19574 };
19575 eles.nodes().layoutPositions(this, options, getPosition);
19576 return this; // chaining
19577};
19578
19579var defaults$6 = {
19580 fit: true,
19581 // whether to fit the viewport to the graph
19582 padding: 30,
19583 // the padding on fit
19584 boundingBox: undefined,
19585 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19586 avoidOverlap: true,
19587 // prevents node overlap, may overflow boundingBox and radius if not enough space
19588 nodeDimensionsIncludeLabels: false,
19589 // Excludes the label when calculating node bounding boxes for the layout algorithm
19590 spacingFactor: undefined,
19591 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19592 radius: undefined,
19593 // the radius of the circle
19594 startAngle: 3 / 2 * Math.PI,
19595 // where nodes start in radians
19596 sweep: undefined,
19597 // how many radians should be between the first and last node (defaults to full circle)
19598 clockwise: true,
19599 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19600 sort: undefined,
19601 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19602 animate: false,
19603 // whether to transition the node positions
19604 animationDuration: 500,
19605 // duration of animation in ms if enabled
19606 animationEasing: undefined,
19607 // easing of animation if enabled
19608 animateFilter: function animateFilter(node, i) {
19609 return true;
19610 },
19611 // 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
19612 ready: undefined,
19613 // callback on layoutready
19614 stop: undefined,
19615 // callback on layoutstop
19616 transform: function transform(node, position) {
19617 return position;
19618 } // transform a given node position. Useful for changing flow direction in discrete layouts
19619};
19620
19621function CircleLayout(options) {
19622 this.options = extend({}, defaults$6, options);
19623}
19624CircleLayout.prototype.run = function () {
19625 var params = this.options;
19626 var options = params;
19627 var cy = params.cy;
19628 var eles = options.eles;
19629 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19630 var nodes = eles.nodes().not(':parent');
19631 if (options.sort) {
19632 nodes = nodes.sort(options.sort);
19633 }
19634 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19635 x1: 0,
19636 y1: 0,
19637 w: cy.width(),
19638 h: cy.height()
19639 });
19640 var center = {
19641 x: bb.x1 + bb.w / 2,
19642 y: bb.y1 + bb.h / 2
19643 };
19644 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19645 var dTheta = sweep / Math.max(1, nodes.length - 1);
19646 var r;
19647 var minDistance = 0;
19648 for (var i = 0; i < nodes.length; i++) {
19649 var n = nodes[i];
19650 var nbb = n.layoutDimensions(options);
19651 var w = nbb.w;
19652 var h = nbb.h;
19653 minDistance = Math.max(minDistance, w, h);
19654 }
19655 if (number$1(options.radius)) {
19656 r = options.radius;
19657 } else if (nodes.length <= 1) {
19658 r = 0;
19659 } else {
19660 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19661 }
19662
19663 // calculate the radius
19664 if (nodes.length > 1 && options.avoidOverlap) {
19665 // but only if more than one node (can't overlap)
19666 minDistance *= 1.75; // just to have some nice spacing
19667
19668 var dcos = Math.cos(dTheta) - Math.cos(0);
19669 var dsin = Math.sin(dTheta) - Math.sin(0);
19670 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19671 r = Math.max(rMin, r);
19672 }
19673 var getPos = function getPos(ele, i) {
19674 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19675 var rx = r * Math.cos(theta);
19676 var ry = r * Math.sin(theta);
19677 var pos = {
19678 x: center.x + rx,
19679 y: center.y + ry
19680 };
19681 return pos;
19682 };
19683 eles.nodes().layoutPositions(this, options, getPos);
19684 return this; // chaining
19685};
19686
19687var defaults$5 = {
19688 fit: true,
19689 // whether to fit the viewport to the graph
19690 padding: 30,
19691 // the padding on fit
19692 startAngle: 3 / 2 * Math.PI,
19693 // where nodes start in radians
19694 sweep: undefined,
19695 // how many radians should be between the first and last node (defaults to full circle)
19696 clockwise: true,
19697 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19698 equidistant: false,
19699 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19700 minNodeSpacing: 10,
19701 // min spacing between outside of nodes (used for radius adjustment)
19702 boundingBox: undefined,
19703 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19704 avoidOverlap: true,
19705 // prevents node overlap, may overflow boundingBox if not enough space
19706 nodeDimensionsIncludeLabels: false,
19707 // Excludes the label when calculating node bounding boxes for the layout algorithm
19708 height: undefined,
19709 // height of layout area (overrides container height)
19710 width: undefined,
19711 // width of layout area (overrides container width)
19712 spacingFactor: undefined,
19713 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19714 concentric: function concentric(node) {
19715 // returns numeric value for each node, placing higher nodes in levels towards the centre
19716 return node.degree();
19717 },
19718 levelWidth: function levelWidth(nodes) {
19719 // the variation of concentric values in each level
19720 return nodes.maxDegree() / 4;
19721 },
19722 animate: false,
19723 // whether to transition the node positions
19724 animationDuration: 500,
19725 // duration of animation in ms if enabled
19726 animationEasing: undefined,
19727 // easing of animation if enabled
19728 animateFilter: function animateFilter(node, i) {
19729 return true;
19730 },
19731 // 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
19732 ready: undefined,
19733 // callback on layoutready
19734 stop: undefined,
19735 // callback on layoutstop
19736 transform: function transform(node, position) {
19737 return position;
19738 } // transform a given node position. Useful for changing flow direction in discrete layouts
19739};
19740
19741function ConcentricLayout(options) {
19742 this.options = extend({}, defaults$5, options);
19743}
19744ConcentricLayout.prototype.run = function () {
19745 var params = this.options;
19746 var options = params;
19747 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19748 var cy = params.cy;
19749 var eles = options.eles;
19750 var nodes = eles.nodes().not(':parent');
19751 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19752 x1: 0,
19753 y1: 0,
19754 w: cy.width(),
19755 h: cy.height()
19756 });
19757 var center = {
19758 x: bb.x1 + bb.w / 2,
19759 y: bb.y1 + bb.h / 2
19760 };
19761 var nodeValues = []; // { node, value }
19762 var maxNodeSize = 0;
19763 for (var i = 0; i < nodes.length; i++) {
19764 var node = nodes[i];
19765 var value = void 0;
19766
19767 // calculate the node value
19768 value = options.concentric(node);
19769 nodeValues.push({
19770 value: value,
19771 node: node
19772 });
19773
19774 // for style mapping
19775 node._private.scratch.concentric = value;
19776 }
19777
19778 // in case we used the `concentric` in style
19779 nodes.updateStyle();
19780
19781 // calculate max size now based on potentially updated mappers
19782 for (var _i = 0; _i < nodes.length; _i++) {
19783 var _node = nodes[_i];
19784 var nbb = _node.layoutDimensions(options);
19785 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19786 }
19787
19788 // sort node values in descreasing order
19789 nodeValues.sort(function (a, b) {
19790 return b.value - a.value;
19791 });
19792 var levelWidth = options.levelWidth(nodes);
19793
19794 // put the values into levels
19795 var levels = [[]];
19796 var currentLevel = levels[0];
19797 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19798 var val = nodeValues[_i2];
19799 if (currentLevel.length > 0) {
19800 var diff = Math.abs(currentLevel[0].value - val.value);
19801 if (diff >= levelWidth) {
19802 currentLevel = [];
19803 levels.push(currentLevel);
19804 }
19805 }
19806 currentLevel.push(val);
19807 }
19808
19809 // create positions from levels
19810
19811 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19812
19813 if (!options.avoidOverlap) {
19814 // then strictly constrain to bb
19815 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19816 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19817 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19818 minDist = Math.min(minDist, rStep);
19819 }
19820
19821 // find the metrics for each level
19822 var r = 0;
19823 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19824 var level = levels[_i3];
19825 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19826 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1);
19827
19828 // calculate the radius
19829 if (level.length > 1 && options.avoidOverlap) {
19830 // but only if more than one node (can't overlap)
19831 var dcos = Math.cos(dTheta) - Math.cos(0);
19832 var dsin = Math.sin(dTheta) - Math.sin(0);
19833 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19834
19835 r = Math.max(rMin, r);
19836 }
19837 level.r = r;
19838 r += minDist;
19839 }
19840 if (options.equidistant) {
19841 var rDeltaMax = 0;
19842 var _r = 0;
19843 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19844 var _level = levels[_i4];
19845 var rDelta = _level.r - _r;
19846 rDeltaMax = Math.max(rDeltaMax, rDelta);
19847 }
19848 _r = 0;
19849 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19850 var _level2 = levels[_i5];
19851 if (_i5 === 0) {
19852 _r = _level2.r;
19853 }
19854 _level2.r = _r;
19855 _r += rDeltaMax;
19856 }
19857 }
19858
19859 // calculate the node positions
19860 var pos = {}; // id => position
19861 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19862 var _level3 = levels[_i6];
19863 var _dTheta = _level3.dTheta;
19864 var _r2 = _level3.r;
19865 for (var j = 0; j < _level3.length; j++) {
19866 var _val = _level3[j];
19867 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19868 var p = {
19869 x: center.x + _r2 * Math.cos(theta),
19870 y: center.y + _r2 * Math.sin(theta)
19871 };
19872 pos[_val.node.id()] = p;
19873 }
19874 }
19875
19876 // position the nodes
19877 eles.nodes().layoutPositions(this, options, function (ele) {
19878 var id = ele.id();
19879 return pos[id];
19880 });
19881 return this; // chaining
19882};
19883
19884/*
19885The CoSE layout was written by Gerardo Huck.
19886https://www.linkedin.com/in/gerardohuck/
19887
19888Based on the following article:
19889http://dl.acm.org/citation.cfm?id=1498047
19890
19891Modifications tracked on Github.
19892*/
19893var DEBUG;
19894
19895/**
19896 * @brief : default layout options
19897 */
19898var defaults$4 = {
19899 // Called on `layoutready`
19900 ready: function ready() {},
19901 // Called on `layoutstop`
19902 stop: function stop() {},
19903 // Whether to animate while running the layout
19904 // true : Animate continuously as the layout is running
19905 // false : Just show the end result
19906 // 'end' : Animate with the end result, from the initial positions to the end positions
19907 animate: true,
19908 // Easing of the animation for animate:'end'
19909 animationEasing: undefined,
19910 // The duration of the animation for animate:'end'
19911 animationDuration: undefined,
19912 // A function that determines whether the node should be animated
19913 // All nodes animated by default on animate enabled
19914 // Non-animated nodes are positioned immediately when the layout starts
19915 animateFilter: function animateFilter(node, i) {
19916 return true;
19917 },
19918 // The layout animates only after this many milliseconds for animate:true
19919 // (prevents flashing on fast runs)
19920 animationThreshold: 250,
19921 // Number of iterations between consecutive screen positions update
19922 refresh: 20,
19923 // Whether to fit the network view after when done
19924 fit: true,
19925 // Padding on fit
19926 padding: 30,
19927 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19928 boundingBox: undefined,
19929 // Excludes the label when calculating node bounding boxes for the layout algorithm
19930 nodeDimensionsIncludeLabels: false,
19931 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19932 randomize: false,
19933 // Extra spacing between components in non-compound graphs
19934 componentSpacing: 40,
19935 // Node repulsion (non overlapping) multiplier
19936 nodeRepulsion: function nodeRepulsion(node) {
19937 return 2048;
19938 },
19939 // Node repulsion (overlapping) multiplier
19940 nodeOverlap: 4,
19941 // Ideal edge (non nested) length
19942 idealEdgeLength: function idealEdgeLength(edge) {
19943 return 32;
19944 },
19945 // Divisor to compute edge forces
19946 edgeElasticity: function edgeElasticity(edge) {
19947 return 32;
19948 },
19949 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19950 nestingFactor: 1.2,
19951 // Gravity force (constant)
19952 gravity: 1,
19953 // Maximum number of iterations to perform
19954 numIter: 1000,
19955 // Initial temperature (maximum node displacement)
19956 initialTemp: 1000,
19957 // Cooling factor (how the temperature is reduced between consecutive iterations
19958 coolingFactor: 0.99,
19959 // Lower temperature threshold (below this point the layout will end)
19960 minTemp: 1.0
19961};
19962
19963/**
19964 * @brief : constructor
19965 * @arg options : object containing layout options
19966 */
19967function CoseLayout(options) {
19968 this.options = extend({}, defaults$4, options);
19969 this.options.layout = this;
19970
19971 // Exclude any edge that has a source or target node that is not in the set of passed-in nodes
19972 var nodes = this.options.eles.nodes();
19973 var edges = this.options.eles.edges();
19974 var notEdges = edges.filter(function (e) {
19975 var sourceId = e.source().data('id');
19976 var targetId = e.target().data('id');
19977 var hasSource = nodes.some(function (n) {
19978 return n.data('id') === sourceId;
19979 });
19980 var hasTarget = nodes.some(function (n) {
19981 return n.data('id') === targetId;
19982 });
19983 return !hasSource || !hasTarget;
19984 });
19985 this.options.eles = this.options.eles.not(notEdges);
19986}
19987
19988/**
19989 * @brief : runs the layout
19990 */
19991CoseLayout.prototype.run = function () {
19992 var options = this.options;
19993 var cy = options.cy;
19994 var layout = this;
19995 layout.stopped = false;
19996 if (options.animate === true || options.animate === false) {
19997 layout.emit({
19998 type: 'layoutstart',
19999 layout: layout
20000 });
20001 }
20002
20003 // Set DEBUG - Global variable
20004 if (true === options.debug) {
20005 DEBUG = true;
20006 } else {
20007 DEBUG = false;
20008 }
20009
20010 // Initialize layout info
20011 var layoutInfo = createLayoutInfo(cy, layout, options);
20012
20013 // Show LayoutInfo contents if debugging
20014 if (DEBUG) {
20015 printLayoutInfo(layoutInfo);
20016 }
20017
20018 // If required, randomize node positions
20019 if (options.randomize) {
20020 randomizePositions(layoutInfo);
20021 }
20022 var startTime = performanceNow();
20023 var refresh = function refresh() {
20024 refreshPositions(layoutInfo, cy, options);
20025
20026 // Fit the graph if necessary
20027 if (true === options.fit) {
20028 cy.fit(options.padding);
20029 }
20030 };
20031 var mainLoop = function mainLoop(i) {
20032 if (layout.stopped || i >= options.numIter) {
20033 // logDebug("Layout manually stopped. Stopping computation in step " + i);
20034 return false;
20035 }
20036
20037 // Do one step in the phisical simulation
20038 step(layoutInfo, options);
20039
20040 // Update temperature
20041 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor;
20042 // logDebug("New temperature: " + layoutInfo.temperature);
20043
20044 if (layoutInfo.temperature < options.minTemp) {
20045 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
20046 return false;
20047 }
20048 return true;
20049 };
20050 var done = function done() {
20051 if (options.animate === true || options.animate === false) {
20052 refresh();
20053
20054 // Layout has finished
20055 layout.one('layoutstop', options.stop);
20056 layout.emit({
20057 type: 'layoutstop',
20058 layout: layout
20059 });
20060 } else {
20061 var nodes = options.eles.nodes();
20062 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20063 nodes.layoutPositions(layout, options, getScaledPos);
20064 }
20065 };
20066 var i = 0;
20067 var loopRet = true;
20068 if (options.animate === true) {
20069 var frame = function frame() {
20070 var f = 0;
20071 while (loopRet && f < options.refresh) {
20072 loopRet = mainLoop(i);
20073 i++;
20074 f++;
20075 }
20076 if (!loopRet) {
20077 // it's done
20078 separateComponents(layoutInfo, options);
20079 done();
20080 } else {
20081 var now = performanceNow();
20082 if (now - startTime >= options.animationThreshold) {
20083 refresh();
20084 }
20085 requestAnimationFrame(frame);
20086 }
20087 };
20088 frame();
20089 } else {
20090 while (loopRet) {
20091 loopRet = mainLoop(i);
20092 i++;
20093 }
20094 separateComponents(layoutInfo, options);
20095 done();
20096 }
20097 return this; // chaining
20098};
20099
20100/**
20101 * @brief : called on continuous layouts to stop them before they finish
20102 */
20103CoseLayout.prototype.stop = function () {
20104 this.stopped = true;
20105 if (this.thread) {
20106 this.thread.stop();
20107 }
20108 this.emit('layoutstop');
20109 return this; // chaining
20110};
20111
20112CoseLayout.prototype.destroy = function () {
20113 if (this.thread) {
20114 this.thread.stop();
20115 }
20116 return this; // chaining
20117};
20118
20119/**
20120 * @brief : Creates an object which is contains all the data
20121 * used in the layout process
20122 * @arg cy : cytoscape.js object
20123 * @return : layoutInfo object initialized
20124 */
20125var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
20126 // Shortcut
20127 var edges = options.eles.edges();
20128 var nodes = options.eles.nodes();
20129 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20130 x1: 0,
20131 y1: 0,
20132 w: cy.width(),
20133 h: cy.height()
20134 });
20135 var layoutInfo = {
20136 isCompound: cy.hasCompoundNodes(),
20137 layoutNodes: [],
20138 idToIndex: {},
20139 nodeSize: nodes.size(),
20140 graphSet: [],
20141 indexToGraph: [],
20142 layoutEdges: [],
20143 edgeSize: edges.size(),
20144 temperature: options.initialTemp,
20145 clientWidth: bb.w,
20146 clientHeight: bb.h,
20147 boundingBox: bb
20148 };
20149 var components = options.eles.components();
20150 var id2cmptId = {};
20151 for (var i = 0; i < components.length; i++) {
20152 var component = components[i];
20153 for (var j = 0; j < component.length; j++) {
20154 var node = component[j];
20155 id2cmptId[node.id()] = i;
20156 }
20157 }
20158
20159 // Iterate over all nodes, creating layout nodes
20160 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20161 var n = nodes[i];
20162 var nbb = n.layoutDimensions(options);
20163 var tempNode = {};
20164 tempNode.isLocked = n.locked();
20165 tempNode.id = n.data('id');
20166 tempNode.parentId = n.data('parent');
20167 tempNode.cmptId = id2cmptId[n.id()];
20168 tempNode.children = [];
20169 tempNode.positionX = n.position('x');
20170 tempNode.positionY = n.position('y');
20171 tempNode.offsetX = 0;
20172 tempNode.offsetY = 0;
20173 tempNode.height = nbb.w;
20174 tempNode.width = nbb.h;
20175 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20176 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20177 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20178 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20179 tempNode.padLeft = parseFloat(n.style('padding'));
20180 tempNode.padRight = parseFloat(n.style('padding'));
20181 tempNode.padTop = parseFloat(n.style('padding'));
20182 tempNode.padBottom = parseFloat(n.style('padding'));
20183
20184 // forces
20185 tempNode.nodeRepulsion = fn$6(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion;
20186
20187 // Add new node
20188 layoutInfo.layoutNodes.push(tempNode);
20189 // Add entry to id-index map
20190 layoutInfo.idToIndex[tempNode.id] = i;
20191 }
20192
20193 // Inline implementation of a queue, used for traversing the graph in BFS order
20194 var queue = [];
20195 var start = 0; // Points to the start the queue
20196 var end = -1; // Points to the end of the queue
20197
20198 var tempGraph = [];
20199
20200 // Second pass to add child information and
20201 // initialize queue for hierarchical traversal
20202 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20203 var n = layoutInfo.layoutNodes[i];
20204 var p_id = n.parentId;
20205 // Check if node n has a parent node
20206 if (null != p_id) {
20207 // Add node Id to parent's list of children
20208 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20209 } else {
20210 // If a node doesn't have a parent, then it's in the root graph
20211 queue[++end] = n.id;
20212 tempGraph.push(n.id);
20213 }
20214 }
20215
20216 // Add root graph to graphSet
20217 layoutInfo.graphSet.push(tempGraph);
20218
20219 // Traverse the graph, level by level,
20220 while (start <= end) {
20221 // Get the node to visit and remove it from queue
20222 var node_id = queue[start++];
20223 var node_ix = layoutInfo.idToIndex[node_id];
20224 var node = layoutInfo.layoutNodes[node_ix];
20225 var children = node.children;
20226 if (children.length > 0) {
20227 // Add children nodes as a new graph to graph set
20228 layoutInfo.graphSet.push(children);
20229 // Add children to que queue to be visited
20230 for (var i = 0; i < children.length; i++) {
20231 queue[++end] = children[i];
20232 }
20233 }
20234 }
20235
20236 // Create indexToGraph map
20237 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20238 var graph = layoutInfo.graphSet[i];
20239 for (var j = 0; j < graph.length; j++) {
20240 var index = layoutInfo.idToIndex[graph[j]];
20241 layoutInfo.indexToGraph[index] = i;
20242 }
20243 }
20244
20245 // Iterate over all edges, creating Layout Edges
20246 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20247 var e = edges[i];
20248 var tempEdge = {};
20249 tempEdge.id = e.data('id');
20250 tempEdge.sourceId = e.data('source');
20251 tempEdge.targetId = e.data('target');
20252
20253 // Compute ideal length
20254 var idealLength = fn$6(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20255 var elasticity = fn$6(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity;
20256
20257 // Check if it's an inter graph edge
20258 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20259 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20260 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20261 var targetGraph = layoutInfo.indexToGraph[targetIx];
20262 if (sourceGraph != targetGraph) {
20263 // Find lowest common graph ancestor
20264 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo);
20265
20266 // Compute sum of node depths, relative to lca graph
20267 var lcaGraph = layoutInfo.graphSet[lca];
20268 var depth = 0;
20269
20270 // Source depth
20271 var tempNode = layoutInfo.layoutNodes[sourceIx];
20272 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20273 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20274 depth++;
20275 }
20276
20277 // Target depth
20278 tempNode = layoutInfo.layoutNodes[targetIx];
20279 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20280 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20281 depth++;
20282 }
20283
20284 // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20285 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20286 // ". Depth: " + depth);
20287
20288 // Update idealLength
20289 idealLength *= depth * options.nestingFactor;
20290 }
20291 tempEdge.idealLength = idealLength;
20292 tempEdge.elasticity = elasticity;
20293 layoutInfo.layoutEdges.push(tempEdge);
20294 }
20295
20296 // Finally, return layoutInfo object
20297 return layoutInfo;
20298};
20299
20300/**
20301 * @brief : This function finds the index of the lowest common
20302 * graph ancestor between 2 nodes in the subtree
20303 * (from the graph hierarchy induced tree) whose
20304 * root is graphIx
20305 *
20306 * @arg node1: node1's ID
20307 * @arg node2: node2's ID
20308 * @arg layoutInfo: layoutInfo object
20309 *
20310 */
20311var findLCA = function findLCA(node1, node2, layoutInfo) {
20312 // Find their common ancester, starting from the root graph
20313 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20314 if (2 > res.count) {
20315 // If aux function couldn't find the common ancester,
20316 // then it is the root graph
20317 return 0;
20318 } else {
20319 return res.graph;
20320 }
20321};
20322
20323/**
20324 * @brief : Auxiliary function used for LCA computation
20325 *
20326 * @arg node1 : node1's ID
20327 * @arg node2 : node2's ID
20328 * @arg graphIx : subgraph index
20329 * @arg layoutInfo : layoutInfo object
20330 *
20331 * @return : object of the form {count: X, graph: Y}, where:
20332 * X is the number of ancestors (max: 2) found in
20333 * graphIx (and it's subgraphs),
20334 * Y is the graph index of the lowest graph containing
20335 * all X nodes
20336 */
20337var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20338 var graph = layoutInfo.graphSet[graphIx];
20339 // If both nodes belongs to graphIx
20340 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20341 return {
20342 count: 2,
20343 graph: graphIx
20344 };
20345 }
20346
20347 // Make recursive calls for all subgraphs
20348 var c = 0;
20349 for (var i = 0; i < graph.length; i++) {
20350 var nodeId = graph[i];
20351 var nodeIx = layoutInfo.idToIndex[nodeId];
20352 var children = layoutInfo.layoutNodes[nodeIx].children;
20353
20354 // If the node has no child, skip it
20355 if (0 === children.length) {
20356 continue;
20357 }
20358 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20359 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20360 if (0 === result.count) {
20361 // Neither node1 nor node2 are present in this subgraph
20362 continue;
20363 } else if (1 === result.count) {
20364 // One of (node1, node2) is present in this subgraph
20365 c++;
20366 if (2 === c) {
20367 // We've already found both nodes, no need to keep searching
20368 break;
20369 }
20370 } else {
20371 // Both nodes are present in this subgraph
20372 return result;
20373 }
20374 }
20375 return {
20376 count: c,
20377 graph: graphIx
20378 };
20379};
20380
20381/**
20382 * @brief: printsLayoutInfo into js console
20383 * Only used for debbuging
20384 */
20385var printLayoutInfo;
20386
20387/**
20388 * @brief : Randomizes the position of all nodes
20389 */
20390var randomizePositions = function randomizePositions(layoutInfo, cy) {
20391 var width = layoutInfo.clientWidth;
20392 var height = layoutInfo.clientHeight;
20393 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20394 var n = layoutInfo.layoutNodes[i];
20395
20396 // No need to randomize compound nodes or locked nodes
20397 if (0 === n.children.length && !n.isLocked) {
20398 n.positionX = Math.random() * width;
20399 n.positionY = Math.random() * height;
20400 }
20401 }
20402};
20403var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20404 var bb = layoutInfo.boundingBox;
20405 var coseBB = {
20406 x1: Infinity,
20407 x2: -Infinity,
20408 y1: Infinity,
20409 y2: -Infinity
20410 };
20411 if (options.boundingBox) {
20412 nodes.forEach(function (node) {
20413 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20414 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20415 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20416 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20417 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20418 });
20419 coseBB.w = coseBB.x2 - coseBB.x1;
20420 coseBB.h = coseBB.y2 - coseBB.y1;
20421 }
20422 return function (ele, i) {
20423 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20424 if (options.boundingBox) {
20425 // then add extra bounding box constraint
20426 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20427 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20428 return {
20429 x: bb.x1 + pctX * bb.w,
20430 y: bb.y1 + pctY * bb.h
20431 };
20432 } else {
20433 return {
20434 x: lnode.positionX,
20435 y: lnode.positionY
20436 };
20437 }
20438 };
20439};
20440
20441/**
20442 * @brief : Updates the positions of nodes in the network
20443 * @arg layoutInfo : LayoutInfo object
20444 * @arg cy : Cytoscape object
20445 * @arg options : Layout options
20446 */
20447var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20448 // var s = 'Refreshing positions';
20449 // logDebug(s);
20450
20451 var layout = options.layout;
20452 var nodes = options.eles.nodes();
20453 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20454 nodes.positions(getScaledPos);
20455
20456 // Trigger layoutReady only on first call
20457 if (true !== layoutInfo.ready) {
20458 // s = 'Triggering layoutready';
20459 // logDebug(s);
20460 layoutInfo.ready = true;
20461 layout.one('layoutready', options.ready);
20462 layout.emit({
20463 type: 'layoutready',
20464 layout: this
20465 });
20466 }
20467};
20468
20469/**
20470 * @brief : Logs a debug message in JS console, if DEBUG is ON
20471 */
20472// var logDebug = function(text) {
20473// if (DEBUG) {
20474// console.debug(text);
20475// }
20476// };
20477
20478/**
20479 * @brief : Performs one iteration of the physical simulation
20480 * @arg layoutInfo : LayoutInfo object already initialized
20481 * @arg cy : Cytoscape object
20482 * @arg options : Layout options
20483 */
20484var step = function step(layoutInfo, options, _step) {
20485 // var s = "\n\n###############################";
20486 // s += "\nSTEP: " + step;
20487 // s += "\n###############################\n";
20488 // logDebug(s);
20489
20490 // Calculate node repulsions
20491 calculateNodeForces(layoutInfo, options);
20492 // Calculate edge forces
20493 calculateEdgeForces(layoutInfo);
20494 // Calculate gravity forces
20495 calculateGravityForces(layoutInfo, options);
20496 // Propagate forces from parent to child
20497 propagateForces(layoutInfo);
20498 // Update positions based on calculated forces
20499 updatePositions(layoutInfo);
20500};
20501
20502/**
20503 * @brief : Computes the node repulsion forces
20504 */
20505var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20506 // Go through each of the graphs in graphSet
20507 // Nodes only repel each other if they belong to the same graph
20508 // var s = 'calculateNodeForces';
20509 // logDebug(s);
20510 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20511 var graph = layoutInfo.graphSet[i];
20512 var numNodes = graph.length;
20513
20514 // s = "Set: " + graph.toString();
20515 // logDebug(s);
20516
20517 // Now get all the pairs of nodes
20518 // Only get each pair once, (A, B) = (B, A)
20519 for (var j = 0; j < numNodes; j++) {
20520 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20521 for (var k = j + 1; k < numNodes; k++) {
20522 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20523 nodeRepulsion(node1, node2, layoutInfo, options);
20524 }
20525 }
20526 }
20527};
20528var randomDistance = function randomDistance(max) {
20529 return -max + 2 * max * Math.random();
20530};
20531
20532/**
20533 * @brief : Compute the node repulsion forces between a pair of nodes
20534 */
20535var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20536 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20537
20538 var cmptId1 = node1.cmptId;
20539 var cmptId2 = node2.cmptId;
20540 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20541 return;
20542 }
20543
20544 // Get direction of line connecting both node centers
20545 var directionX = node2.positionX - node1.positionX;
20546 var directionY = node2.positionY - node1.positionY;
20547 var maxRandDist = 1;
20548 // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20549
20550 // If both centers are the same, apply a random force
20551 if (0 === directionX && 0 === directionY) {
20552 directionX = randomDistance(maxRandDist);
20553 directionY = randomDistance(maxRandDist);
20554 }
20555 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20556 if (overlap > 0) {
20557 // s += "\nNodes DO overlap.";
20558 // s += "\nOverlap: " + overlap;
20559 // If nodes overlap, repulsion force is proportional
20560 // to the overlap
20561 var force = options.nodeOverlap * overlap;
20562
20563 // Compute the module and components of the force vector
20564 var distance = Math.sqrt(directionX * directionX + directionY * directionY);
20565 // s += "\nDistance: " + distance;
20566 var forceX = force * directionX / distance;
20567 var forceY = force * directionY / distance;
20568 } else {
20569 // s += "\nNodes do NOT overlap.";
20570 // If there's no overlap, force is inversely proportional
20571 // to squared distance
20572
20573 // Get clipping points for both nodes
20574 var point1 = findClippingPoint(node1, directionX, directionY);
20575 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY);
20576
20577 // Use clipping points to compute distance
20578 var distanceX = point2.x - point1.x;
20579 var distanceY = point2.y - point1.y;
20580 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20581 var distance = Math.sqrt(distanceSqr);
20582 // s += "\nDistance: " + distance;
20583
20584 // Compute the module and components of the force vector
20585 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20586 var forceX = force * distanceX / distance;
20587 var forceY = force * distanceY / distance;
20588 }
20589
20590 // Apply force
20591 if (!node1.isLocked) {
20592 node1.offsetX -= forceX;
20593 node1.offsetY -= forceY;
20594 }
20595 if (!node2.isLocked) {
20596 node2.offsetX += forceX;
20597 node2.offsetY += forceY;
20598 }
20599
20600 // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20601 // logDebug(s);
20602
20603 return;
20604};
20605
20606/**
20607 * @brief : Determines whether two nodes overlap or not
20608 * @return : Amount of overlapping (0 => no overlap)
20609 */
20610var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20611 if (dX > 0) {
20612 var overlapX = node1.maxX - node2.minX;
20613 } else {
20614 var overlapX = node2.maxX - node1.minX;
20615 }
20616 if (dY > 0) {
20617 var overlapY = node1.maxY - node2.minY;
20618 } else {
20619 var overlapY = node2.maxY - node1.minY;
20620 }
20621 if (overlapX >= 0 && overlapY >= 0) {
20622 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20623 } else {
20624 return 0;
20625 }
20626};
20627
20628/**
20629 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20630 * the rectangular bounding box of it's source/target node
20631 */
20632var findClippingPoint = function findClippingPoint(node, dX, dY) {
20633 // Shorcuts
20634 var X = node.positionX;
20635 var Y = node.positionY;
20636 var H = node.height || 1;
20637 var W = node.width || 1;
20638 var dirSlope = dY / dX;
20639 var nodeSlope = H / W;
20640
20641 // var s = 'Computing clipping point of node ' + node.id +
20642 // " . Height: " + H + ", Width: " + W +
20643 // "\nDirection " + dX + ", " + dY;
20644 //
20645 // Compute intersection
20646 var res = {};
20647
20648 // Case: Vertical direction (up)
20649 if (0 === dX && 0 < dY) {
20650 res.x = X;
20651 // s += "\nUp direction";
20652 res.y = Y + H / 2;
20653 return res;
20654 }
20655
20656 // Case: Vertical direction (down)
20657 if (0 === dX && 0 > dY) {
20658 res.x = X;
20659 res.y = Y + H / 2;
20660 // s += "\nDown direction";
20661
20662 return res;
20663 }
20664
20665 // Case: Intersects the right border
20666 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20667 res.x = X + W / 2;
20668 res.y = Y + W * dY / 2 / dX;
20669 // s += "\nRightborder";
20670
20671 return res;
20672 }
20673
20674 // Case: Intersects the left border
20675 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20676 res.x = X - W / 2;
20677 res.y = Y - W * dY / 2 / dX;
20678 // s += "\nLeftborder";
20679
20680 return res;
20681 }
20682
20683 // Case: Intersects the top border
20684 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20685 res.x = X + H * dX / 2 / dY;
20686 res.y = Y + H / 2;
20687 // s += "\nTop border";
20688
20689 return res;
20690 }
20691
20692 // Case: Intersects the bottom border
20693 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20694 res.x = X - H * dX / 2 / dY;
20695 res.y = Y - H / 2;
20696 // s += "\nBottom border";
20697
20698 return res;
20699 }
20700
20701 // s += "\nClipping point found at " + res.x + ", " + res.y;
20702 // logDebug(s);
20703 return res;
20704};
20705
20706/**
20707 * @brief : Calculates all edge forces
20708 */
20709var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20710 // Iterate over all edges
20711 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20712 // Get edge, source & target nodes
20713 var edge = layoutInfo.layoutEdges[i];
20714 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20715 var source = layoutInfo.layoutNodes[sourceIx];
20716 var targetIx = layoutInfo.idToIndex[edge.targetId];
20717 var target = layoutInfo.layoutNodes[targetIx];
20718
20719 // Get direction of line connecting both node centers
20720 var directionX = target.positionX - source.positionX;
20721 var directionY = target.positionY - source.positionY;
20722
20723 // If both centers are the same, do nothing.
20724 // A random force has already been applied as node repulsion
20725 if (0 === directionX && 0 === directionY) {
20726 continue;
20727 }
20728
20729 // Get clipping points for both nodes
20730 var point1 = findClippingPoint(source, directionX, directionY);
20731 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20732 var lx = point2.x - point1.x;
20733 var ly = point2.y - point1.y;
20734 var l = Math.sqrt(lx * lx + ly * ly);
20735 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20736 if (0 !== l) {
20737 var forceX = force * lx / l;
20738 var forceY = force * ly / l;
20739 } else {
20740 var forceX = 0;
20741 var forceY = 0;
20742 }
20743
20744 // Add this force to target and source nodes
20745 if (!source.isLocked) {
20746 source.offsetX += forceX;
20747 source.offsetY += forceY;
20748 }
20749 if (!target.isLocked) {
20750 target.offsetX -= forceX;
20751 target.offsetY -= forceY;
20752 }
20753
20754 // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20755 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20756 // logDebug(s);
20757 }
20758};
20759
20760/**
20761 * @brief : Computes gravity forces for all nodes
20762 */
20763var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20764 if (options.gravity === 0) {
20765 return;
20766 }
20767 var distThreshold = 1;
20768
20769 // var s = 'calculateGravityForces';
20770 // logDebug(s);
20771 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20772 var graph = layoutInfo.graphSet[i];
20773 var numNodes = graph.length;
20774
20775 // s = "Set: " + graph.toString();
20776 // logDebug(s);
20777
20778 // Compute graph center
20779 if (0 === i) {
20780 var centerX = layoutInfo.clientHeight / 2;
20781 var centerY = layoutInfo.clientWidth / 2;
20782 } else {
20783 // Get Parent node for this graph, and use its position as center
20784 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20785 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20786 var centerX = parent.positionX;
20787 var centerY = parent.positionY;
20788 }
20789 // s = "Center found at: " + centerX + ", " + centerY;
20790 // logDebug(s);
20791
20792 // Apply force to all nodes in graph
20793 for (var j = 0; j < numNodes; j++) {
20794 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20795 // s = "Node: " + node.id;
20796
20797 if (node.isLocked) {
20798 continue;
20799 }
20800 var dx = centerX - node.positionX;
20801 var dy = centerY - node.positionY;
20802 var d = Math.sqrt(dx * dx + dy * dy);
20803 if (d > distThreshold) {
20804 var fx = options.gravity * dx / d;
20805 var fy = options.gravity * dy / d;
20806 node.offsetX += fx;
20807 node.offsetY += fy;
20808 // s += ": Applied force: " + fx + ", " + fy;
20809 }
20810 // logDebug(s);
20811 }
20812 }
20813};
20814
20815/**
20816 * @brief : This function propagates the existing offsets from
20817 * parent nodes to its descendents.
20818 * @arg layoutInfo : layoutInfo Object
20819 * @arg cy : cytoscape Object
20820 * @arg options : Layout options
20821 */
20822var propagateForces = function propagateForces(layoutInfo, options) {
20823 // Inline implementation of a queue, used for traversing the graph in BFS order
20824 var queue = [];
20825 var start = 0; // Points to the start the queue
20826 var end = -1; // Points to the end of the queue
20827
20828 // logDebug('propagateForces');
20829
20830 // Start by visiting the nodes in the root graph
20831 queue.push.apply(queue, layoutInfo.graphSet[0]);
20832 end += layoutInfo.graphSet[0].length;
20833
20834 // Traverse the graph, level by level,
20835 while (start <= end) {
20836 // Get the node to visit and remove it from queue
20837 var nodeId = queue[start++];
20838 var nodeIndex = layoutInfo.idToIndex[nodeId];
20839 var node = layoutInfo.layoutNodes[nodeIndex];
20840 var children = node.children;
20841
20842 // We only need to process the node if it's compound
20843 if (0 < children.length && !node.isLocked) {
20844 var offX = node.offsetX;
20845 var offY = node.offsetY;
20846
20847 // var s = "Propagating offset from parent node : " + node.id +
20848 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20849 // s += "\n Children: " + children.toString();
20850 // logDebug(s);
20851
20852 for (var i = 0; i < children.length; i++) {
20853 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]];
20854 // Propagate offset
20855 childNode.offsetX += offX;
20856 childNode.offsetY += offY;
20857 // Add children to queue to be visited
20858 queue[++end] = children[i];
20859 }
20860
20861 // Reset parent offsets
20862 node.offsetX = 0;
20863 node.offsetY = 0;
20864 }
20865 }
20866};
20867
20868/**
20869 * @brief : Updates the layout model positions, based on
20870 * the accumulated forces
20871 */
20872var updatePositions = function updatePositions(layoutInfo, options) {
20873 // var s = 'Updating positions';
20874 // logDebug(s);
20875
20876 // Reset boundaries for compound nodes
20877 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20878 var n = layoutInfo.layoutNodes[i];
20879 if (0 < n.children.length) {
20880 // logDebug("Resetting boundaries of compound node: " + n.id);
20881 n.maxX = undefined;
20882 n.minX = undefined;
20883 n.maxY = undefined;
20884 n.minY = undefined;
20885 }
20886 }
20887 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20888 var n = layoutInfo.layoutNodes[i];
20889 if (0 < n.children.length || n.isLocked) {
20890 // No need to set compound or locked node position
20891 // logDebug("Skipping position update of node: " + n.id);
20892 continue;
20893 }
20894 // s = "Node: " + n.id + " Previous position: (" +
20895 // n.positionX + ", " + n.positionY + ").";
20896
20897 // Limit displacement in order to improve stability
20898 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20899 n.positionX += tempForce.x;
20900 n.positionY += tempForce.y;
20901 n.offsetX = 0;
20902 n.offsetY = 0;
20903 n.minX = n.positionX - n.width;
20904 n.maxX = n.positionX + n.width;
20905 n.minY = n.positionY - n.height;
20906 n.maxY = n.positionY + n.height;
20907 // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20908 // logDebug(s);
20909
20910 // Update ancestry boudaries
20911 updateAncestryBoundaries(n, layoutInfo);
20912 }
20913
20914 // Update size, position of compund nodes
20915 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20916 var n = layoutInfo.layoutNodes[i];
20917 if (0 < n.children.length && !n.isLocked) {
20918 n.positionX = (n.maxX + n.minX) / 2;
20919 n.positionY = (n.maxY + n.minY) / 2;
20920 n.width = n.maxX - n.minX;
20921 n.height = n.maxY - n.minY;
20922 // s = "Updating position, size of compound node " + n.id;
20923 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20924 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20925 // logDebug(s);
20926 }
20927 }
20928};
20929
20930/**
20931 * @brief : Limits a force (forceX, forceY) to be not
20932 * greater (in modulo) than max.
20933 8 Preserves force direction.
20934 */
20935var limitForce = function limitForce(forceX, forceY, max) {
20936 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20937 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20938 if (force > max) {
20939 var res = {
20940 x: max * forceX / force,
20941 y: max * forceY / force
20942 };
20943 } else {
20944 var res = {
20945 x: forceX,
20946 y: forceY
20947 };
20948 }
20949
20950 // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20951 // logDebug(s);
20952
20953 return res;
20954};
20955
20956/**
20957 * @brief : Function used for keeping track of compound node
20958 * sizes, since they should bound all their subnodes.
20959 */
20960var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20961 // var s = "Propagating new position/size of node " + node.id;
20962 var parentId = node.parentId;
20963 if (null == parentId) {
20964 // If there's no parent, we are done
20965 // s += ". No parent node.";
20966 // logDebug(s);
20967 return;
20968 }
20969
20970 // Get Parent Node
20971 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20972 var flag = false;
20973
20974 // MaxX
20975 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20976 p.maxX = node.maxX + p.padRight;
20977 flag = true;
20978 // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20979 }
20980
20981 // MinX
20982 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20983 p.minX = node.minX - p.padLeft;
20984 flag = true;
20985 // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20986 }
20987
20988 // MaxY
20989 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20990 p.maxY = node.maxY + p.padBottom;
20991 flag = true;
20992 // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20993 }
20994
20995 // MinY
20996 if (null == p.minY || node.minY - p.padTop < p.minY) {
20997 p.minY = node.minY - p.padTop;
20998 flag = true;
20999 // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
21000 }
21001
21002 // If updated boundaries, propagate changes upward
21003 if (flag) {
21004 // logDebug(s);
21005 return updateAncestryBoundaries(p, layoutInfo);
21006 }
21007
21008 // s += ". No changes in boundaries/position of parent node " + p.id;
21009 // logDebug(s);
21010 return;
21011};
21012var separateComponents = function separateComponents(layoutInfo, options) {
21013 var nodes = layoutInfo.layoutNodes;
21014 var components = [];
21015 for (var i = 0; i < nodes.length; i++) {
21016 var node = nodes[i];
21017 var cid = node.cmptId;
21018 var component = components[cid] = components[cid] || [];
21019 component.push(node);
21020 }
21021 var totalA = 0;
21022 for (var i = 0; i < components.length; i++) {
21023 var c = components[i];
21024 if (!c) {
21025 continue;
21026 }
21027 c.x1 = Infinity;
21028 c.x2 = -Infinity;
21029 c.y1 = Infinity;
21030 c.y2 = -Infinity;
21031 for (var j = 0; j < c.length; j++) {
21032 var n = c[j];
21033 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
21034 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
21035 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
21036 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
21037 }
21038 c.w = c.x2 - c.x1;
21039 c.h = c.y2 - c.y1;
21040 totalA += c.w * c.h;
21041 }
21042 components.sort(function (c1, c2) {
21043 return c2.w * c2.h - c1.w * c1.h;
21044 });
21045 var x = 0;
21046 var y = 0;
21047 var usedW = 0;
21048 var rowH = 0;
21049 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
21050 for (var i = 0; i < components.length; i++) {
21051 var c = components[i];
21052 if (!c) {
21053 continue;
21054 }
21055 for (var j = 0; j < c.length; j++) {
21056 var n = c[j];
21057 if (!n.isLocked) {
21058 n.positionX += x - c.x1;
21059 n.positionY += y - c.y1;
21060 }
21061 }
21062 x += c.w + options.componentSpacing;
21063 usedW += c.w + options.componentSpacing;
21064 rowH = Math.max(rowH, c.h);
21065 if (usedW > maxRowW) {
21066 y += rowH + options.componentSpacing;
21067 x = 0;
21068 usedW = 0;
21069 rowH = 0;
21070 }
21071 }
21072};
21073
21074var defaults$3 = {
21075 fit: true,
21076 // whether to fit the viewport to the graph
21077 padding: 30,
21078 // padding used on fit
21079 boundingBox: undefined,
21080 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21081 avoidOverlap: true,
21082 // prevents node overlap, may overflow boundingBox if not enough space
21083 avoidOverlapPadding: 10,
21084 // extra spacing around nodes when avoidOverlap: true
21085 nodeDimensionsIncludeLabels: false,
21086 // Excludes the label when calculating node bounding boxes for the layout algorithm
21087 spacingFactor: undefined,
21088 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21089 condense: false,
21090 // uses all available space on false, uses minimal space on true
21091 rows: undefined,
21092 // force num of rows in the grid
21093 cols: undefined,
21094 // force num of columns in the grid
21095 position: function position(node) {},
21096 // returns { row, col } for element
21097 sort: undefined,
21098 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
21099 animate: false,
21100 // whether to transition the node positions
21101 animationDuration: 500,
21102 // duration of animation in ms if enabled
21103 animationEasing: undefined,
21104 // easing of animation if enabled
21105 animateFilter: function animateFilter(node, i) {
21106 return true;
21107 },
21108 // 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
21109 ready: undefined,
21110 // callback on layoutready
21111 stop: undefined,
21112 // callback on layoutstop
21113 transform: function transform(node, position) {
21114 return position;
21115 } // transform a given node position. Useful for changing flow direction in discrete layouts
21116};
21117
21118function GridLayout(options) {
21119 this.options = extend({}, defaults$3, options);
21120}
21121GridLayout.prototype.run = function () {
21122 var params = this.options;
21123 var options = params;
21124 var cy = params.cy;
21125 var eles = options.eles;
21126 var nodes = eles.nodes().not(':parent');
21127 if (options.sort) {
21128 nodes = nodes.sort(options.sort);
21129 }
21130 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21131 x1: 0,
21132 y1: 0,
21133 w: cy.width(),
21134 h: cy.height()
21135 });
21136 if (bb.h === 0 || bb.w === 0) {
21137 eles.nodes().layoutPositions(this, options, function (ele) {
21138 return {
21139 x: bb.x1,
21140 y: bb.y1
21141 };
21142 });
21143 } else {
21144 // width/height * splits^2 = cells where splits is number of times to split width
21145 var cells = nodes.size();
21146 var splits = Math.sqrt(cells * bb.h / bb.w);
21147 var rows = Math.round(splits);
21148 var cols = Math.round(bb.w / bb.h * splits);
21149 var small = function small(val) {
21150 if (val == null) {
21151 return Math.min(rows, cols);
21152 } else {
21153 var min = Math.min(rows, cols);
21154 if (min == rows) {
21155 rows = val;
21156 } else {
21157 cols = val;
21158 }
21159 }
21160 };
21161 var large = function large(val) {
21162 if (val == null) {
21163 return Math.max(rows, cols);
21164 } else {
21165 var max = Math.max(rows, cols);
21166 if (max == rows) {
21167 rows = val;
21168 } else {
21169 cols = val;
21170 }
21171 }
21172 };
21173 var oRows = options.rows;
21174 var oCols = options.cols != null ? options.cols : options.columns;
21175
21176 // if rows or columns were set in options, use those values
21177 if (oRows != null && oCols != null) {
21178 rows = oRows;
21179 cols = oCols;
21180 } else if (oRows != null && oCols == null) {
21181 rows = oRows;
21182 cols = Math.ceil(cells / rows);
21183 } else if (oRows == null && oCols != null) {
21184 cols = oCols;
21185 rows = Math.ceil(cells / cols);
21186 }
21187
21188 // otherwise use the automatic values and adjust accordingly
21189
21190 // if rounding was up, see if we can reduce rows or columns
21191 else if (cols * rows > cells) {
21192 var sm = small();
21193 var lg = large();
21194
21195 // reducing the small side takes away the most cells, so try it first
21196 if ((sm - 1) * lg >= cells) {
21197 small(sm - 1);
21198 } else if ((lg - 1) * sm >= cells) {
21199 large(lg - 1);
21200 }
21201 } else {
21202 // if rounding was too low, add rows or columns
21203 while (cols * rows < cells) {
21204 var _sm = small();
21205 var _lg = large();
21206
21207 // try to add to larger side first (adds less in multiplication)
21208 if ((_lg + 1) * _sm >= cells) {
21209 large(_lg + 1);
21210 } else {
21211 small(_sm + 1);
21212 }
21213 }
21214 }
21215 var cellWidth = bb.w / cols;
21216 var cellHeight = bb.h / rows;
21217 if (options.condense) {
21218 cellWidth = 0;
21219 cellHeight = 0;
21220 }
21221 if (options.avoidOverlap) {
21222 for (var i = 0; i < nodes.length; i++) {
21223 var node = nodes[i];
21224 var pos = node._private.position;
21225 if (pos.x == null || pos.y == null) {
21226 // for bb
21227 pos.x = 0;
21228 pos.y = 0;
21229 }
21230 var nbb = node.layoutDimensions(options);
21231 var p = options.avoidOverlapPadding;
21232 var w = nbb.w + p;
21233 var h = nbb.h + p;
21234 cellWidth = Math.max(cellWidth, w);
21235 cellHeight = Math.max(cellHeight, h);
21236 }
21237 }
21238 var cellUsed = {}; // e.g. 'c-0-2' => true
21239
21240 var used = function used(row, col) {
21241 return cellUsed['c-' + row + '-' + col] ? true : false;
21242 };
21243 var use = function use(row, col) {
21244 cellUsed['c-' + row + '-' + col] = true;
21245 };
21246
21247 // to keep track of current cell position
21248 var row = 0;
21249 var col = 0;
21250 var moveToNextCell = function moveToNextCell() {
21251 col++;
21252 if (col >= cols) {
21253 col = 0;
21254 row++;
21255 }
21256 };
21257
21258 // get a cache of all the manual positions
21259 var id2manPos = {};
21260 for (var _i = 0; _i < nodes.length; _i++) {
21261 var _node = nodes[_i];
21262 var rcPos = options.position(_node);
21263 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21264 // must have at least row or col def'd
21265 var _pos = {
21266 row: rcPos.row,
21267 col: rcPos.col
21268 };
21269 if (_pos.col === undefined) {
21270 // find unused col
21271 _pos.col = 0;
21272 while (used(_pos.row, _pos.col)) {
21273 _pos.col++;
21274 }
21275 } else if (_pos.row === undefined) {
21276 // find unused row
21277 _pos.row = 0;
21278 while (used(_pos.row, _pos.col)) {
21279 _pos.row++;
21280 }
21281 }
21282 id2manPos[_node.id()] = _pos;
21283 use(_pos.row, _pos.col);
21284 }
21285 }
21286 var getPos = function getPos(element, i) {
21287 var x, y;
21288 if (element.locked() || element.isParent()) {
21289 return false;
21290 }
21291
21292 // see if we have a manual position set
21293 var rcPos = id2manPos[element.id()];
21294 if (rcPos) {
21295 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21296 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21297 } else {
21298 // otherwise set automatically
21299
21300 while (used(row, col)) {
21301 moveToNextCell();
21302 }
21303 x = col * cellWidth + cellWidth / 2 + bb.x1;
21304 y = row * cellHeight + cellHeight / 2 + bb.y1;
21305 use(row, col);
21306 moveToNextCell();
21307 }
21308 return {
21309 x: x,
21310 y: y
21311 };
21312 };
21313 nodes.layoutPositions(this, options, getPos);
21314 }
21315 return this; // chaining
21316};
21317
21318// default layout options
21319var defaults$2 = {
21320 ready: function ready() {},
21321 // on layoutready
21322 stop: function stop() {} // on layoutstop
21323};
21324
21325// constructor
21326// options : object containing layout options
21327function NullLayout(options) {
21328 this.options = extend({}, defaults$2, options);
21329}
21330
21331// runs the layout
21332NullLayout.prototype.run = function () {
21333 var options = this.options;
21334 var eles = options.eles; // elements to consider in the layout
21335 var layout = this;
21336
21337 // cy is automatically populated for us in the constructor
21338 // (disable eslint for next line as this serves as example layout code to external developers)
21339 // eslint-disable-next-line no-unused-vars
21340 options.cy;
21341 layout.emit('layoutstart');
21342
21343 // puts all nodes at (0, 0)
21344 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21345 eles.nodes().positions(function () {
21346 return {
21347 x: 0,
21348 y: 0
21349 };
21350 });
21351
21352 // trigger layoutready when each node has had its position set at least once
21353 layout.one('layoutready', options.ready);
21354 layout.emit('layoutready');
21355
21356 // trigger layoutstop when the layout stops (e.g. finishes)
21357 layout.one('layoutstop', options.stop);
21358 layout.emit('layoutstop');
21359 return this; // chaining
21360};
21361
21362// called on continuous layouts to stop them before they finish
21363NullLayout.prototype.stop = function () {
21364 return this; // chaining
21365};
21366
21367var defaults$1 = {
21368 positions: undefined,
21369 // map of (node id) => (position obj); or function(node){ return somPos; }
21370 zoom: undefined,
21371 // the zoom level to set (prob want fit = false if set)
21372 pan: undefined,
21373 // the pan level to set (prob want fit = false if set)
21374 fit: true,
21375 // whether to fit to viewport
21376 padding: 30,
21377 // padding on fit
21378 spacingFactor: undefined,
21379 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21380 animate: false,
21381 // whether to transition the node positions
21382 animationDuration: 500,
21383 // duration of animation in ms if enabled
21384 animationEasing: undefined,
21385 // easing of animation if enabled
21386 animateFilter: function animateFilter(node, i) {
21387 return true;
21388 },
21389 // 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
21390 ready: undefined,
21391 // callback on layoutready
21392 stop: undefined,
21393 // callback on layoutstop
21394 transform: function transform(node, position) {
21395 return position;
21396 } // transform a given node position. Useful for changing flow direction in discrete layouts
21397};
21398
21399function PresetLayout(options) {
21400 this.options = extend({}, defaults$1, options);
21401}
21402PresetLayout.prototype.run = function () {
21403 var options = this.options;
21404 var eles = options.eles;
21405 var nodes = eles.nodes();
21406 var posIsFn = fn$6(options.positions);
21407 function getPosition(node) {
21408 if (options.positions == null) {
21409 return copyPosition(node.position());
21410 }
21411 if (posIsFn) {
21412 return options.positions(node);
21413 }
21414 var pos = options.positions[node._private.data.id];
21415 if (pos == null) {
21416 return null;
21417 }
21418 return pos;
21419 }
21420 nodes.layoutPositions(this, options, function (node, i) {
21421 var position = getPosition(node);
21422 if (node.locked() || position == null) {
21423 return false;
21424 }
21425 return position;
21426 });
21427 return this; // chaining
21428};
21429
21430var defaults = {
21431 fit: true,
21432 // whether to fit to viewport
21433 padding: 30,
21434 // fit padding
21435 boundingBox: undefined,
21436 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21437 animate: false,
21438 // whether to transition the node positions
21439 animationDuration: 500,
21440 // duration of animation in ms if enabled
21441 animationEasing: undefined,
21442 // easing of animation if enabled
21443 animateFilter: function animateFilter(node, i) {
21444 return true;
21445 },
21446 // 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
21447 ready: undefined,
21448 // callback on layoutready
21449 stop: undefined,
21450 // callback on layoutstop
21451 transform: function transform(node, position) {
21452 return position;
21453 } // transform a given node position. Useful for changing flow direction in discrete layouts
21454};
21455
21456function RandomLayout(options) {
21457 this.options = extend({}, defaults, options);
21458}
21459RandomLayout.prototype.run = function () {
21460 var options = this.options;
21461 var cy = options.cy;
21462 var eles = options.eles;
21463 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21464 x1: 0,
21465 y1: 0,
21466 w: cy.width(),
21467 h: cy.height()
21468 });
21469 var getPos = function getPos(node, i) {
21470 return {
21471 x: bb.x1 + Math.round(Math.random() * bb.w),
21472 y: bb.y1 + Math.round(Math.random() * bb.h)
21473 };
21474 };
21475 eles.nodes().layoutPositions(this, options, getPos);
21476 return this; // chaining
21477};
21478
21479var layout = [{
21480 name: 'breadthfirst',
21481 impl: BreadthFirstLayout
21482}, {
21483 name: 'circle',
21484 impl: CircleLayout
21485}, {
21486 name: 'concentric',
21487 impl: ConcentricLayout
21488}, {
21489 name: 'cose',
21490 impl: CoseLayout
21491}, {
21492 name: 'grid',
21493 impl: GridLayout
21494}, {
21495 name: 'null',
21496 impl: NullLayout
21497}, {
21498 name: 'preset',
21499 impl: PresetLayout
21500}, {
21501 name: 'random',
21502 impl: RandomLayout
21503}];
21504
21505function NullRenderer(options) {
21506 this.options = options;
21507 this.notifications = 0; // for testing
21508}
21509
21510var noop = function noop() {};
21511var throwImgErr = function throwImgErr() {
21512 throw new Error('A headless instance can not render images');
21513};
21514NullRenderer.prototype = {
21515 recalculateRenderedStyle: noop,
21516 notify: function notify() {
21517 this.notifications++;
21518 },
21519 init: noop,
21520 isHeadless: function isHeadless() {
21521 return true;
21522 },
21523 png: throwImgErr,
21524 jpg: throwImgErr
21525};
21526
21527var BRp$f = {};
21528BRp$f.arrowShapeWidth = 0.3;
21529BRp$f.registerArrowShapes = function () {
21530 var arrowShapes = this.arrowShapes = {};
21531 var renderer = this;
21532
21533 // Contract for arrow shapes:
21534 // 0, 0 is arrow tip
21535 // (0, 1) is direction towards node
21536 // (1, 0) is right
21537 //
21538 // functional api:
21539 // collide: check x, y in shape
21540 // roughCollide: called before collide, no false negatives
21541 // draw: draw
21542 // spacing: dist(arrowTip, nodeBoundary)
21543 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21544
21545 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21546 var x1 = translation.x - size / 2 - padding;
21547 var x2 = translation.x + size / 2 + padding;
21548 var y1 = translation.y - size / 2 - padding;
21549 var y2 = translation.y + size / 2 + padding;
21550 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21551 return inside;
21552 };
21553 var transform = function transform(x, y, size, angle, translation) {
21554 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21555 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21556 var xScaled = xRotated * size;
21557 var yScaled = yRotated * size;
21558 var xTranslated = xScaled + translation.x;
21559 var yTranslated = yScaled + translation.y;
21560 return {
21561 x: xTranslated,
21562 y: yTranslated
21563 };
21564 };
21565 var transformPoints = function transformPoints(pts, size, angle, translation) {
21566 var retPts = [];
21567 for (var i = 0; i < pts.length; i += 2) {
21568 var x = pts[i];
21569 var y = pts[i + 1];
21570 retPts.push(transform(x, y, size, angle, translation));
21571 }
21572 return retPts;
21573 };
21574 var pointsToArr = function pointsToArr(pts) {
21575 var ret = [];
21576 for (var i = 0; i < pts.length; i++) {
21577 var p = pts[i];
21578 ret.push(p.x, p.y);
21579 }
21580 return ret;
21581 };
21582 var standardGap = function standardGap(edge) {
21583 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21584 };
21585 var defineArrowShape = function defineArrowShape(name, defn) {
21586 if (string(defn)) {
21587 defn = arrowShapes[defn];
21588 }
21589 arrowShapes[name] = extend({
21590 name: name,
21591 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21592 collide: function collide(x, y, size, angle, translation, padding) {
21593 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21594 var inside = pointInsidePolygonPoints(x, y, points);
21595 return inside;
21596 },
21597 roughCollide: bbCollide,
21598 draw: function draw(context, size, angle, translation) {
21599 var points = transformPoints(this.points, size, angle, translation);
21600 renderer.arrowShapeImpl('polygon')(context, points);
21601 },
21602 spacing: function spacing(edge) {
21603 return 0;
21604 },
21605 gap: standardGap
21606 }, defn);
21607 };
21608 defineArrowShape('none', {
21609 collide: falsify,
21610 roughCollide: falsify,
21611 draw: noop$1,
21612 spacing: zeroify,
21613 gap: zeroify
21614 });
21615 defineArrowShape('triangle', {
21616 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21617 });
21618 defineArrowShape('arrow', 'triangle');
21619 defineArrowShape('triangle-backcurve', {
21620 points: arrowShapes['triangle'].points,
21621 controlPoint: [0, -0.15],
21622 roughCollide: bbCollide,
21623 draw: function draw(context, size, angle, translation, edgeWidth) {
21624 var ptsTrans = transformPoints(this.points, size, angle, translation);
21625 var ctrlPt = this.controlPoint;
21626 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21627 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21628 },
21629 gap: function gap(edge) {
21630 return standardGap(edge) * 0.8;
21631 }
21632 });
21633 defineArrowShape('triangle-tee', {
21634 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21635 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21636 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21637 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21638 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21639 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21640 return inside;
21641 },
21642 draw: function draw(context, size, angle, translation, edgeWidth) {
21643 var triPts = transformPoints(this.points, size, angle, translation);
21644 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21645 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21646 }
21647 });
21648 defineArrowShape('circle-triangle', {
21649 radius: 0.15,
21650 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21651 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21652 var t = translation;
21653 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21654 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21655 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21656 },
21657 draw: function draw(context, size, angle, translation, edgeWidth) {
21658 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21659 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21660 },
21661 spacing: function spacing(edge) {
21662 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21663 }
21664 });
21665 defineArrowShape('triangle-cross', {
21666 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21667 baseCrossLinePts: [-0.15, -0.4,
21668 // first half of the rectangle
21669 -0.15, -0.4, 0.15, -0.4,
21670 // second half of the rectangle
21671 0.15, -0.4],
21672 crossLinePts: function crossLinePts(size, edgeWidth) {
21673 // shift points so that the distance between the cross points matches edge width
21674 var p = this.baseCrossLinePts.slice();
21675 var shiftFactor = edgeWidth / size;
21676 var y0 = 3;
21677 var y1 = 5;
21678 p[y0] = p[y0] - shiftFactor;
21679 p[y1] = p[y1] - shiftFactor;
21680 return p;
21681 },
21682 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21683 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21684 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21685 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21686 return inside;
21687 },
21688 draw: function draw(context, size, angle, translation, edgeWidth) {
21689 var triPts = transformPoints(this.points, size, angle, translation);
21690 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21691 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21692 }
21693 });
21694 defineArrowShape('vee', {
21695 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21696 gap: function gap(edge) {
21697 return standardGap(edge) * 0.525;
21698 }
21699 });
21700 defineArrowShape('circle', {
21701 radius: 0.15,
21702 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21703 var t = translation;
21704 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21705 return inside;
21706 },
21707 draw: function draw(context, size, angle, translation, edgeWidth) {
21708 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21709 },
21710 spacing: function spacing(edge) {
21711 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21712 }
21713 });
21714 defineArrowShape('tee', {
21715 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21716 spacing: function spacing(edge) {
21717 return 1;
21718 },
21719 gap: function gap(edge) {
21720 return 1;
21721 }
21722 });
21723 defineArrowShape('square', {
21724 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21725 });
21726 defineArrowShape('diamond', {
21727 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21728 gap: function gap(edge) {
21729 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21730 }
21731 });
21732 defineArrowShape('chevron', {
21733 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21734 gap: function gap(edge) {
21735 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21736 }
21737 });
21738};
21739
21740var BRp$e = {};
21741
21742// Project mouse
21743BRp$e.projectIntoViewport = function (clientX, clientY) {
21744 var cy = this.cy;
21745 var offsets = this.findContainerClientCoords();
21746 var offsetLeft = offsets[0];
21747 var offsetTop = offsets[1];
21748 var scale = offsets[4];
21749 var pan = cy.pan();
21750 var zoom = cy.zoom();
21751 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21752 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21753 return [x, y];
21754};
21755BRp$e.findContainerClientCoords = function () {
21756 if (this.containerBB) {
21757 return this.containerBB;
21758 }
21759 var container = this.container;
21760 var rect = container.getBoundingClientRect();
21761 var style = this.cy.window().getComputedStyle(container);
21762 var styleValue = function styleValue(name) {
21763 return parseFloat(style.getPropertyValue(name));
21764 };
21765 var padding = {
21766 left: styleValue('padding-left'),
21767 right: styleValue('padding-right'),
21768 top: styleValue('padding-top'),
21769 bottom: styleValue('padding-bottom')
21770 };
21771 var border = {
21772 left: styleValue('border-left-width'),
21773 right: styleValue('border-right-width'),
21774 top: styleValue('border-top-width'),
21775 bottom: styleValue('border-bottom-width')
21776 };
21777 var clientWidth = container.clientWidth;
21778 var clientHeight = container.clientHeight;
21779 var paddingHor = padding.left + padding.right;
21780 var paddingVer = padding.top + padding.bottom;
21781 var borderHor = border.left + border.right;
21782 var scale = rect.width / (clientWidth + borderHor);
21783 var unscaledW = clientWidth - paddingHor;
21784 var unscaledH = clientHeight - paddingVer;
21785 var left = rect.left + padding.left + border.left;
21786 var top = rect.top + padding.top + border.top;
21787 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21788};
21789BRp$e.invalidateContainerClientCoordsCache = function () {
21790 this.containerBB = null;
21791};
21792BRp$e.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21793 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21794};
21795BRp$e.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21796 var self = this;
21797 var r = this;
21798 var eles = r.getCachedZSortedEles();
21799 var near = []; // 1 node max, 1 edge max
21800 var zoom = r.cy.zoom();
21801 var hasCompounds = r.cy.hasCompoundNodes();
21802 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21803 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21804 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21805 var minSqDist = Infinity;
21806 var nearEdge;
21807 var nearNode;
21808 if (interactiveElementsOnly) {
21809 eles = eles.interactive;
21810 }
21811 function addEle(ele, sqDist) {
21812 if (ele.isNode()) {
21813 if (nearNode) {
21814 return; // can't replace node
21815 } else {
21816 nearNode = ele;
21817 near.push(ele);
21818 }
21819 }
21820 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21821 if (nearEdge) {
21822 // then replace existing edge
21823 // can replace only if same z-index
21824 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) {
21825 for (var i = 0; i < near.length; i++) {
21826 if (near[i].isEdge()) {
21827 near[i] = ele;
21828 nearEdge = ele;
21829 minSqDist = sqDist != null ? sqDist : minSqDist;
21830 break;
21831 }
21832 }
21833 }
21834 } else {
21835 near.push(ele);
21836 nearEdge = ele;
21837 minSqDist = sqDist != null ? sqDist : minSqDist;
21838 }
21839 }
21840 }
21841 function checkNode(node) {
21842 var width = node.outerWidth() + 2 * nodeThreshold;
21843 var height = node.outerHeight() + 2 * nodeThreshold;
21844 var hw = width / 2;
21845 var hh = height / 2;
21846 var pos = node.position();
21847 var cornerRadius = node.pstyle('corner-radius').value === 'auto' ? 'auto' : node.pstyle('corner-radius').pfValue;
21848 var rs = node._private.rscratch;
21849 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21850 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21851 ) {
21852 var shape = r.nodeShapes[self.getNodeShape(node)];
21853 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y, cornerRadius, rs)) {
21854 addEle(node, 0);
21855 return true;
21856 }
21857 }
21858 }
21859 function checkEdge(edge) {
21860 var _p = edge._private;
21861 var rs = _p.rscratch;
21862 var styleWidth = edge.pstyle('width').pfValue;
21863 var scale = edge.pstyle('arrow-scale').value;
21864 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21865 var widthSq = width * width;
21866 var width2 = width * 2;
21867 var src = _p.source;
21868 var tgt = _p.target;
21869 var sqDist;
21870 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21871 var pts = rs.allpts;
21872 for (var i = 0; i + 3 < pts.length; i += 2) {
21873 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]))) {
21874 addEle(edge, sqDist);
21875 return true;
21876 }
21877 }
21878 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21879 var pts = rs.allpts;
21880 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21881 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]))) {
21882 addEle(edge, sqDist);
21883 return true;
21884 }
21885 }
21886 }
21887
21888 // if we're close to the edge but didn't hit it, maybe we hit its arrows
21889
21890 var src = src || _p.source;
21891 var tgt = tgt || _p.target;
21892 var arSize = self.getArrowWidth(styleWidth, scale);
21893 var arrows = [{
21894 name: 'source',
21895 x: rs.arrowStartX,
21896 y: rs.arrowStartY,
21897 angle: rs.srcArrowAngle
21898 }, {
21899 name: 'target',
21900 x: rs.arrowEndX,
21901 y: rs.arrowEndY,
21902 angle: rs.tgtArrowAngle
21903 }, {
21904 name: 'mid-source',
21905 x: rs.midX,
21906 y: rs.midY,
21907 angle: rs.midsrcArrowAngle
21908 }, {
21909 name: 'mid-target',
21910 x: rs.midX,
21911 y: rs.midY,
21912 angle: rs.midtgtArrowAngle
21913 }];
21914 for (var i = 0; i < arrows.length; i++) {
21915 var ar = arrows[i];
21916 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21917 var edgeWidth = edge.pstyle('width').pfValue;
21918 if (shape.roughCollide(x, y, arSize, ar.angle, {
21919 x: ar.x,
21920 y: ar.y
21921 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21922 x: ar.x,
21923 y: ar.y
21924 }, edgeWidth, edgeThreshold)) {
21925 addEle(edge);
21926 return true;
21927 }
21928 }
21929
21930 // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21931 if (hasCompounds && near.length > 0) {
21932 checkNode(src);
21933 checkNode(tgt);
21934 }
21935 }
21936 function preprop(obj, name, pre) {
21937 return getPrefixedProperty(obj, name, pre);
21938 }
21939 function checkLabel(ele, prefix) {
21940 var _p = ele._private;
21941 var th = labelThreshold;
21942 var prefixDash;
21943 if (prefix) {
21944 prefixDash = prefix + '-';
21945 } else {
21946 prefixDash = '';
21947 }
21948 ele.boundingBox();
21949 var bb = _p.labelBounds[prefix || 'main'];
21950 var text = ele.pstyle(prefixDash + 'label').value;
21951 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21952 if (!eventsEnabled || !text) {
21953 return;
21954 }
21955 var lx = preprop(_p.rscratch, 'labelX', prefix);
21956 var ly = preprop(_p.rscratch, 'labelY', prefix);
21957 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21958 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21959 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21960 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21961 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21962 var ly1 = bb.y1 - th - oy;
21963 var ly2 = bb.y2 + th - oy;
21964 if (theta) {
21965 var cos = Math.cos(theta);
21966 var sin = Math.sin(theta);
21967 var rotate = function rotate(x, y) {
21968 x = x - lx;
21969 y = y - ly;
21970 return {
21971 x: x * cos - y * sin + lx,
21972 y: x * sin + y * cos + ly
21973 };
21974 };
21975 var px1y1 = rotate(lx1, ly1);
21976 var px1y2 = rotate(lx1, ly2);
21977 var px2y1 = rotate(lx2, ly1);
21978 var px2y2 = rotate(lx2, ly2);
21979 var points = [
21980 // with the margin added after the rotation is applied
21981 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21982 if (pointInsidePolygonPoints(x, y, points)) {
21983 addEle(ele);
21984 return true;
21985 }
21986 } else {
21987 // do a cheaper bb check
21988 if (inBoundingBox(bb, x, y)) {
21989 addEle(ele);
21990 return true;
21991 }
21992 }
21993 }
21994 for (var i = eles.length - 1; i >= 0; i--) {
21995 // reverse order for precedence
21996 var ele = eles[i];
21997 if (ele.isNode()) {
21998 checkNode(ele) || checkLabel(ele);
21999 } else {
22000 // then edge
22001 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
22002 }
22003 }
22004 return near;
22005};
22006
22007// 'Give me everything from this box'
22008BRp$e.getAllInBox = function (x1, y1, x2, y2) {
22009 var eles = this.getCachedZSortedEles().interactive;
22010 var box = [];
22011 var x1c = Math.min(x1, x2);
22012 var x2c = Math.max(x1, x2);
22013 var y1c = Math.min(y1, y2);
22014 var y2c = Math.max(y1, y2);
22015 x1 = x1c;
22016 x2 = x2c;
22017 y1 = y1c;
22018 y2 = y2c;
22019 var boxBb = makeBoundingBox({
22020 x1: x1,
22021 y1: y1,
22022 x2: x2,
22023 y2: y2
22024 });
22025 for (var e = 0; e < eles.length; e++) {
22026 var ele = eles[e];
22027 if (ele.isNode()) {
22028 var node = ele;
22029 var nodeBb = node.boundingBox({
22030 includeNodes: true,
22031 includeEdges: false,
22032 includeLabels: false
22033 });
22034 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
22035 box.push(node);
22036 }
22037 } else {
22038 var edge = ele;
22039 var _p = edge._private;
22040 var rs = _p.rscratch;
22041 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
22042 continue;
22043 }
22044 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
22045 continue;
22046 }
22047 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
22048 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
22049 var allInside = true;
22050 for (var i = 0; i < pts.length; i++) {
22051 if (!pointInBoundingBox(boxBb, pts[i])) {
22052 allInside = false;
22053 break;
22054 }
22055 }
22056 if (allInside) {
22057 box.push(edge);
22058 }
22059 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22060 box.push(edge);
22061 }
22062 }
22063 }
22064 return box;
22065};
22066
22067var BRp$d = {};
22068BRp$d.calculateArrowAngles = function (edge) {
22069 var rs = edge._private.rscratch;
22070 var isHaystack = rs.edgeType === 'haystack';
22071 var isBezier = rs.edgeType === 'bezier';
22072 var isMultibezier = rs.edgeType === 'multibezier';
22073 var isSegments = rs.edgeType === 'segments';
22074 var isCompound = rs.edgeType === 'compound';
22075 var isSelf = rs.edgeType === 'self';
22076
22077 // Displacement gives direction for arrowhead orientation
22078 var dispX, dispY;
22079 var startX, startY, endX, endY, midX, midY;
22080 if (isHaystack) {
22081 startX = rs.haystackPts[0];
22082 startY = rs.haystackPts[1];
22083 endX = rs.haystackPts[2];
22084 endY = rs.haystackPts[3];
22085 } else {
22086 startX = rs.arrowStartX;
22087 startY = rs.arrowStartY;
22088 endX = rs.arrowEndX;
22089 endY = rs.arrowEndY;
22090 }
22091 midX = rs.midX;
22092 midY = rs.midY;
22093
22094 // source
22095 //
22096
22097 if (isSegments) {
22098 dispX = startX - rs.segpts[0];
22099 dispY = startY - rs.segpts[1];
22100 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22101 var pts = rs.allpts;
22102 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22103 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22104 dispX = startX - bX;
22105 dispY = startY - bY;
22106 } else {
22107 dispX = startX - midX;
22108 dispY = startY - midY;
22109 }
22110 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY);
22111
22112 // mid target
22113 //
22114
22115 var midX = rs.midX;
22116 var midY = rs.midY;
22117 if (isHaystack) {
22118 midX = (startX + endX) / 2;
22119 midY = (startY + endY) / 2;
22120 }
22121 dispX = endX - startX;
22122 dispY = endY - startY;
22123 if (isSegments) {
22124 var pts = rs.allpts;
22125 if (pts.length / 2 % 2 === 0) {
22126 var i2 = pts.length / 2;
22127 var i1 = i2 - 2;
22128 dispX = pts[i2] - pts[i1];
22129 dispY = pts[i2 + 1] - pts[i1 + 1];
22130 } else if (rs.isRound) {
22131 dispX = rs.midVector[1];
22132 dispY = -rs.midVector[0];
22133 } else {
22134 var i2 = pts.length / 2 - 1;
22135 var i1 = i2 - 2;
22136 dispX = pts[i2] - pts[i1];
22137 dispY = pts[i2 + 1] - pts[i1 + 1];
22138 }
22139 } else if (isMultibezier || isCompound || isSelf) {
22140 var pts = rs.allpts;
22141 var cpts = rs.ctrlpts;
22142 var bp0x, bp0y;
22143 var bp1x, bp1y;
22144 if (cpts.length / 2 % 2 === 0) {
22145 var p0 = pts.length / 2 - 1; // startpt
22146 var ic = p0 + 2;
22147 var p1 = ic + 2;
22148 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22149 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22150 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22151 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22152 } else {
22153 var ic = pts.length / 2 - 1; // ctrpt
22154 var p0 = ic - 2; // startpt
22155 var p1 = ic + 2; // endpt
22156
22157 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22158 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22159 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22160 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22161 }
22162 dispX = bp1x - bp0x;
22163 dispY = bp1y - bp0y;
22164 }
22165 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22166 rs.midDispX = dispX;
22167 rs.midDispY = dispY;
22168
22169 // mid source
22170 //
22171
22172 dispX *= -1;
22173 dispY *= -1;
22174 if (isSegments) {
22175 var pts = rs.allpts;
22176 if (pts.length / 2 % 2 === 0) ; else if (!rs.isRound) {
22177 var i2 = pts.length / 2 - 1;
22178 var i3 = i2 + 2;
22179 dispX = -(pts[i3] - pts[i2]);
22180 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22181 }
22182 }
22183 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY);
22184
22185 // target
22186 //
22187
22188 if (isSegments) {
22189 dispX = endX - rs.segpts[rs.segpts.length - 2];
22190 dispY = endY - rs.segpts[rs.segpts.length - 1];
22191 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22192 var pts = rs.allpts;
22193 var l = pts.length;
22194 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22195 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22196 dispX = endX - bX;
22197 dispY = endY - bY;
22198 } else {
22199 dispX = endX - midX;
22200 dispY = endY - midY;
22201 }
22202 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22203};
22204BRp$d.getArrowWidth = BRp$d.getArrowHeight = function (edgeWidth, scale) {
22205 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22206 var cachedVal = cache[edgeWidth + ', ' + scale];
22207 if (cachedVal) {
22208 return cachedVal;
22209 }
22210 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22211 cache[edgeWidth + ', ' + scale] = cachedVal;
22212 return cachedVal;
22213};
22214
22215/**
22216 * Explained by Blindman67 at https://stackoverflow.com/a/44856925/11028828
22217 */
22218
22219// Declare reused variable to avoid reallocating variables every time the function is called
22220var x,
22221 y,
22222 v1 = {},
22223 v2 = {},
22224 sinA,
22225 sinA90,
22226 radDirection,
22227 drawDirection,
22228 angle,
22229 halfAngle,
22230 cRadius,
22231 lenOut,
22232 radius,
22233 limit;
22234var startX, startY, stopX, stopY;
22235var lastPoint;
22236
22237// convert 2 points into vector form, polar form, and normalised
22238var asVec = function asVec(p, pp, v) {
22239 v.x = pp.x - p.x;
22240 v.y = pp.y - p.y;
22241 v.len = Math.sqrt(v.x * v.x + v.y * v.y);
22242 v.nx = v.x / v.len;
22243 v.ny = v.y / v.len;
22244 v.ang = Math.atan2(v.ny, v.nx);
22245};
22246var invertVec = function invertVec(originalV, invertedV) {
22247 invertedV.x = originalV.x * -1;
22248 invertedV.y = originalV.y * -1;
22249 invertedV.nx = originalV.nx * -1;
22250 invertedV.ny = originalV.ny * -1;
22251 invertedV.ang = originalV.ang > 0 ? -(Math.PI - originalV.ang) : Math.PI + originalV.ang;
22252};
22253var calcCornerArc = function calcCornerArc(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius) {
22254 //-----------------------------------------
22255 // Part 1
22256 previousPoint !== lastPoint ? asVec(currentPoint, previousPoint, v1) : invertVec(v2, v1); // Avoid recalculating vec if it is the invert of the last one calculated
22257 asVec(currentPoint, nextPoint, v2);
22258 sinA = v1.nx * v2.ny - v1.ny * v2.nx;
22259 sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny;
22260 angle = Math.asin(Math.max(-1, Math.min(1, sinA)));
22261 if (Math.abs(angle) < 1e-6) {
22262 x = currentPoint.x;
22263 y = currentPoint.y;
22264 cRadius = radius = 0;
22265 return;
22266 }
22267 //-----------------------------------------
22268 radDirection = 1;
22269 drawDirection = false;
22270 if (sinA90 < 0) {
22271 if (angle < 0) {
22272 angle = Math.PI + angle;
22273 } else {
22274 angle = Math.PI - angle;
22275 radDirection = -1;
22276 drawDirection = true;
22277 }
22278 } else {
22279 if (angle > 0) {
22280 radDirection = -1;
22281 drawDirection = true;
22282 }
22283 }
22284 if (currentPoint.radius !== undefined) {
22285 radius = currentPoint.radius;
22286 } else {
22287 radius = radiusMax;
22288 }
22289 //-----------------------------------------
22290 // Part 2
22291 halfAngle = angle / 2;
22292 //-----------------------------------------
22293
22294 limit = Math.min(v1.len / 2, v2.len / 2);
22295 if (isArcRadius) {
22296 //-----------------------------------------
22297 // Part 3
22298 lenOut = Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle));
22299
22300 //-----------------------------------------
22301 // Special part A
22302 if (lenOut > limit) {
22303 lenOut = limit;
22304 cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
22305 } else {
22306 cRadius = radius;
22307 }
22308 } else {
22309 lenOut = Math.min(limit, radius);
22310 cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
22311 }
22312 //-----------------------------------------
22313
22314 //-----------------------------------------
22315 // Part 4
22316 stopX = currentPoint.x + v2.nx * lenOut;
22317 stopY = currentPoint.y + v2.ny * lenOut;
22318 //-----------------------------------------
22319 // Part 5
22320 x = stopX - v2.ny * cRadius * radDirection;
22321 y = stopY + v2.nx * cRadius * radDirection;
22322 //-----------------------------------------
22323 // Additional Part : calculate start point E
22324 startX = currentPoint.x + v1.nx * lenOut;
22325 startY = currentPoint.y + v1.ny * lenOut;
22326
22327 // Save last point to avoid recalculating vector when not needed
22328 lastPoint = currentPoint;
22329};
22330
22331/**
22332 * Draw corner provided by {@link getRoundCorner}
22333 *
22334 * @param ctx :CanvasRenderingContext2D
22335 * @param roundCorner {{cx:number, cy:number, radius:number, endAngle: number, startAngle: number, counterClockwise: boolean}}
22336 */
22337function drawPreparedRoundCorner(ctx, roundCorner) {
22338 if (roundCorner.radius === 0) ctx.lineTo(roundCorner.cx, roundCorner.cy);else ctx.arc(roundCorner.cx, roundCorner.cy, roundCorner.radius, roundCorner.startAngle, roundCorner.endAngle, roundCorner.counterClockwise);
22339}
22340
22341/**
22342 * Get round corner from a point and its previous and next neighbours in a path
22343 *
22344 * @param previousPoint {{x: number, y:number, radius: number?}}
22345 * @param currentPoint {{x: number, y:number, radius: number?}}
22346 * @param nextPoint {{x: number, y:number, radius: number?}}
22347 * @param radiusMax :number
22348 * @param isArcRadius :boolean
22349 * @return {{
22350 * cx:number, cy:number, radius:number,
22351 * startX:number, startY:number,
22352 * stopX:number, stopY: number,
22353 * endAngle: number, startAngle: number, counterClockwise: boolean
22354 * }}
22355 */
22356function getRoundCorner(previousPoint, currentPoint, nextPoint, radiusMax) {
22357 var isArcRadius = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
22358 if (radiusMax === 0 || currentPoint.radius === 0) return {
22359 cx: currentPoint.x,
22360 cy: currentPoint.y,
22361 radius: 0,
22362 startX: currentPoint.x,
22363 startY: currentPoint.y,
22364 stopX: currentPoint.x,
22365 stopY: currentPoint.y,
22366 startAngle: undefined,
22367 endAngle: undefined,
22368 counterClockwise: undefined
22369 };
22370 calcCornerArc(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius);
22371 return {
22372 cx: x,
22373 cy: y,
22374 radius: cRadius,
22375 startX: startX,
22376 startY: startY,
22377 stopX: stopX,
22378 stopY: stopY,
22379 startAngle: v1.ang + Math.PI / 2 * radDirection,
22380 endAngle: v2.ang - Math.PI / 2 * radDirection,
22381 counterClockwise: drawDirection
22382 };
22383}
22384
22385var BRp$c = {};
22386BRp$c.findMidptPtsEtc = function (edge, pairInfo) {
22387 var posPts = pairInfo.posPts,
22388 intersectionPts = pairInfo.intersectionPts,
22389 vectorNormInverse = pairInfo.vectorNormInverse;
22390 var midptPts;
22391
22392 // n.b. assumes all edges in bezier bundle have same endpoints specified
22393 var srcManEndpt = edge.pstyle('source-endpoint');
22394 var tgtManEndpt = edge.pstyle('target-endpoint');
22395 var haveManualEndPts = srcManEndpt.units != null && tgtManEndpt.units != null;
22396 var recalcVectorNormInverse = function recalcVectorNormInverse(x1, y1, x2, y2) {
22397 var dy = y2 - y1;
22398 var dx = x2 - x1;
22399 var l = Math.sqrt(dx * dx + dy * dy);
22400 return {
22401 x: -dy / l,
22402 y: dx / l
22403 };
22404 };
22405 var edgeDistances = edge.pstyle('edge-distances').value;
22406 switch (edgeDistances) {
22407 case 'node-position':
22408 midptPts = posPts;
22409 break;
22410 case 'intersection':
22411 midptPts = intersectionPts;
22412 break;
22413 case 'endpoints':
22414 {
22415 if (haveManualEndPts) {
22416 var _this$manualEndptToPx = this.manualEndptToPx(edge.source()[0], srcManEndpt),
22417 _this$manualEndptToPx2 = _slicedToArray(_this$manualEndptToPx, 2),
22418 x1 = _this$manualEndptToPx2[0],
22419 y1 = _this$manualEndptToPx2[1];
22420 var _this$manualEndptToPx3 = this.manualEndptToPx(edge.target()[0], tgtManEndpt),
22421 _this$manualEndptToPx4 = _slicedToArray(_this$manualEndptToPx3, 2),
22422 x2 = _this$manualEndptToPx4[0],
22423 y2 = _this$manualEndptToPx4[1];
22424 var endPts = {
22425 x1: x1,
22426 y1: y1,
22427 x2: x2,
22428 y2: y2
22429 };
22430 vectorNormInverse = recalcVectorNormInverse(x1, y1, x2, y2);
22431 midptPts = endPts;
22432 } else {
22433 warn("Edge ".concat(edge.id(), " has edge-distances:endpoints specified without manual endpoints specified via source-endpoint and target-endpoint. Falling back on edge-distances:intersection (default)."));
22434 midptPts = intersectionPts; // back to default
22435 }
22436
22437 break;
22438 }
22439 }
22440 return {
22441 midptPts: midptPts,
22442 vectorNormInverse: vectorNormInverse
22443 };
22444};
22445BRp$c.findHaystackPoints = function (edges) {
22446 for (var i = 0; i < edges.length; i++) {
22447 var edge = edges[i];
22448 var _p = edge._private;
22449 var rs = _p.rscratch;
22450 if (!rs.haystack) {
22451 var angle = Math.random() * 2 * Math.PI;
22452 rs.source = {
22453 x: Math.cos(angle),
22454 y: Math.sin(angle)
22455 };
22456 angle = Math.random() * 2 * Math.PI;
22457 rs.target = {
22458 x: Math.cos(angle),
22459 y: Math.sin(angle)
22460 };
22461 }
22462 var src = _p.source;
22463 var tgt = _p.target;
22464 var srcPos = src.position();
22465 var tgtPos = tgt.position();
22466 var srcW = src.width();
22467 var tgtW = tgt.width();
22468 var srcH = src.height();
22469 var tgtH = tgt.height();
22470 var radius = edge.pstyle('haystack-radius').value;
22471 var halfRadius = radius / 2; // b/c have to half width/height
22472
22473 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];
22474 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22475 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2;
22476
22477 // always override as haystack in case set to different type previously
22478 rs.edgeType = 'haystack';
22479 rs.haystack = true;
22480 this.storeEdgeProjections(edge);
22481 this.calculateArrowAngles(edge);
22482 this.recalculateEdgeLabelProjections(edge);
22483 this.calculateLabelAngles(edge);
22484 }
22485};
22486BRp$c.findSegmentsPoints = function (edge, pairInfo) {
22487 // Segments (multiple straight lines)
22488
22489 var rs = edge._private.rscratch;
22490 var segmentWs = edge.pstyle('segment-weights');
22491 var segmentDs = edge.pstyle('segment-distances');
22492 var segmentRs = edge.pstyle('segment-radii');
22493 var segmentTs = edge.pstyle('radius-type');
22494 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22495 var lastRadius = segmentRs.pfValue[segmentRs.pfValue.length - 1];
22496 var lastRadiusType = segmentTs.pfValue[segmentTs.pfValue.length - 1];
22497 rs.edgeType = 'segments';
22498 rs.segpts = [];
22499 rs.radii = [];
22500 rs.isArcRadius = [];
22501 for (var s = 0; s < segmentsN; s++) {
22502 var w = segmentWs.pfValue[s];
22503 var d = segmentDs.pfValue[s];
22504 var w1 = 1 - w;
22505 var w2 = w;
22506 var _this$findMidptPtsEtc = this.findMidptPtsEtc(edge, pairInfo),
22507 midptPts = _this$findMidptPtsEtc.midptPts,
22508 vectorNormInverse = _this$findMidptPtsEtc.vectorNormInverse;
22509 var adjustedMidpt = {
22510 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22511 y: midptPts.y1 * w1 + midptPts.y2 * w2
22512 };
22513 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22514 rs.radii.push(segmentRs.pfValue[s] !== undefined ? segmentRs.pfValue[s] : lastRadius);
22515 rs.isArcRadius.push((segmentTs.pfValue[s] !== undefined ? segmentTs.pfValue[s] : lastRadiusType) === 'arc-radius');
22516 }
22517};
22518BRp$c.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22519 // Self-edge
22520
22521 var rs = edge._private.rscratch;
22522 var dirCounts = pairInfo.dirCounts,
22523 srcPos = pairInfo.srcPos;
22524 var ctrlptDists = edge.pstyle('control-point-distances');
22525 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22526 var loopDir = edge.pstyle('loop-direction').pfValue;
22527 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22528 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22529 rs.edgeType = 'self';
22530 var j = i;
22531 var loopDist = stepSize;
22532 if (edgeIsUnbundled) {
22533 j = 0;
22534 loopDist = ctrlptDist;
22535 }
22536 var loopAngle = loopDir - Math.PI / 2;
22537 var outAngle = loopAngle - loopSwp / 2;
22538 var inAngle = loopAngle + loopSwp / 2;
22539
22540 // increase by step size for overlapping loops, keyed on direction and sweep values
22541 var dc = String(loopDir + '_' + loopSwp);
22542 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22543 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)];
22544};
22545BRp$c.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22546 // Compound edge
22547
22548 var rs = edge._private.rscratch;
22549 rs.edgeType = 'compound';
22550 var srcPos = pairInfo.srcPos,
22551 tgtPos = pairInfo.tgtPos,
22552 srcW = pairInfo.srcW,
22553 srcH = pairInfo.srcH,
22554 tgtW = pairInfo.tgtW,
22555 tgtH = pairInfo.tgtH;
22556 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22557 var ctrlptDists = edge.pstyle('control-point-distances');
22558 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22559 var j = i;
22560 var loopDist = stepSize;
22561 if (edgeIsUnbundled) {
22562 j = 0;
22563 loopDist = ctrlptDist;
22564 }
22565 var loopW = 50;
22566 var loopaPos = {
22567 x: srcPos.x - srcW / 2,
22568 y: srcPos.y - srcH / 2
22569 };
22570 var loopbPos = {
22571 x: tgtPos.x - tgtW / 2,
22572 y: tgtPos.y - tgtH / 2
22573 };
22574 var loopPos = {
22575 x: Math.min(loopaPos.x, loopbPos.x),
22576 y: Math.min(loopaPos.y, loopbPos.y)
22577 };
22578
22579 // avoids cases with impossible beziers
22580 var minCompoundStretch = 0.5;
22581 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22582 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22583 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];
22584};
22585BRp$c.findStraightEdgePoints = function (edge) {
22586 // Straight edge within bundle
22587
22588 edge._private.rscratch.edgeType = 'straight';
22589};
22590BRp$c.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22591 var rs = edge._private.rscratch;
22592 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22593 var ctrlptDists = edge.pstyle('control-point-distances');
22594 var ctrlptWs = edge.pstyle('control-point-weights');
22595 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22596 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22597 var ctrlptWeight = ctrlptWs.value[0];
22598
22599 // (Multi)bezier
22600
22601 var multi = edgeIsUnbundled;
22602 rs.edgeType = multi ? 'multibezier' : 'bezier';
22603 rs.ctrlpts = [];
22604 for (var b = 0; b < bezierN; b++) {
22605 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22606 var manctrlptDist = void 0;
22607 var sign = signum(normctrlptDist);
22608 if (multi) {
22609 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22610 ctrlptWeight = ctrlptWs.value[b];
22611 }
22612 if (edgeIsUnbundled) {
22613 // multi or single unbundled
22614 manctrlptDist = ctrlptDist;
22615 } else {
22616 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22617 }
22618 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22619 var w1 = 1 - ctrlptWeight;
22620 var w2 = ctrlptWeight;
22621 var _this$findMidptPtsEtc2 = this.findMidptPtsEtc(edge, pairInfo),
22622 midptPts = _this$findMidptPtsEtc2.midptPts,
22623 vectorNormInverse = _this$findMidptPtsEtc2.vectorNormInverse;
22624 var adjustedMidpt = {
22625 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22626 y: midptPts.y1 * w1 + midptPts.y2 * w2
22627 };
22628 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22629 }
22630};
22631BRp$c.findTaxiPoints = function (edge, pairInfo) {
22632 // Taxicab geometry with two turns maximum
22633
22634 var rs = edge._private.rscratch;
22635 rs.edgeType = 'segments';
22636 var VERTICAL = 'vertical';
22637 var HORIZONTAL = 'horizontal';
22638 var LEFTWARD = 'leftward';
22639 var RIGHTWARD = 'rightward';
22640 var DOWNWARD = 'downward';
22641 var UPWARD = 'upward';
22642 var AUTO = 'auto';
22643 var posPts = pairInfo.posPts,
22644 srcW = pairInfo.srcW,
22645 srcH = pairInfo.srcH,
22646 tgtW = pairInfo.tgtW,
22647 tgtH = pairInfo.tgtH;
22648 var edgeDistances = edge.pstyle('edge-distances').value;
22649 var dIncludesNodeBody = edgeDistances !== 'node-position';
22650 var taxiDir = edge.pstyle('taxi-direction').value;
22651 var rawTaxiDir = taxiDir; // unprocessed value
22652 var taxiTurn = edge.pstyle('taxi-turn');
22653 var turnIsPercent = taxiTurn.units === '%';
22654 var taxiTurnPfVal = taxiTurn.pfValue;
22655 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22656 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22657 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22658 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22659 var pdx = posPts.x2 - posPts.x1;
22660 var pdy = posPts.y2 - posPts.y1;
22661
22662 // take away the effective w/h from the magnitude of the delta value
22663 var subDWH = function subDWH(dxy, dwh) {
22664 if (dxy > 0) {
22665 return Math.max(dxy - dwh, 0);
22666 } else {
22667 return Math.min(dxy + dwh, 0);
22668 }
22669 };
22670 var dx = subDWH(pdx, dw);
22671 var dy = subDWH(pdy, dh);
22672 var isExplicitDir = false;
22673 if (rawTaxiDir === AUTO) {
22674 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22675 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22676 taxiDir = VERTICAL;
22677 isExplicitDir = true;
22678 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22679 taxiDir = HORIZONTAL;
22680 isExplicitDir = true;
22681 }
22682 var isVert = taxiDir === VERTICAL;
22683 var l = isVert ? dy : dx;
22684 var pl = isVert ? pdy : pdx;
22685 var sgnL = signum(pl);
22686 var forcedDir = false;
22687 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22688 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22689 sgnL *= -1;
22690 l = sgnL * Math.abs(l);
22691 forcedDir = true;
22692 }
22693 var d;
22694 if (turnIsPercent) {
22695 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22696 d = p * l;
22697 } else {
22698 var k = taxiTurnPfVal < 0 ? l : 0;
22699 d = k + taxiTurnPfVal * sgnL;
22700 }
22701 var getIsTooClose = function getIsTooClose(d) {
22702 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22703 };
22704 var isTooCloseSrc = getIsTooClose(d);
22705 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22706 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22707 if (isTooClose && !forcedDir) {
22708 // non-ideal routing
22709 if (isVert) {
22710 // vertical fallbacks
22711 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22712 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22713 if (lShapeInsideSrc) {
22714 // horizontal Z-shape (direction not respected)
22715 var x = (posPts.x1 + posPts.x2) / 2;
22716 var y1 = posPts.y1,
22717 y2 = posPts.y2;
22718 rs.segpts = [x, y1, x, y2];
22719 } else if (lShapeInsideTgt) {
22720 // vertical Z-shape (distance not respected)
22721 var y = (posPts.y1 + posPts.y2) / 2;
22722 var x1 = posPts.x1,
22723 x2 = posPts.x2;
22724 rs.segpts = [x1, y, x2, y];
22725 } else {
22726 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22727 rs.segpts = [posPts.x1, posPts.y2];
22728 }
22729 } else {
22730 // horizontal fallbacks
22731 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22732 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22733 if (_lShapeInsideSrc) {
22734 // vertical Z-shape (direction not respected)
22735 var _y = (posPts.y1 + posPts.y2) / 2;
22736 var _x = posPts.x1,
22737 _x2 = posPts.x2;
22738 rs.segpts = [_x, _y, _x2, _y];
22739 } else if (_lShapeInsideTgt) {
22740 // horizontal Z-shape (turn distance not respected)
22741 var _x3 = (posPts.x1 + posPts.x2) / 2;
22742 var _y2 = posPts.y1,
22743 _y3 = posPts.y2;
22744 rs.segpts = [_x3, _y2, _x3, _y3];
22745 } else {
22746 // L-shape (turn distance not respected, but works well for tree siblings)
22747 rs.segpts = [posPts.x2, posPts.y1];
22748 }
22749 }
22750 } else {
22751 // ideal routing
22752 if (isVert) {
22753 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22754 var _x4 = posPts.x1,
22755 _x5 = posPts.x2;
22756 rs.segpts = [_x4, _y4, _x5, _y4];
22757 } else {
22758 // horizontal
22759 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22760 var _y5 = posPts.y1,
22761 _y6 = posPts.y2;
22762 rs.segpts = [_x6, _y5, _x6, _y6];
22763 }
22764 }
22765 if (rs.isRound) {
22766 var radius = edge.pstyle('taxi-radius').value;
22767 var isArcRadius = edge.pstyle('radius-type').value[0] === 'arc-radius';
22768 rs.radii = new Array(rs.segpts.length / 2).fill(radius);
22769 rs.isArcRadius = new Array(rs.segpts.length / 2).fill(isArcRadius);
22770 }
22771};
22772BRp$c.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22773 var rs = edge._private.rscratch;
22774
22775 // can only correct beziers for now...
22776 if (rs.edgeType === 'bezier') {
22777 var srcPos = pairInfo.srcPos,
22778 tgtPos = pairInfo.tgtPos,
22779 srcW = pairInfo.srcW,
22780 srcH = pairInfo.srcH,
22781 tgtW = pairInfo.tgtW,
22782 tgtH = pairInfo.tgtH,
22783 srcShape = pairInfo.srcShape,
22784 tgtShape = pairInfo.tgtShape,
22785 srcCornerRadius = pairInfo.srcCornerRadius,
22786 tgtCornerRadius = pairInfo.tgtCornerRadius,
22787 srcRs = pairInfo.srcRs,
22788 tgtRs = pairInfo.tgtRs;
22789 var badStart = !number$1(rs.startX) || !number$1(rs.startY);
22790 var badAStart = !number$1(rs.arrowStartX) || !number$1(rs.arrowStartY);
22791 var badEnd = !number$1(rs.endX) || !number$1(rs.endY);
22792 var badAEnd = !number$1(rs.arrowEndX) || !number$1(rs.arrowEndY);
22793 var minCpADistFactor = 3;
22794 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22795 var minCpADist = minCpADistFactor * arrowW;
22796 var startACpDist = dist({
22797 x: rs.ctrlpts[0],
22798 y: rs.ctrlpts[1]
22799 }, {
22800 x: rs.startX,
22801 y: rs.startY
22802 });
22803 var closeStartACp = startACpDist < minCpADist;
22804 var endACpDist = dist({
22805 x: rs.ctrlpts[0],
22806 y: rs.ctrlpts[1]
22807 }, {
22808 x: rs.endX,
22809 y: rs.endY
22810 });
22811 var closeEndACp = endACpDist < minCpADist;
22812 var overlapping = false;
22813 if (badStart || badAStart || closeStartACp) {
22814 overlapping = true;
22815
22816 // project control point along line from src centre to outside the src shape
22817 // (otherwise intersection will yield nothing)
22818 var cpD = {
22819 // delta
22820 x: rs.ctrlpts[0] - srcPos.x,
22821 y: rs.ctrlpts[1] - srcPos.y
22822 };
22823 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22824 var cpM = {
22825 // normalised delta
22826 x: cpD.x / cpL,
22827 y: cpD.y / cpL
22828 };
22829 var radius = Math.max(srcW, srcH);
22830 var cpProj = {
22831 // *2 radius guarantees outside shape
22832 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22833 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22834 };
22835 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0, srcCornerRadius, srcRs);
22836 if (closeStartACp) {
22837 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22838 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22839 } else {
22840 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22841 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22842 }
22843 }
22844 if (badEnd || badAEnd || closeEndACp) {
22845 overlapping = true;
22846
22847 // project control point along line from tgt centre to outside the tgt shape
22848 // (otherwise intersection will yield nothing)
22849 var _cpD = {
22850 // delta
22851 x: rs.ctrlpts[0] - tgtPos.x,
22852 y: rs.ctrlpts[1] - tgtPos.y
22853 };
22854 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22855 var _cpM = {
22856 // normalised delta
22857 x: _cpD.x / _cpL,
22858 y: _cpD.y / _cpL
22859 };
22860 var _radius = Math.max(srcW, srcH);
22861 var _cpProj = {
22862 // *2 radius guarantees outside shape
22863 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22864 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22865 };
22866 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0, tgtCornerRadius, tgtRs);
22867 if (closeEndACp) {
22868 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22869 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22870 } else {
22871 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22872 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22873 }
22874 }
22875 if (overlapping) {
22876 // recalc endpts
22877 this.findEndpoints(edge);
22878 }
22879 }
22880};
22881BRp$c.storeAllpts = function (edge) {
22882 var rs = edge._private.rscratch;
22883 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22884 rs.allpts = [];
22885 rs.allpts.push(rs.startX, rs.startY);
22886 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22887 // ctrl pt itself
22888 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]);
22889
22890 // the midpt between ctrlpts as intermediate destination pts
22891 if (b + 3 < rs.ctrlpts.length) {
22892 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22893 }
22894 }
22895 rs.allpts.push(rs.endX, rs.endY);
22896 var m, mt;
22897 if (rs.ctrlpts.length / 2 % 2 === 0) {
22898 m = rs.allpts.length / 2 - 1;
22899 rs.midX = rs.allpts[m];
22900 rs.midY = rs.allpts[m + 1];
22901 } else {
22902 m = rs.allpts.length / 2 - 3;
22903 mt = 0.5;
22904 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22905 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22906 }
22907 } else if (rs.edgeType === 'straight') {
22908 // need to calc these after endpts
22909 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY];
22910
22911 // default midpt for labels etc
22912 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22913 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22914 } else if (rs.edgeType === 'segments') {
22915 rs.allpts = [];
22916 rs.allpts.push(rs.startX, rs.startY);
22917 rs.allpts.push.apply(rs.allpts, rs.segpts);
22918 rs.allpts.push(rs.endX, rs.endY);
22919 if (rs.isRound) {
22920 rs.roundCorners = [];
22921 for (var i = 2; i + 3 < rs.allpts.length; i += 2) {
22922 var radius = rs.radii[i / 2 - 1];
22923 var isArcRadius = rs.isArcRadius[i / 2 - 1];
22924 rs.roundCorners.push(getRoundCorner({
22925 x: rs.allpts[i - 2],
22926 y: rs.allpts[i - 1]
22927 }, {
22928 x: rs.allpts[i],
22929 y: rs.allpts[i + 1],
22930 radius: radius
22931 }, {
22932 x: rs.allpts[i + 2],
22933 y: rs.allpts[i + 3]
22934 }, radius, isArcRadius));
22935 }
22936 }
22937 if (rs.segpts.length % 4 === 0) {
22938 var i2 = rs.segpts.length / 2;
22939 var i1 = i2 - 2;
22940 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22941 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22942 } else {
22943 var _i = rs.segpts.length / 2 - 1;
22944 if (!rs.isRound) {
22945 rs.midX = rs.segpts[_i];
22946 rs.midY = rs.segpts[_i + 1];
22947 } else {
22948 var point = {
22949 x: rs.segpts[_i],
22950 y: rs.segpts[_i + 1]
22951 };
22952 var corner = rs.roundCorners[_i / 2];
22953 var v = [point.x - corner.cx, point.y - corner.cy];
22954 var factor = corner.radius / Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2));
22955 v = v.map(function (c) {
22956 return c * factor;
22957 });
22958 rs.midX = corner.cx + v[0];
22959 rs.midY = corner.cy + v[1];
22960 rs.midVector = v;
22961 }
22962 }
22963 }
22964};
22965BRp$c.checkForInvalidEdgeWarning = function (edge) {
22966 var rs = edge[0]._private.rscratch;
22967 if (rs.nodesOverlap || number$1(rs.startX) && number$1(rs.startY) && number$1(rs.endX) && number$1(rs.endY)) {
22968 rs.loggedErr = false;
22969 } else {
22970 if (!rs.loggedErr) {
22971 rs.loggedErr = true;
22972 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.');
22973 }
22974 }
22975};
22976BRp$c.findEdgeControlPoints = function (edges) {
22977 var _this = this;
22978 if (!edges || edges.length === 0) {
22979 return;
22980 }
22981 var r = this;
22982 var cy = r.cy;
22983 var hasCompounds = cy.hasCompoundNodes();
22984 var hashTable = {
22985 map: new Map$2(),
22986 get: function get(pairId) {
22987 var map2 = this.map.get(pairId[0]);
22988 if (map2 != null) {
22989 return map2.get(pairId[1]);
22990 } else {
22991 return null;
22992 }
22993 },
22994 set: function set(pairId, val) {
22995 var map2 = this.map.get(pairId[0]);
22996 if (map2 == null) {
22997 map2 = new Map$2();
22998 this.map.set(pairId[0], map2);
22999 }
23000 map2.set(pairId[1], val);
23001 }
23002 };
23003 var pairIds = [];
23004 var haystackEdges = [];
23005
23006 // create a table of edge (src, tgt) => list of edges between them
23007 for (var i = 0; i < edges.length; i++) {
23008 var edge = edges[i];
23009 var _p = edge._private;
23010 var curveStyle = edge.pstyle('curve-style').value;
23011
23012 // ignore edges who are not to be displayed
23013 // they shouldn't take up space
23014 if (edge.removed() || !edge.takesUpSpace()) {
23015 continue;
23016 }
23017 if (curveStyle === 'haystack') {
23018 haystackEdges.push(edge);
23019 continue;
23020 }
23021 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle.endsWith('segments') || curveStyle === 'straight' || curveStyle === 'straight-triangle' || curveStyle.endsWith('taxi');
23022 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
23023 var src = _p.source;
23024 var tgt = _p.target;
23025 var srcIndex = src.poolIndex();
23026 var tgtIndex = tgt.poolIndex();
23027 var pairId = [srcIndex, tgtIndex].sort();
23028 var tableEntry = hashTable.get(pairId);
23029 if (tableEntry == null) {
23030 tableEntry = {
23031 eles: []
23032 };
23033 hashTable.set(pairId, tableEntry);
23034 pairIds.push(pairId);
23035 }
23036 tableEntry.eles.push(edge);
23037 if (edgeIsUnbundled) {
23038 tableEntry.hasUnbundled = true;
23039 }
23040 if (edgeIsBezier) {
23041 tableEntry.hasBezier = true;
23042 }
23043 }
23044
23045 // for each pair (src, tgt), create the ctrl pts
23046 // Nested for loop is OK; total number of iterations for both loops = edgeCount
23047 var _loop = function _loop(p) {
23048 var pairId = pairIds[p];
23049 var pairInfo = hashTable.get(pairId);
23050 var swappedpairInfo = void 0;
23051 if (!pairInfo.hasUnbundled) {
23052 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
23053 return e.isBundledBezier();
23054 });
23055 clearArray(pairInfo.eles);
23056 pllEdges.forEach(function (edge) {
23057 return pairInfo.eles.push(edge);
23058 });
23059
23060 // for each pair id, the edges should be sorted by index
23061 pairInfo.eles.sort(function (edge1, edge2) {
23062 return edge1.poolIndex() - edge2.poolIndex();
23063 });
23064 }
23065 var firstEdge = pairInfo.eles[0];
23066 var src = firstEdge.source();
23067 var tgt = firstEdge.target();
23068
23069 // make sure src/tgt distinction is consistent w.r.t. pairId
23070 if (src.poolIndex() > tgt.poolIndex()) {
23071 var temp = src;
23072 src = tgt;
23073 tgt = temp;
23074 }
23075 var srcPos = pairInfo.srcPos = src.position();
23076 var tgtPos = pairInfo.tgtPos = tgt.position();
23077 var srcW = pairInfo.srcW = src.outerWidth();
23078 var srcH = pairInfo.srcH = src.outerHeight();
23079 var tgtW = pairInfo.tgtW = tgt.outerWidth();
23080 var tgtH = pairInfo.tgtH = tgt.outerHeight();
23081 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
23082 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
23083 var srcCornerRadius = pairInfo.srcCornerRadius = src.pstyle('corner-radius').value === 'auto' ? 'auto' : src.pstyle('corner-radius').pfValue;
23084 var tgtCornerRadius = pairInfo.tgtCornerRadius = tgt.pstyle('corner-radius').value === 'auto' ? 'auto' : tgt.pstyle('corner-radius').pfValue;
23085 var tgtRs = pairInfo.tgtRs = tgt._private.rscratch;
23086 var srcRs = pairInfo.srcRs = src._private.rscratch;
23087 pairInfo.dirCounts = {
23088 'north': 0,
23089 'west': 0,
23090 'south': 0,
23091 'east': 0,
23092 'northwest': 0,
23093 'southwest': 0,
23094 'northeast': 0,
23095 'southeast': 0
23096 };
23097 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
23098 var _edge = pairInfo.eles[_i2];
23099 var rs = _edge[0]._private.rscratch;
23100 var _curveStyle = _edge.pstyle('curve-style').value;
23101 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle.endsWith('segments') || _curveStyle.endsWith('taxi');
23102
23103 // whether the normalised pair order is the reverse of the edge's src-tgt order
23104 var edgeIsSwapped = !src.same(_edge.source());
23105 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
23106 pairInfo.calculatedIntersection = true;
23107
23108 // pt outside src shape to calc distance/displacement from src to tgt
23109 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0, srcCornerRadius, srcRs);
23110 var srcIntn = pairInfo.srcIntn = srcOutside;
23111
23112 // pt outside tgt shape to calc distance/displacement from src to tgt
23113 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0, tgtCornerRadius, tgtRs);
23114 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
23115 var intersectionPts = pairInfo.intersectionPts = {
23116 x1: srcOutside[0],
23117 x2: tgtOutside[0],
23118 y1: srcOutside[1],
23119 y2: tgtOutside[1]
23120 };
23121 var posPts = pairInfo.posPts = {
23122 x1: srcPos.x,
23123 x2: tgtPos.x,
23124 y1: srcPos.y,
23125 y2: tgtPos.y
23126 };
23127 var dy = tgtOutside[1] - srcOutside[1];
23128 var dx = tgtOutside[0] - srcOutside[0];
23129 var l = Math.sqrt(dx * dx + dy * dy);
23130 var vector = pairInfo.vector = {
23131 x: dx,
23132 y: dy
23133 };
23134 var vectorNorm = pairInfo.vectorNorm = {
23135 x: vector.x / l,
23136 y: vector.y / l
23137 };
23138 var vectorNormInverse = {
23139 x: -vectorNorm.y,
23140 y: vectorNorm.x
23141 };
23142
23143 // if node shapes overlap, then no ctrl pts to draw
23144 pairInfo.nodesOverlap = !number$1(l) || tgtShape.checkPoint(srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y, tgtCornerRadius, tgtRs) || srcShape.checkPoint(tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y, srcCornerRadius, srcRs);
23145 pairInfo.vectorNormInverse = vectorNormInverse;
23146 swappedpairInfo = {
23147 nodesOverlap: pairInfo.nodesOverlap,
23148 dirCounts: pairInfo.dirCounts,
23149 calculatedIntersection: true,
23150 hasBezier: pairInfo.hasBezier,
23151 hasUnbundled: pairInfo.hasUnbundled,
23152 eles: pairInfo.eles,
23153 srcPos: tgtPos,
23154 tgtPos: srcPos,
23155 srcW: tgtW,
23156 srcH: tgtH,
23157 tgtW: srcW,
23158 tgtH: srcH,
23159 srcIntn: tgtIntn,
23160 tgtIntn: srcIntn,
23161 srcShape: tgtShape,
23162 tgtShape: srcShape,
23163 posPts: {
23164 x1: posPts.x2,
23165 y1: posPts.y2,
23166 x2: posPts.x1,
23167 y2: posPts.y1
23168 },
23169 intersectionPts: {
23170 x1: intersectionPts.x2,
23171 y1: intersectionPts.y2,
23172 x2: intersectionPts.x1,
23173 y2: intersectionPts.y1
23174 },
23175 vector: {
23176 x: -vector.x,
23177 y: -vector.y
23178 },
23179 vectorNorm: {
23180 x: -vectorNorm.x,
23181 y: -vectorNorm.y
23182 },
23183 vectorNormInverse: {
23184 x: -vectorNormInverse.x,
23185 y: -vectorNormInverse.y
23186 }
23187 };
23188 }
23189 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
23190 rs.nodesOverlap = passedPairInfo.nodesOverlap;
23191 rs.srcIntn = passedPairInfo.srcIntn;
23192 rs.tgtIntn = passedPairInfo.tgtIntn;
23193 rs.isRound = _curveStyle.startsWith('round');
23194 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
23195 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23196 } else if (src === tgt) {
23197 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23198 } else if (_curveStyle.endsWith('segments')) {
23199 _this.findSegmentsPoints(_edge, passedPairInfo);
23200 } else if (_curveStyle.endsWith('taxi')) {
23201 _this.findTaxiPoints(_edge, passedPairInfo);
23202 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
23203 _this.findStraightEdgePoints(_edge);
23204 } else {
23205 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
23206 }
23207 _this.findEndpoints(_edge);
23208 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
23209 _this.checkForInvalidEdgeWarning(_edge);
23210 _this.storeAllpts(_edge);
23211 _this.storeEdgeProjections(_edge);
23212 _this.calculateArrowAngles(_edge);
23213 _this.recalculateEdgeLabelProjections(_edge);
23214 _this.calculateLabelAngles(_edge);
23215 } // for pair edges
23216 };
23217 for (var p = 0; p < pairIds.length; p++) {
23218 _loop(p);
23219 } // for pair ids
23220
23221 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
23222 this.findHaystackPoints(haystackEdges);
23223};
23224function getPts(pts) {
23225 var retPts = [];
23226 if (pts == null) {
23227 return;
23228 }
23229 for (var i = 0; i < pts.length; i += 2) {
23230 var x = pts[i];
23231 var y = pts[i + 1];
23232 retPts.push({
23233 x: x,
23234 y: y
23235 });
23236 }
23237 return retPts;
23238}
23239BRp$c.getSegmentPoints = function (edge) {
23240 var rs = edge[0]._private.rscratch;
23241 var type = rs.edgeType;
23242 if (type === 'segments') {
23243 this.recalculateRenderedStyle(edge);
23244 return getPts(rs.segpts);
23245 }
23246};
23247BRp$c.getControlPoints = function (edge) {
23248 var rs = edge[0]._private.rscratch;
23249 var type = rs.edgeType;
23250 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23251 this.recalculateRenderedStyle(edge);
23252 return getPts(rs.ctrlpts);
23253 }
23254};
23255BRp$c.getEdgeMidpoint = function (edge) {
23256 var rs = edge[0]._private.rscratch;
23257 this.recalculateRenderedStyle(edge);
23258 return {
23259 x: rs.midX,
23260 y: rs.midY
23261 };
23262};
23263
23264var BRp$b = {};
23265BRp$b.manualEndptToPx = function (node, prop) {
23266 var r = this;
23267 var npos = node.position();
23268 var w = node.outerWidth();
23269 var h = node.outerHeight();
23270 var rs = node._private.rscratch;
23271 if (prop.value.length === 2) {
23272 var p = [prop.pfValue[0], prop.pfValue[1]];
23273 if (prop.units[0] === '%') {
23274 p[0] = p[0] * w;
23275 }
23276 if (prop.units[1] === '%') {
23277 p[1] = p[1] * h;
23278 }
23279 p[0] += npos.x;
23280 p[1] += npos.y;
23281 return p;
23282 } else {
23283 var angle = prop.pfValue[0];
23284 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23285
23286 var l = 2 * Math.max(w, h);
23287 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23288 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0, node.pstyle('corner-radius').value === 'auto' ? 'auto' : node.pstyle('corner-radius').pfValue, rs);
23289 }
23290};
23291BRp$b.findEndpoints = function (edge) {
23292 var r = this;
23293 var intersect;
23294 var source = edge.source()[0];
23295 var target = edge.target()[0];
23296 var srcPos = source.position();
23297 var tgtPos = target.position();
23298 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23299 var srcArShape = edge.pstyle('source-arrow-shape').value;
23300 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23301 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23302 var srcRs = source._private.rscratch;
23303 var tgtRs = target._private.rscratch;
23304 var curveStyle = edge.pstyle('curve-style').value;
23305 var rs = edge._private.rscratch;
23306 var et = rs.edgeType;
23307 var taxi = curveStyle === 'taxi';
23308 var self = et === 'self' || et === 'compound';
23309 var bezier = et === 'bezier' || et === 'multibezier' || self;
23310 var multi = et !== 'bezier';
23311 var lines = et === 'straight' || et === 'segments';
23312 var segments = et === 'segments';
23313 var hasEndpts = bezier || multi || lines;
23314 var overrideEndpts = self || taxi;
23315 var srcManEndpt = edge.pstyle('source-endpoint');
23316 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23317 var srcCornerRadius = source.pstyle('corner-radius').value === 'auto' ? 'auto' : source.pstyle('corner-radius').pfValue;
23318 var tgtManEndpt = edge.pstyle('target-endpoint');
23319 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23320 var tgtCornerRadius = target.pstyle('corner-radius').value === 'auto' ? 'auto' : target.pstyle('corner-radius').pfValue;
23321 rs.srcManEndpt = srcManEndpt;
23322 rs.tgtManEndpt = tgtManEndpt;
23323 var p1; // last known point of edge on target side
23324 var p2; // last known point of edge on source side
23325
23326 var p1_i; // point to intersect with target shape
23327 var p2_i; // point to intersect with source shape
23328
23329 if (bezier) {
23330 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23331 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23332 p1 = cpEnd;
23333 p2 = cpStart;
23334 } else if (lines) {
23335 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23336 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23337 p1 = tgtArrowFromPt;
23338 p2 = srcArrowFromPt;
23339 }
23340 if (tgtManEndptVal === 'inside-to-node') {
23341 intersect = [tgtPos.x, tgtPos.y];
23342 } else if (tgtManEndpt.units) {
23343 intersect = this.manualEndptToPx(target, tgtManEndpt);
23344 } else if (tgtManEndptVal === 'outside-to-line') {
23345 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23346 } else {
23347 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23348 p1_i = p1;
23349 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23350 p1_i = [srcPos.x, srcPos.y];
23351 }
23352 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0, tgtCornerRadius, tgtRs);
23353 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23354 var trs = target._private.rscratch;
23355 var lw = trs.labelWidth;
23356 var lh = trs.labelHeight;
23357 var lx = trs.labelX;
23358 var ly = trs.labelY;
23359 var lw2 = lw / 2;
23360 var lh2 = lh / 2;
23361 var va = target.pstyle('text-valign').value;
23362 if (va === 'top') {
23363 ly -= lh2;
23364 } else if (va === 'bottom') {
23365 ly += lh2;
23366 }
23367 var ha = target.pstyle('text-halign').value;
23368 if (ha === 'left') {
23369 lx -= lw2;
23370 } else if (ha === 'right') {
23371 lx += lw2;
23372 }
23373 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);
23374 if (labelIntersect.length > 0) {
23375 var refPt = srcPos;
23376 var intSqdist = sqdist(refPt, array2point(intersect));
23377 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23378 var minSqDist = intSqdist;
23379 if (labIntSqdist < intSqdist) {
23380 intersect = labelIntersect;
23381 minSqDist = labIntSqdist;
23382 }
23383 if (labelIntersect.length > 2) {
23384 var labInt2SqDist = sqdist(refPt, {
23385 x: labelIntersect[2],
23386 y: labelIntersect[3]
23387 });
23388 if (labInt2SqDist < minSqDist) {
23389 intersect = [labelIntersect[2], labelIntersect[3]];
23390 }
23391 }
23392 }
23393 }
23394 }
23395 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23396 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23397 rs.endX = edgeEnd[0];
23398 rs.endY = edgeEnd[1];
23399 rs.arrowEndX = arrowEnd[0];
23400 rs.arrowEndY = arrowEnd[1];
23401 if (srcManEndptVal === 'inside-to-node') {
23402 intersect = [srcPos.x, srcPos.y];
23403 } else if (srcManEndpt.units) {
23404 intersect = this.manualEndptToPx(source, srcManEndpt);
23405 } else if (srcManEndptVal === 'outside-to-line') {
23406 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23407 } else {
23408 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23409 p2_i = p2;
23410 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23411 p2_i = [tgtPos.x, tgtPos.y];
23412 }
23413 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0, srcCornerRadius, srcRs);
23414 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23415 var srs = source._private.rscratch;
23416 var _lw = srs.labelWidth;
23417 var _lh = srs.labelHeight;
23418 var _lx = srs.labelX;
23419 var _ly = srs.labelY;
23420 var _lw2 = _lw / 2;
23421 var _lh2 = _lh / 2;
23422 var _va = source.pstyle('text-valign').value;
23423 if (_va === 'top') {
23424 _ly -= _lh2;
23425 } else if (_va === 'bottom') {
23426 _ly += _lh2;
23427 }
23428 var _ha = source.pstyle('text-halign').value;
23429 if (_ha === 'left') {
23430 _lx -= _lw2;
23431 } else if (_ha === 'right') {
23432 _lx += _lw2;
23433 }
23434 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);
23435 if (_labelIntersect.length > 0) {
23436 var _refPt = tgtPos;
23437 var _intSqdist = sqdist(_refPt, array2point(intersect));
23438 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23439 var _minSqDist = _intSqdist;
23440 if (_labIntSqdist < _intSqdist) {
23441 intersect = [_labelIntersect[0], _labelIntersect[1]];
23442 _minSqDist = _labIntSqdist;
23443 }
23444 if (_labelIntersect.length > 2) {
23445 var _labInt2SqDist = sqdist(_refPt, {
23446 x: _labelIntersect[2],
23447 y: _labelIntersect[3]
23448 });
23449 if (_labInt2SqDist < _minSqDist) {
23450 intersect = [_labelIntersect[2], _labelIntersect[3]];
23451 }
23452 }
23453 }
23454 }
23455 }
23456 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23457 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23458 rs.startX = edgeStart[0];
23459 rs.startY = edgeStart[1];
23460 rs.arrowStartX = arrowStart[0];
23461 rs.arrowStartY = arrowStart[1];
23462 if (hasEndpts) {
23463 if (!number$1(rs.startX) || !number$1(rs.startY) || !number$1(rs.endX) || !number$1(rs.endY)) {
23464 rs.badLine = true;
23465 } else {
23466 rs.badLine = false;
23467 }
23468 }
23469};
23470BRp$b.getSourceEndpoint = function (edge) {
23471 var rs = edge[0]._private.rscratch;
23472 this.recalculateRenderedStyle(edge);
23473 switch (rs.edgeType) {
23474 case 'haystack':
23475 return {
23476 x: rs.haystackPts[0],
23477 y: rs.haystackPts[1]
23478 };
23479 default:
23480 return {
23481 x: rs.arrowStartX,
23482 y: rs.arrowStartY
23483 };
23484 }
23485};
23486BRp$b.getTargetEndpoint = function (edge) {
23487 var rs = edge[0]._private.rscratch;
23488 this.recalculateRenderedStyle(edge);
23489 switch (rs.edgeType) {
23490 case 'haystack':
23491 return {
23492 x: rs.haystackPts[2],
23493 y: rs.haystackPts[3]
23494 };
23495 default:
23496 return {
23497 x: rs.arrowEndX,
23498 y: rs.arrowEndY
23499 };
23500 }
23501};
23502
23503var BRp$a = {};
23504function pushBezierPts(r, edge, pts) {
23505 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23506 return qbezierAt(p1, p2, p3, t);
23507 };
23508 var _p = edge._private;
23509 var bpts = _p.rstyle.bezierPts;
23510 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23511 var p = r.bezierProjPcts[i];
23512 bpts.push({
23513 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23514 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23515 });
23516 }
23517}
23518BRp$a.storeEdgeProjections = function (edge) {
23519 var _p = edge._private;
23520 var rs = _p.rscratch;
23521 var et = rs.edgeType;
23522
23523 // clear the cached points state
23524 _p.rstyle.bezierPts = null;
23525 _p.rstyle.linePts = null;
23526 _p.rstyle.haystackPts = null;
23527 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23528 _p.rstyle.bezierPts = [];
23529 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23530 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23531 }
23532 } else if (et === 'segments') {
23533 var lpts = _p.rstyle.linePts = [];
23534 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23535 lpts.push({
23536 x: rs.allpts[i],
23537 y: rs.allpts[i + 1]
23538 });
23539 }
23540 } else if (et === 'haystack') {
23541 var hpts = rs.haystackPts;
23542 _p.rstyle.haystackPts = [{
23543 x: hpts[0],
23544 y: hpts[1]
23545 }, {
23546 x: hpts[2],
23547 y: hpts[3]
23548 }];
23549 }
23550 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23551};
23552BRp$a.recalculateEdgeProjections = function (edges) {
23553 this.findEdgeControlPoints(edges);
23554};
23555
23556var BRp$9 = {};
23557BRp$9.recalculateNodeLabelProjection = function (node) {
23558 var content = node.pstyle('label').strValue;
23559 if (emptyString(content)) {
23560 return;
23561 }
23562 var textX, textY;
23563 var _p = node._private;
23564 var nodeWidth = node.width();
23565 var nodeHeight = node.height();
23566 var padding = node.padding();
23567 var nodePos = node.position();
23568 var textHalign = node.pstyle('text-halign').strValue;
23569 var textValign = node.pstyle('text-valign').strValue;
23570 var rs = _p.rscratch;
23571 var rstyle = _p.rstyle;
23572 switch (textHalign) {
23573 case 'left':
23574 textX = nodePos.x - nodeWidth / 2 - padding;
23575 break;
23576 case 'right':
23577 textX = nodePos.x + nodeWidth / 2 + padding;
23578 break;
23579 default:
23580 // e.g. center
23581 textX = nodePos.x;
23582 }
23583 switch (textValign) {
23584 case 'top':
23585 textY = nodePos.y - nodeHeight / 2 - padding;
23586 break;
23587 case 'bottom':
23588 textY = nodePos.y + nodeHeight / 2 + padding;
23589 break;
23590 default:
23591 // e.g. middle
23592 textY = nodePos.y;
23593 }
23594 rs.labelX = textX;
23595 rs.labelY = textY;
23596 rstyle.labelX = textX;
23597 rstyle.labelY = textY;
23598 this.calculateLabelAngles(node);
23599 this.applyLabelDimensions(node);
23600};
23601var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23602 var angle = Math.atan(dy / dx);
23603 if (dx === 0 && angle < 0) {
23604 angle = angle * -1;
23605 }
23606 return angle;
23607};
23608var lineAngle = function lineAngle(p0, p1) {
23609 var dx = p1.x - p0.x;
23610 var dy = p1.y - p0.y;
23611 return lineAngleFromDelta(dx, dy);
23612};
23613var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23614 var t0 = bound(0, t - 0.001, 1);
23615 var t1 = bound(0, t + 0.001, 1);
23616 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23617 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23618 return lineAngle(lp0, lp1);
23619};
23620BRp$9.recalculateEdgeLabelProjections = function (edge) {
23621 var p;
23622 var _p = edge._private;
23623 var rs = _p.rscratch;
23624 var r = this;
23625 var content = {
23626 mid: edge.pstyle('label').strValue,
23627 source: edge.pstyle('source-label').strValue,
23628 target: edge.pstyle('target-label').strValue
23629 };
23630 if (content.mid || content.source || content.target) ; else {
23631 return; // no labels => no calcs
23632 }
23633
23634 // add center point to style so bounding box calculations can use it
23635 //
23636 p = {
23637 x: rs.midX,
23638 y: rs.midY
23639 };
23640 var setRs = function setRs(propName, prefix, value) {
23641 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23642 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23643 };
23644 setRs('labelX', null, p.x);
23645 setRs('labelY', null, p.y);
23646 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23647 setRs('labelAutoAngle', null, midAngle);
23648 var createControlPointInfo = function createControlPointInfo() {
23649 if (createControlPointInfo.cache) {
23650 return createControlPointInfo.cache;
23651 } // use cache so only 1x per edge
23652
23653 var ctrlpts = [];
23654
23655 // store each ctrlpt info init
23656 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23657 var p0 = {
23658 x: rs.allpts[i],
23659 y: rs.allpts[i + 1]
23660 };
23661 var p1 = {
23662 x: rs.allpts[i + 2],
23663 y: rs.allpts[i + 3]
23664 }; // ctrlpt
23665 var p2 = {
23666 x: rs.allpts[i + 4],
23667 y: rs.allpts[i + 5]
23668 };
23669 ctrlpts.push({
23670 p0: p0,
23671 p1: p1,
23672 p2: p2,
23673 startDist: 0,
23674 length: 0,
23675 segments: []
23676 });
23677 }
23678 var bpts = _p.rstyle.bezierPts;
23679 var nProjs = r.bezierProjPcts.length;
23680 function addSegment(cp, p0, p1, t0, t1) {
23681 var length = dist(p0, p1);
23682 var prevSegment = cp.segments[cp.segments.length - 1];
23683 var segment = {
23684 p0: p0,
23685 p1: p1,
23686 t0: t0,
23687 t1: t1,
23688 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23689 length: length
23690 };
23691 cp.segments.push(segment);
23692 cp.length += length;
23693 }
23694
23695 // update each ctrlpt with segment info
23696 for (var _i = 0; _i < ctrlpts.length; _i++) {
23697 var cp = ctrlpts[_i];
23698 var prevCp = ctrlpts[_i - 1];
23699 if (prevCp) {
23700 cp.startDist = prevCp.startDist + prevCp.length;
23701 }
23702 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23703
23704 for (var j = 0; j < nProjs - 1; j++) {
23705 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23706 }
23707 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23708 }
23709
23710 return createControlPointInfo.cache = ctrlpts;
23711 };
23712 var calculateEndProjection = function calculateEndProjection(prefix) {
23713 var angle;
23714 var isSrc = prefix === 'source';
23715 if (!content[prefix]) {
23716 return;
23717 }
23718 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23719 switch (rs.edgeType) {
23720 case 'self':
23721 case 'compound':
23722 case 'bezier':
23723 case 'multibezier':
23724 {
23725 var cps = createControlPointInfo();
23726 var selected;
23727 var startDist = 0;
23728 var totalDist = 0;
23729
23730 // find the segment we're on
23731 for (var i = 0; i < cps.length; i++) {
23732 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23733 for (var j = 0; j < _cp.segments.length; j++) {
23734 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23735 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23736 startDist = totalDist;
23737 totalDist += _seg.length;
23738 if (totalDist >= offset || lastSeg) {
23739 selected = {
23740 cp: _cp,
23741 segment: _seg
23742 };
23743 break;
23744 }
23745 }
23746 if (selected) {
23747 break;
23748 }
23749 }
23750 var cp = selected.cp;
23751 var seg = selected.segment;
23752 var tSegment = (offset - startDist) / seg.length;
23753 var segDt = seg.t1 - seg.t0;
23754 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23755 t = bound(0, t, 1);
23756 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23757 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23758 break;
23759 }
23760 case 'straight':
23761 case 'segments':
23762 case 'haystack':
23763 {
23764 var d = 0,
23765 di,
23766 d0;
23767 var p0, p1;
23768 var l = rs.allpts.length;
23769 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23770 if (isSrc) {
23771 p0 = {
23772 x: rs.allpts[_i2],
23773 y: rs.allpts[_i2 + 1]
23774 };
23775 p1 = {
23776 x: rs.allpts[_i2 + 2],
23777 y: rs.allpts[_i2 + 3]
23778 };
23779 } else {
23780 p0 = {
23781 x: rs.allpts[l - 2 - _i2],
23782 y: rs.allpts[l - 1 - _i2]
23783 };
23784 p1 = {
23785 x: rs.allpts[l - 4 - _i2],
23786 y: rs.allpts[l - 3 - _i2]
23787 };
23788 }
23789 di = dist(p0, p1);
23790 d0 = d;
23791 d += di;
23792 if (d >= offset) {
23793 break;
23794 }
23795 }
23796 var pD = offset - d0;
23797 var _t = pD / di;
23798 _t = bound(0, _t, 1);
23799 p = lineAt(p0, p1, _t);
23800 angle = lineAngle(p0, p1);
23801 break;
23802 }
23803 }
23804 setRs('labelX', prefix, p.x);
23805 setRs('labelY', prefix, p.y);
23806 setRs('labelAutoAngle', prefix, angle);
23807 };
23808 calculateEndProjection('source');
23809 calculateEndProjection('target');
23810 this.applyLabelDimensions(edge);
23811};
23812BRp$9.applyLabelDimensions = function (ele) {
23813 this.applyPrefixedLabelDimensions(ele);
23814 if (ele.isEdge()) {
23815 this.applyPrefixedLabelDimensions(ele, 'source');
23816 this.applyPrefixedLabelDimensions(ele, 'target');
23817 }
23818};
23819BRp$9.applyPrefixedLabelDimensions = function (ele, prefix) {
23820 var _p = ele._private;
23821 var text = this.getLabelText(ele, prefix);
23822 var labelDims = this.calculateLabelDimensions(ele, text);
23823 var lineHeight = ele.pstyle('line-height').pfValue;
23824 var textWrap = ele.pstyle('text-wrap').strValue;
23825 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23826 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23827 var normPerLineHeight = labelDims.height / numLines;
23828 var labelLineHeight = normPerLineHeight * lineHeight;
23829 var width = labelDims.width;
23830 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23831 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23832 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23833 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23834 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23835 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23836};
23837BRp$9.getLabelText = function (ele, prefix) {
23838 var _p = ele._private;
23839 var pfd = prefix ? prefix + '-' : '';
23840 var text = ele.pstyle(pfd + 'label').strValue;
23841 var textTransform = ele.pstyle('text-transform').value;
23842 var rscratch = function rscratch(propName, value) {
23843 if (value) {
23844 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23845 return value;
23846 } else {
23847 return getPrefixedProperty(_p.rscratch, propName, prefix);
23848 }
23849 };
23850
23851 // for empty text, skip all processing
23852 if (!text) {
23853 return '';
23854 }
23855 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23856 text = text.toUpperCase();
23857 } else if (textTransform == 'lowercase') {
23858 text = text.toLowerCase();
23859 }
23860 var wrapStyle = ele.pstyle('text-wrap').value;
23861 if (wrapStyle === 'wrap') {
23862 var labelKey = rscratch('labelKey');
23863
23864 // save recalc if the label is the same as before
23865 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23866 return rscratch('labelWrapCachedText');
23867 }
23868 var zwsp = "\u200B";
23869 var lines = text.split('\n');
23870 var maxW = ele.pstyle('text-max-width').pfValue;
23871 var overflow = ele.pstyle('text-overflow-wrap').value;
23872 var overflowAny = overflow === 'anywhere';
23873 var wrappedLines = [];
23874 var separatorRegex = /[\s\u200b]+|$/g; // Include end of string to add last word
23875
23876 for (var l = 0; l < lines.length; l++) {
23877 var line = lines[l];
23878 var lineDims = this.calculateLabelDimensions(ele, line);
23879 var lineW = lineDims.width;
23880 if (overflowAny) {
23881 var processedLine = line.split('').join(zwsp);
23882 line = processedLine;
23883 }
23884 if (lineW > maxW) {
23885 // line is too long
23886 var separatorMatches = line.matchAll(separatorRegex);
23887 var subline = '';
23888 var previousIndex = 0;
23889 // Add fake match
23890 var _iterator = _createForOfIteratorHelper(separatorMatches),
23891 _step;
23892 try {
23893 for (_iterator.s(); !(_step = _iterator.n()).done;) {
23894 var separatorMatch = _step.value;
23895 var wordSeparator = separatorMatch[0];
23896 var word = line.substring(previousIndex, separatorMatch.index);
23897 previousIndex = separatorMatch.index + wordSeparator.length;
23898 var testLine = subline.length === 0 ? word : subline + word + wordSeparator;
23899 var testDims = this.calculateLabelDimensions(ele, testLine);
23900 var testW = testDims.width;
23901 if (testW <= maxW) {
23902 // word fits on current line
23903 subline += word + wordSeparator;
23904 } else {
23905 // word starts new line
23906 if (subline) {
23907 wrappedLines.push(subline);
23908 }
23909 subline = word + wordSeparator;
23910 }
23911 }
23912
23913 // if there's remaining text, put it in a wrapped line
23914 } catch (err) {
23915 _iterator.e(err);
23916 } finally {
23917 _iterator.f();
23918 }
23919 if (!subline.match(/^[\s\u200b]+$/)) {
23920 wrappedLines.push(subline);
23921 }
23922 } else {
23923 // line is already short enough
23924 wrappedLines.push(line);
23925 }
23926 } // for
23927
23928 rscratch('labelWrapCachedLines', wrappedLines);
23929 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23930 rscratch('labelWrapKey', labelKey);
23931 } else if (wrapStyle === 'ellipsis') {
23932 var _maxW = ele.pstyle('text-max-width').pfValue;
23933 var ellipsized = '';
23934 var ellipsis = "\u2026";
23935 var incLastCh = false;
23936 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23937 // the label already fits
23938 return text;
23939 }
23940 for (var i = 0; i < text.length; i++) {
23941 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23942 if (widthWithNextCh > _maxW) {
23943 break;
23944 }
23945 ellipsized += text[i];
23946 if (i === text.length - 1) {
23947 incLastCh = true;
23948 }
23949 }
23950 if (!incLastCh) {
23951 ellipsized += ellipsis;
23952 }
23953 return ellipsized;
23954 } // if ellipsize
23955
23956 return text;
23957};
23958BRp$9.getLabelJustification = function (ele) {
23959 var justification = ele.pstyle('text-justification').strValue;
23960 var textHalign = ele.pstyle('text-halign').strValue;
23961 if (justification === 'auto') {
23962 if (ele.isNode()) {
23963 switch (textHalign) {
23964 case 'left':
23965 return 'right';
23966 case 'right':
23967 return 'left';
23968 default:
23969 return 'center';
23970 }
23971 } else {
23972 return 'center';
23973 }
23974 } else {
23975 return justification;
23976 }
23977};
23978BRp$9.calculateLabelDimensions = function (ele, text) {
23979 var r = this;
23980 var containerWindow = r.cy.window();
23981 var document = containerWindow.document;
23982 var cacheKey = hashString(text, ele._private.labelDimsKey);
23983 var cache = r.labelDimCache || (r.labelDimCache = []);
23984 var existingVal = cache[cacheKey];
23985 if (existingVal != null) {
23986 return existingVal;
23987 }
23988 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23989 var fStyle = ele.pstyle('font-style').strValue;
23990 var size = ele.pstyle('font-size').pfValue;
23991 var family = ele.pstyle('font-family').strValue;
23992 var weight = ele.pstyle('font-weight').strValue;
23993 var canvas = this.labelCalcCanvas;
23994 var c2d = this.labelCalcCanvasContext;
23995 if (!canvas) {
23996 canvas = this.labelCalcCanvas = document.createElement('canvas');
23997 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23998 var ds = canvas.style;
23999 ds.position = 'absolute';
24000 ds.left = '-9999px';
24001 ds.top = '-9999px';
24002 ds.zIndex = '-1';
24003 ds.visibility = 'hidden';
24004 ds.pointerEvents = 'none';
24005 }
24006 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
24007 var width = 0;
24008 var height = 0;
24009 var lines = text.split('\n');
24010 for (var i = 0; i < lines.length; i++) {
24011 var line = lines[i];
24012 var metrics = c2d.measureText(line);
24013 var w = Math.ceil(metrics.width);
24014 var h = size;
24015 width = Math.max(w, width);
24016 height += h;
24017 }
24018 width += padding;
24019 height += padding;
24020 return cache[cacheKey] = {
24021 width: width,
24022 height: height
24023 };
24024};
24025BRp$9.calculateLabelAngle = function (ele, prefix) {
24026 var _p = ele._private;
24027 var rs = _p.rscratch;
24028 var isEdge = ele.isEdge();
24029 var prefixDash = prefix ? prefix + '-' : '';
24030 var rot = ele.pstyle(prefixDash + 'text-rotation');
24031 var rotStr = rot.strValue;
24032 if (rotStr === 'none') {
24033 return 0;
24034 } else if (isEdge && rotStr === 'autorotate') {
24035 return rs.labelAutoAngle;
24036 } else if (rotStr === 'autorotate') {
24037 return 0;
24038 } else {
24039 return rot.pfValue;
24040 }
24041};
24042BRp$9.calculateLabelAngles = function (ele) {
24043 var r = this;
24044 var isEdge = ele.isEdge();
24045 var _p = ele._private;
24046 var rs = _p.rscratch;
24047 rs.labelAngle = r.calculateLabelAngle(ele);
24048 if (isEdge) {
24049 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
24050 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
24051 }
24052};
24053
24054var BRp$8 = {};
24055var TOO_SMALL_CUT_RECT = 28;
24056var warnedCutRect = false;
24057BRp$8.getNodeShape = function (node) {
24058 var r = this;
24059 var shape = node.pstyle('shape').value;
24060 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
24061 if (!warnedCutRect) {
24062 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
24063 warnedCutRect = true;
24064 }
24065 return 'rectangle';
24066 }
24067 if (node.isParent()) {
24068 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
24069 return shape;
24070 } else {
24071 return 'rectangle';
24072 }
24073 }
24074 if (shape === 'polygon') {
24075 var points = node.pstyle('shape-polygon-points').value;
24076 return r.nodeShapes.makePolygon(points).name;
24077 }
24078 return shape;
24079};
24080
24081var BRp$7 = {};
24082BRp$7.registerCalculationListeners = function () {
24083 var cy = this.cy;
24084 var elesToUpdate = cy.collection();
24085 var r = this;
24086 var enqueue = function enqueue(eles) {
24087 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
24088 elesToUpdate.merge(eles);
24089 if (dirtyStyleCaches) {
24090 for (var i = 0; i < eles.length; i++) {
24091 var ele = eles[i];
24092 var _p = ele._private;
24093 var rstyle = _p.rstyle;
24094 rstyle.clean = false;
24095 rstyle.cleanConnected = false;
24096 }
24097 }
24098 };
24099 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
24100 var ele = e.target;
24101 enqueue(ele);
24102 }).on('style.* background.*', function onDirtyStyle(e) {
24103 var ele = e.target;
24104 enqueue(ele, false);
24105 });
24106 var updateEleCalcs = function updateEleCalcs(willDraw) {
24107 if (willDraw) {
24108 var fns = r.onUpdateEleCalcsFns;
24109
24110 // because we need to have up-to-date style (e.g. stylesheet mappers)
24111 // before calculating rendered style (and pstyle might not be called yet)
24112 elesToUpdate.cleanStyle();
24113 for (var i = 0; i < elesToUpdate.length; i++) {
24114 var ele = elesToUpdate[i];
24115 var rstyle = ele._private.rstyle;
24116 if (ele.isNode() && !rstyle.cleanConnected) {
24117 enqueue(ele.connectedEdges());
24118 rstyle.cleanConnected = true;
24119 }
24120 }
24121 if (fns) {
24122 for (var _i = 0; _i < fns.length; _i++) {
24123 var fn = fns[_i];
24124 fn(willDraw, elesToUpdate);
24125 }
24126 }
24127 r.recalculateRenderedStyle(elesToUpdate);
24128 elesToUpdate = cy.collection();
24129 }
24130 };
24131 r.flushRenderedStyleQueue = function () {
24132 updateEleCalcs(true);
24133 };
24134 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24135};
24136BRp$7.onUpdateEleCalcs = function (fn) {
24137 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24138 fns.push(fn);
24139};
24140BRp$7.recalculateRenderedStyle = function (eles, useCache) {
24141 var isCleanConnected = function isCleanConnected(ele) {
24142 return ele._private.rstyle.cleanConnected;
24143 };
24144 var edges = [];
24145 var nodes = [];
24146
24147 // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24148 if (this.destroyed) {
24149 return;
24150 }
24151
24152 // use cache by default for perf
24153 if (useCache === undefined) {
24154 useCache = true;
24155 }
24156 for (var i = 0; i < eles.length; i++) {
24157 var ele = eles[i];
24158 var _p = ele._private;
24159 var rstyle = _p.rstyle;
24160
24161 // an edge may be implicitly dirty b/c of one of its connected nodes
24162 // (and a request for recalc may come in between frames)
24163 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24164 rstyle.clean = false;
24165 }
24166
24167 // only update if dirty and in graph
24168 if (useCache && rstyle.clean || ele.removed()) {
24169 continue;
24170 }
24171
24172 // only update if not display: none
24173 if (ele.pstyle('display').value === 'none') {
24174 continue;
24175 }
24176 if (_p.group === 'nodes') {
24177 nodes.push(ele);
24178 } else {
24179 // edges
24180 edges.push(ele);
24181 }
24182 rstyle.clean = true;
24183 }
24184
24185 // update node data from projections
24186 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24187 var _ele = nodes[_i2];
24188 var _p2 = _ele._private;
24189 var _rstyle = _p2.rstyle;
24190 var pos = _ele.position();
24191 this.recalculateNodeLabelProjection(_ele);
24192 _rstyle.nodeX = pos.x;
24193 _rstyle.nodeY = pos.y;
24194 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24195 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24196 }
24197 this.recalculateEdgeProjections(edges);
24198
24199 // update edge data from projections
24200 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24201 var _ele2 = edges[_i3];
24202 var _p3 = _ele2._private;
24203 var _rstyle2 = _p3.rstyle;
24204 var rs = _p3.rscratch;
24205
24206 // update rstyle positions
24207 _rstyle2.srcX = rs.arrowStartX;
24208 _rstyle2.srcY = rs.arrowStartY;
24209 _rstyle2.tgtX = rs.arrowEndX;
24210 _rstyle2.tgtY = rs.arrowEndY;
24211 _rstyle2.midX = rs.midX;
24212 _rstyle2.midY = rs.midY;
24213 _rstyle2.labelAngle = rs.labelAngle;
24214 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24215 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24216 }
24217};
24218
24219var BRp$6 = {};
24220BRp$6.updateCachedGrabbedEles = function () {
24221 var eles = this.cachedZSortedEles;
24222 if (!eles) {
24223 // just let this be recalculated on the next z sort tick
24224 return;
24225 }
24226 eles.drag = [];
24227 eles.nondrag = [];
24228 var grabTargets = [];
24229 for (var i = 0; i < eles.length; i++) {
24230 var ele = eles[i];
24231 var rs = ele._private.rscratch;
24232 if (ele.grabbed() && !ele.isParent()) {
24233 grabTargets.push(ele);
24234 } else if (rs.inDragLayer) {
24235 eles.drag.push(ele);
24236 } else {
24237 eles.nondrag.push(ele);
24238 }
24239 }
24240
24241 // put the grab target nodes last so it's on top of its neighbourhood
24242 for (var i = 0; i < grabTargets.length; i++) {
24243 var ele = grabTargets[i];
24244 eles.drag.push(ele);
24245 }
24246};
24247BRp$6.invalidateCachedZSortedEles = function () {
24248 this.cachedZSortedEles = null;
24249};
24250BRp$6.getCachedZSortedEles = function (forceRecalc) {
24251 if (forceRecalc || !this.cachedZSortedEles) {
24252 var eles = this.cy.mutableElements().toArray();
24253 eles.sort(zIndexSort);
24254 eles.interactive = eles.filter(function (ele) {
24255 return ele.interactive();
24256 });
24257 this.cachedZSortedEles = eles;
24258 this.updateCachedGrabbedEles();
24259 } else {
24260 eles = this.cachedZSortedEles;
24261 }
24262 return eles;
24263};
24264
24265var BRp$5 = {};
24266[BRp$e, BRp$d, BRp$c, BRp$b, BRp$a, BRp$9, BRp$8, BRp$7, BRp$6].forEach(function (props) {
24267 extend(BRp$5, props);
24268});
24269
24270var BRp$4 = {};
24271BRp$4.getCachedImage = function (url, crossOrigin, onLoad) {
24272 var r = this;
24273 var imageCache = r.imageCache = r.imageCache || {};
24274 var cache = imageCache[url];
24275 if (cache) {
24276 if (!cache.image.complete) {
24277 cache.image.addEventListener('load', onLoad);
24278 }
24279 return cache.image;
24280 } else {
24281 cache = imageCache[url] = imageCache[url] || {};
24282 var image = cache.image = new Image(); // eslint-disable-line no-undef
24283
24284 image.addEventListener('load', onLoad);
24285 image.addEventListener('error', function () {
24286 image.error = true;
24287 });
24288
24289 // #1582 safari doesn't load data uris with crossOrigin properly
24290 // https://bugs.webkit.org/show_bug.cgi?id=123978
24291 var dataUriPrefix = 'data:';
24292 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24293 if (!isDataUri) {
24294 // if crossorigin is 'null'(stringified), then manually set it to null
24295 crossOrigin = crossOrigin === 'null' ? null : crossOrigin;
24296 image.crossOrigin = crossOrigin; // prevent tainted canvas
24297 }
24298
24299 image.src = url;
24300 return image;
24301 }
24302};
24303
24304var BRp$3 = {};
24305
24306/* global document, ResizeObserver, MutationObserver */
24307
24308BRp$3.registerBinding = function (target, event, handler, useCapture) {
24309 // eslint-disable-line no-unused-vars
24310 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24311 var b = this.binder(target);
24312 return b.on.apply(b, args);
24313};
24314BRp$3.binder = function (tgt) {
24315 var r = this;
24316 var containerWindow = r.cy.window();
24317 var tgtIsDom = tgt === containerWindow || tgt === containerWindow.document || tgt === containerWindow.document.body || domElement(tgt);
24318 if (r.supportsPassiveEvents == null) {
24319 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24320 var supportsPassive = false;
24321 try {
24322 var opts = Object.defineProperty({}, 'passive', {
24323 get: function get() {
24324 supportsPassive = true;
24325 return true;
24326 }
24327 });
24328 containerWindow.addEventListener('test', null, opts);
24329 } catch (err) {
24330 // not supported
24331 }
24332 r.supportsPassiveEvents = supportsPassive;
24333 }
24334 var on = function on(event, handler, useCapture) {
24335 var args = Array.prototype.slice.call(arguments);
24336 if (tgtIsDom && r.supportsPassiveEvents) {
24337 // replace useCapture w/ opts obj
24338 args[2] = {
24339 capture: useCapture != null ? useCapture : false,
24340 passive: false,
24341 once: false
24342 };
24343 }
24344 r.bindings.push({
24345 target: tgt,
24346 args: args
24347 });
24348 (tgt.addEventListener || tgt.on).apply(tgt, args);
24349 return this;
24350 };
24351 return {
24352 on: on,
24353 addEventListener: on,
24354 addListener: on,
24355 bind: on
24356 };
24357};
24358BRp$3.nodeIsDraggable = function (node) {
24359 return node && node.isNode() && !node.locked() && node.grabbable();
24360};
24361BRp$3.nodeIsGrabbable = function (node) {
24362 return this.nodeIsDraggable(node) && node.interactive();
24363};
24364BRp$3.load = function () {
24365 var r = this;
24366 var containerWindow = r.cy.window();
24367 var isSelected = function isSelected(ele) {
24368 return ele.selected();
24369 };
24370 var triggerEvents = function triggerEvents(target, names, e, position) {
24371 if (target == null) {
24372 target = r.cy;
24373 }
24374 for (var i = 0; i < names.length; i++) {
24375 var name = names[i];
24376 target.emit({
24377 originalEvent: e,
24378 type: name,
24379 position: position
24380 });
24381 }
24382 };
24383 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24384 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24385 };
24386
24387 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24388 var allowPassthrough = true;
24389 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24390 // a grabbable compound node below the ele => no passthrough panning
24391 for (var i = 0; downs && i < downs.length; i++) {
24392 var down = downs[i];
24393
24394 //if any parent node in event hierarchy isn't pannable, reject passthrough
24395 if (down.isNode() && down.isParent() && !down.pannable()) {
24396 allowPassthrough = false;
24397 break;
24398 }
24399 }
24400 } else {
24401 allowPassthrough = true;
24402 }
24403 return allowPassthrough;
24404 };
24405 var setGrabbed = function setGrabbed(ele) {
24406 ele[0]._private.grabbed = true;
24407 };
24408 var setFreed = function setFreed(ele) {
24409 ele[0]._private.grabbed = false;
24410 };
24411 var setInDragLayer = function setInDragLayer(ele) {
24412 ele[0]._private.rscratch.inDragLayer = true;
24413 };
24414 var setOutDragLayer = function setOutDragLayer(ele) {
24415 ele[0]._private.rscratch.inDragLayer = false;
24416 };
24417 var setGrabTarget = function setGrabTarget(ele) {
24418 ele[0]._private.rscratch.isGrabTarget = true;
24419 };
24420 var removeGrabTarget = function removeGrabTarget(ele) {
24421 ele[0]._private.rscratch.isGrabTarget = false;
24422 };
24423 var addToDragList = function addToDragList(ele, opts) {
24424 var list = opts.addToList;
24425 var listHasEle = list.has(ele);
24426 if (!listHasEle && ele.grabbable() && !ele.locked()) {
24427 list.merge(ele);
24428 setGrabbed(ele);
24429 }
24430 };
24431
24432 // helper function to determine which child nodes and inner edges
24433 // of a compound node to be dragged as well as the grabbed and selected nodes
24434 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24435 if (!node.cy().hasCompoundNodes()) {
24436 return;
24437 }
24438 if (opts.inDragLayer == null && opts.addToList == null) {
24439 return;
24440 } // nothing to do
24441
24442 var innerNodes = node.descendants();
24443 if (opts.inDragLayer) {
24444 innerNodes.forEach(setInDragLayer);
24445 innerNodes.connectedEdges().forEach(setInDragLayer);
24446 }
24447 if (opts.addToList) {
24448 addToDragList(innerNodes, opts);
24449 }
24450 };
24451
24452 // adds the given nodes and its neighbourhood to the drag layer
24453 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24454 opts = opts || {};
24455 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24456 if (opts.inDragLayer) {
24457 nodes.forEach(setInDragLayer);
24458 nodes.neighborhood().stdFilter(function (ele) {
24459 return !hasCompoundNodes || ele.isEdge();
24460 }).forEach(setInDragLayer);
24461 }
24462 if (opts.addToList) {
24463 nodes.forEach(function (ele) {
24464 addToDragList(ele, opts);
24465 });
24466 }
24467 addDescendantsToDrag(nodes, opts); // always add to drag
24468
24469 // also add nodes and edges related to the topmost ancestor
24470 updateAncestorsInDragLayer(nodes, {
24471 inDragLayer: opts.inDragLayer
24472 });
24473 r.updateCachedGrabbedEles();
24474 };
24475 var addNodeToDrag = addNodesToDrag;
24476 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24477 if (!grabbedEles) {
24478 return;
24479 }
24480
24481 // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24482 r.getCachedZSortedEles().forEach(function (ele) {
24483 setFreed(ele);
24484 setOutDragLayer(ele);
24485 removeGrabTarget(ele);
24486 });
24487 r.updateCachedGrabbedEles();
24488 };
24489
24490 // helper function to determine which ancestor nodes and edges should go
24491 // to the drag layer (or should be removed from drag layer).
24492 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24493 if (opts.inDragLayer == null && opts.addToList == null) {
24494 return;
24495 } // nothing to do
24496
24497 if (!node.cy().hasCompoundNodes()) {
24498 return;
24499 }
24500
24501 // find top-level parent
24502 var parent = node.ancestors().orphans();
24503
24504 // no parent node: no nodes to add to the drag layer
24505 if (parent.same(node)) {
24506 return;
24507 }
24508 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24509 var edges = nodes.connectedEdges();
24510 if (opts.inDragLayer) {
24511 edges.forEach(setInDragLayer);
24512 nodes.forEach(setInDragLayer);
24513 }
24514 if (opts.addToList) {
24515 nodes.forEach(function (ele) {
24516 addToDragList(ele, opts);
24517 });
24518 }
24519 };
24520 var blurActiveDomElement = function blurActiveDomElement() {
24521 if (document.activeElement != null && document.activeElement.blur != null) {
24522 document.activeElement.blur();
24523 }
24524 };
24525 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24526 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined';
24527
24528 // watch for when the cy container is removed from the dom
24529 if (haveMutationsApi) {
24530 r.removeObserver = new MutationObserver(function (mutns) {
24531 // eslint-disable-line no-undef
24532 for (var i = 0; i < mutns.length; i++) {
24533 var mutn = mutns[i];
24534 var rNodes = mutn.removedNodes;
24535 if (rNodes) {
24536 for (var j = 0; j < rNodes.length; j++) {
24537 var rNode = rNodes[j];
24538 if (rNode === r.container) {
24539 r.destroy();
24540 break;
24541 }
24542 }
24543 }
24544 }
24545 });
24546 if (r.container.parentNode) {
24547 r.removeObserver.observe(r.container.parentNode, {
24548 childList: true
24549 });
24550 }
24551 } else {
24552 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24553 // eslint-disable-line no-unused-vars
24554 r.destroy();
24555 });
24556 }
24557 var onResize = debounce_1(function () {
24558 r.cy.resize();
24559 }, 100);
24560 if (haveMutationsApi) {
24561 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24562
24563 r.styleObserver.observe(r.container, {
24564 attributes: true
24565 });
24566 }
24567
24568 // auto resize
24569 r.registerBinding(containerWindow, 'resize', onResize); // eslint-disable-line no-undef
24570
24571 if (haveResizeObserverApi) {
24572 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24573
24574 r.resizeObserver.observe(r.container);
24575 }
24576 var forEachUp = function forEachUp(domEle, fn) {
24577 while (domEle != null) {
24578 fn(domEle);
24579 domEle = domEle.parentNode;
24580 }
24581 };
24582 var invalidateCoords = function invalidateCoords() {
24583 r.invalidateContainerClientCoordsCache();
24584 };
24585 forEachUp(r.container, function (domEle) {
24586 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24587 r.registerBinding(domEle, 'animationend', invalidateCoords);
24588 r.registerBinding(domEle, 'scroll', invalidateCoords);
24589 });
24590
24591 // stop right click menu from appearing on cy
24592 r.registerBinding(r.container, 'contextmenu', function (e) {
24593 e.preventDefault();
24594 });
24595 var inBoxSelection = function inBoxSelection() {
24596 return r.selection[4] !== 0;
24597 };
24598 var eventInContainer = function eventInContainer(e) {
24599 // save cycles if mouse events aren't to be captured
24600 var containerPageCoords = r.findContainerClientCoords();
24601 var x = containerPageCoords[0];
24602 var y = containerPageCoords[1];
24603 var width = containerPageCoords[2];
24604 var height = containerPageCoords[3];
24605 var positions = e.touches ? e.touches : [e];
24606 var atLeastOnePosInside = false;
24607 for (var i = 0; i < positions.length; i++) {
24608 var p = positions[i];
24609 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24610 atLeastOnePosInside = true;
24611 break;
24612 }
24613 }
24614 if (!atLeastOnePosInside) {
24615 return false;
24616 }
24617 var container = r.container;
24618 var target = e.target;
24619 var tParent = target.parentNode;
24620 var containerIsTarget = false;
24621 while (tParent) {
24622 if (tParent === container) {
24623 containerIsTarget = true;
24624 break;
24625 }
24626 tParent = tParent.parentNode;
24627 }
24628 if (!containerIsTarget) {
24629 return false;
24630 } // if target is outisde cy container, then this event is not for us
24631
24632 return true;
24633 };
24634
24635 // Primary key
24636 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24637 if (!eventInContainer(e)) {
24638 return;
24639 }
24640
24641 // during left mouse button gestures, ignore other buttons
24642 if (r.hoverData.which === 1 && e.which !== 1) {
24643 return;
24644 }
24645 e.preventDefault();
24646 blurActiveDomElement();
24647 r.hoverData.capture = true;
24648 r.hoverData.which = e.which;
24649 var cy = r.cy;
24650 var gpos = [e.clientX, e.clientY];
24651 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24652 var select = r.selection;
24653 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24654 var near = nears[0];
24655 var draggedElements = r.dragData.possibleDragElements;
24656 r.hoverData.mdownPos = pos;
24657 r.hoverData.mdownGPos = gpos;
24658 var checkForTaphold = function checkForTaphold() {
24659 r.hoverData.tapholdCancelled = false;
24660 clearTimeout(r.hoverData.tapholdTimeout);
24661 r.hoverData.tapholdTimeout = setTimeout(function () {
24662 if (r.hoverData.tapholdCancelled) {
24663 return;
24664 } else {
24665 var ele = r.hoverData.down;
24666 if (ele) {
24667 ele.emit({
24668 originalEvent: e,
24669 type: 'taphold',
24670 position: {
24671 x: pos[0],
24672 y: pos[1]
24673 }
24674 });
24675 } else {
24676 cy.emit({
24677 originalEvent: e,
24678 type: 'taphold',
24679 position: {
24680 x: pos[0],
24681 y: pos[1]
24682 }
24683 });
24684 }
24685 }
24686 }, r.tapholdDuration);
24687 };
24688
24689 // Right click button
24690 if (e.which == 3) {
24691 r.hoverData.cxtStarted = true;
24692 var cxtEvt = {
24693 originalEvent: e,
24694 type: 'cxttapstart',
24695 position: {
24696 x: pos[0],
24697 y: pos[1]
24698 }
24699 };
24700 if (near) {
24701 near.activate();
24702 near.emit(cxtEvt);
24703 r.hoverData.down = near;
24704 } else {
24705 cy.emit(cxtEvt);
24706 }
24707 r.hoverData.downTime = new Date().getTime();
24708 r.hoverData.cxtDragged = false;
24709
24710 // Primary button
24711 } else if (e.which == 1) {
24712 if (near) {
24713 near.activate();
24714 }
24715
24716 // Element dragging
24717 {
24718 // If something is under the cursor and it is draggable, prepare to grab it
24719 if (near != null) {
24720 if (r.nodeIsGrabbable(near)) {
24721 var makeEvent = function makeEvent(type) {
24722 return {
24723 originalEvent: e,
24724 type: type,
24725 position: {
24726 x: pos[0],
24727 y: pos[1]
24728 }
24729 };
24730 };
24731 var triggerGrab = function triggerGrab(ele) {
24732 ele.emit(makeEvent('grab'));
24733 };
24734 setGrabTarget(near);
24735 if (!near.selected()) {
24736 draggedElements = r.dragData.possibleDragElements = cy.collection();
24737 addNodeToDrag(near, {
24738 addToList: draggedElements
24739 });
24740 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24741 } else {
24742 draggedElements = r.dragData.possibleDragElements = cy.collection();
24743 var selectedNodes = cy.$(function (ele) {
24744 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24745 });
24746 addNodesToDrag(selectedNodes, {
24747 addToList: draggedElements
24748 });
24749 near.emit(makeEvent('grabon'));
24750 selectedNodes.forEach(triggerGrab);
24751 }
24752 r.redrawHint('eles', true);
24753 r.redrawHint('drag', true);
24754 }
24755 }
24756 r.hoverData.down = near;
24757 r.hoverData.downs = nears;
24758 r.hoverData.downTime = new Date().getTime();
24759 }
24760 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24761 x: pos[0],
24762 y: pos[1]
24763 });
24764 if (near == null) {
24765 select[4] = 1;
24766 r.data.bgActivePosistion = {
24767 x: pos[0],
24768 y: pos[1]
24769 };
24770 r.redrawHint('select', true);
24771 r.redraw();
24772 } else if (near.pannable()) {
24773 select[4] = 1; // for future pan
24774 }
24775
24776 checkForTaphold();
24777 }
24778
24779 // Initialize selection box coordinates
24780 select[0] = select[2] = pos[0];
24781 select[1] = select[3] = pos[1];
24782 }, false);
24783 r.registerBinding(containerWindow, 'mousemove', function mousemoveHandler(e) {
24784 // eslint-disable-line no-undef
24785 var capture = r.hoverData.capture;
24786 if (!capture && !eventInContainer(e)) {
24787 return;
24788 }
24789 var preventDefault = false;
24790 var cy = r.cy;
24791 var zoom = cy.zoom();
24792 var gpos = [e.clientX, e.clientY];
24793 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24794 var mdownPos = r.hoverData.mdownPos;
24795 var mdownGPos = r.hoverData.mdownGPos;
24796 var select = r.selection;
24797 var near = null;
24798 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24799 near = r.findNearestElement(pos[0], pos[1], true, false);
24800 }
24801 var last = r.hoverData.last;
24802 var down = r.hoverData.down;
24803 var disp = [pos[0] - select[2], pos[1] - select[3]];
24804 var draggedElements = r.dragData.possibleDragElements;
24805 var isOverThresholdDrag;
24806 if (mdownGPos) {
24807 var dx = gpos[0] - mdownGPos[0];
24808 var dx2 = dx * dx;
24809 var dy = gpos[1] - mdownGPos[1];
24810 var dy2 = dy * dy;
24811 var dist2 = dx2 + dy2;
24812 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24813 }
24814 var multSelKeyDown = isMultSelKeyDown(e);
24815 if (isOverThresholdDrag) {
24816 r.hoverData.tapholdCancelled = true;
24817 }
24818 var updateDragDelta = function updateDragDelta() {
24819 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24820 if (dragDelta.length === 0) {
24821 dragDelta.push(disp[0]);
24822 dragDelta.push(disp[1]);
24823 } else {
24824 dragDelta[0] += disp[0];
24825 dragDelta[1] += disp[1];
24826 }
24827 };
24828 preventDefault = true;
24829 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24830 x: pos[0],
24831 y: pos[1]
24832 });
24833 var goIntoBoxMode = function goIntoBoxMode() {
24834 r.data.bgActivePosistion = undefined;
24835 if (!r.hoverData.selecting) {
24836 cy.emit({
24837 originalEvent: e,
24838 type: 'boxstart',
24839 position: {
24840 x: pos[0],
24841 y: pos[1]
24842 }
24843 });
24844 }
24845 select[4] = 1;
24846 r.hoverData.selecting = true;
24847 r.redrawHint('select', true);
24848 r.redraw();
24849 };
24850
24851 // trigger context drag if rmouse down
24852 if (r.hoverData.which === 3) {
24853 // but only if over threshold
24854 if (isOverThresholdDrag) {
24855 var cxtEvt = {
24856 originalEvent: e,
24857 type: 'cxtdrag',
24858 position: {
24859 x: pos[0],
24860 y: pos[1]
24861 }
24862 };
24863 if (down) {
24864 down.emit(cxtEvt);
24865 } else {
24866 cy.emit(cxtEvt);
24867 }
24868 r.hoverData.cxtDragged = true;
24869 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24870 if (r.hoverData.cxtOver) {
24871 r.hoverData.cxtOver.emit({
24872 originalEvent: e,
24873 type: 'cxtdragout',
24874 position: {
24875 x: pos[0],
24876 y: pos[1]
24877 }
24878 });
24879 }
24880 r.hoverData.cxtOver = near;
24881 if (near) {
24882 near.emit({
24883 originalEvent: e,
24884 type: 'cxtdragover',
24885 position: {
24886 x: pos[0],
24887 y: pos[1]
24888 }
24889 });
24890 }
24891 }
24892 }
24893
24894 // Check if we are drag panning the entire graph
24895 } else if (r.hoverData.dragging) {
24896 preventDefault = true;
24897 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24898 var deltaP;
24899 if (r.hoverData.justStartedPan) {
24900 var mdPos = r.hoverData.mdownPos;
24901 deltaP = {
24902 x: (pos[0] - mdPos[0]) * zoom,
24903 y: (pos[1] - mdPos[1]) * zoom
24904 };
24905 r.hoverData.justStartedPan = false;
24906 } else {
24907 deltaP = {
24908 x: disp[0] * zoom,
24909 y: disp[1] * zoom
24910 };
24911 }
24912 cy.panBy(deltaP);
24913 cy.emit('dragpan');
24914 r.hoverData.dragged = true;
24915 }
24916
24917 // Needs reproject due to pan changing viewport
24918 pos = r.projectIntoViewport(e.clientX, e.clientY);
24919
24920 // Checks primary button down & out of time & mouse not moved much
24921 } else if (select[4] == 1 && (down == null || down.pannable())) {
24922 if (isOverThresholdDrag) {
24923 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24924 goIntoBoxMode();
24925 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24926 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24927 if (allowPassthrough) {
24928 r.hoverData.dragging = true;
24929 r.hoverData.justStartedPan = true;
24930 select[4] = 0;
24931 r.data.bgActivePosistion = array2point(mdownPos);
24932 r.redrawHint('select', true);
24933 r.redraw();
24934 }
24935 }
24936 if (down && down.pannable() && down.active()) {
24937 down.unactivate();
24938 }
24939 }
24940 } else {
24941 if (down && down.pannable() && down.active()) {
24942 down.unactivate();
24943 }
24944 if ((!down || !down.grabbed()) && near != last) {
24945 if (last) {
24946 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24947 x: pos[0],
24948 y: pos[1]
24949 });
24950 }
24951 if (near) {
24952 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24953 x: pos[0],
24954 y: pos[1]
24955 });
24956 }
24957 r.hoverData.last = near;
24958 }
24959 if (down) {
24960 if (isOverThresholdDrag) {
24961 // then we can take action
24962
24963 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24964 // then selection overrides
24965 if (down && down.grabbed()) {
24966 freeDraggedElements(draggedElements);
24967 down.emit('freeon');
24968 draggedElements.emit('free');
24969 if (r.dragData.didDrag) {
24970 down.emit('dragfreeon');
24971 draggedElements.emit('dragfree');
24972 }
24973 }
24974 goIntoBoxMode();
24975 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24976 // drag node
24977 var justStartedDrag = !r.dragData.didDrag;
24978 if (justStartedDrag) {
24979 r.redrawHint('eles', true);
24980 }
24981 r.dragData.didDrag = true; // indicate that we actually did drag the node
24982
24983 // now, add the elements to the drag layer if not done already
24984 if (!r.hoverData.draggingEles) {
24985 addNodesToDrag(draggedElements, {
24986 inDragLayer: true
24987 });
24988 }
24989 var totalShift = {
24990 x: 0,
24991 y: 0
24992 };
24993 if (number$1(disp[0]) && number$1(disp[1])) {
24994 totalShift.x += disp[0];
24995 totalShift.y += disp[1];
24996 if (justStartedDrag) {
24997 var dragDelta = r.hoverData.dragDelta;
24998 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
24999 totalShift.x += dragDelta[0];
25000 totalShift.y += dragDelta[1];
25001 }
25002 }
25003 }
25004 r.hoverData.draggingEles = true;
25005 draggedElements.silentShift(totalShift).emit('position drag');
25006 r.redrawHint('drag', true);
25007 r.redraw();
25008 }
25009 } else {
25010 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25011 updateDragDelta();
25012 }
25013 }
25014
25015 // prevent the dragging from triggering text selection on the page
25016 preventDefault = true;
25017 }
25018 select[2] = pos[0];
25019 select[3] = pos[1];
25020 if (preventDefault) {
25021 if (e.stopPropagation) e.stopPropagation();
25022 if (e.preventDefault) e.preventDefault();
25023 return false;
25024 }
25025 }, false);
25026 var clickTimeout, didDoubleClick, prevClickTimeStamp;
25027 r.registerBinding(containerWindow, 'mouseup', function mouseupHandler(e) {
25028 // eslint-disable-line no-undef
25029 // during left mouse button gestures, ignore other buttons
25030 if (r.hoverData.which === 1 && e.which !== 1 && r.hoverData.capture) {
25031 return;
25032 }
25033 var capture = r.hoverData.capture;
25034 if (!capture) {
25035 return;
25036 }
25037 r.hoverData.capture = false;
25038 var cy = r.cy;
25039 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25040 var select = r.selection;
25041 var near = r.findNearestElement(pos[0], pos[1], true, false);
25042 var draggedElements = r.dragData.possibleDragElements;
25043 var down = r.hoverData.down;
25044 var multSelKeyDown = isMultSelKeyDown(e);
25045 if (r.data.bgActivePosistion) {
25046 r.redrawHint('select', true);
25047 r.redraw();
25048 }
25049 r.hoverData.tapholdCancelled = true;
25050 r.data.bgActivePosistion = undefined; // not active bg now
25051
25052 if (down) {
25053 down.unactivate();
25054 }
25055 if (r.hoverData.which === 3) {
25056 var cxtEvt = {
25057 originalEvent: e,
25058 type: 'cxttapend',
25059 position: {
25060 x: pos[0],
25061 y: pos[1]
25062 }
25063 };
25064 if (down) {
25065 down.emit(cxtEvt);
25066 } else {
25067 cy.emit(cxtEvt);
25068 }
25069 if (!r.hoverData.cxtDragged) {
25070 var cxtTap = {
25071 originalEvent: e,
25072 type: 'cxttap',
25073 position: {
25074 x: pos[0],
25075 y: pos[1]
25076 }
25077 };
25078 if (down) {
25079 down.emit(cxtTap);
25080 } else {
25081 cy.emit(cxtTap);
25082 }
25083 }
25084 r.hoverData.cxtDragged = false;
25085 r.hoverData.which = null;
25086 } else if (r.hoverData.which === 1) {
25087 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25088 x: pos[0],
25089 y: pos[1]
25090 });
25091 if (!r.dragData.didDrag &&
25092 // didn't move a node around
25093 !r.hoverData.dragged &&
25094 // didn't pan
25095 !r.hoverData.selecting &&
25096 // not box selection
25097 !r.hoverData.isOverThresholdDrag // didn't move too much
25098 ) {
25099 triggerEvents(down, ["click", "tap", "vclick"], e, {
25100 x: pos[0],
25101 y: pos[1]
25102 });
25103 didDoubleClick = false;
25104 if (e.timeStamp - prevClickTimeStamp <= cy.multiClickDebounceTime()) {
25105 clickTimeout && clearTimeout(clickTimeout);
25106 didDoubleClick = true;
25107 prevClickTimeStamp = null;
25108 triggerEvents(down, ["dblclick", "dbltap", "vdblclick"], e, {
25109 x: pos[0],
25110 y: pos[1]
25111 });
25112 } else {
25113 clickTimeout = setTimeout(function () {
25114 if (didDoubleClick) return;
25115 triggerEvents(down, ["oneclick", "onetap", "voneclick"], e, {
25116 x: pos[0],
25117 y: pos[1]
25118 });
25119 }, cy.multiClickDebounceTime());
25120 prevClickTimeStamp = e.timeStamp;
25121 }
25122 }
25123
25124 // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25125 if (down == null // not mousedown on node
25126 && !r.dragData.didDrag // didn't move the node around
25127 && !r.hoverData.selecting // not box selection
25128 && !r.hoverData.dragged // didn't pan
25129 && !isMultSelKeyDown(e)) {
25130 cy.$(isSelected).unselect(['tapunselect']);
25131 if (draggedElements.length > 0) {
25132 r.redrawHint('eles', true);
25133 }
25134 r.dragData.possibleDragElements = draggedElements = cy.collection();
25135 }
25136
25137 // Single selection
25138 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25139 if (near != null && near._private.selectable) {
25140 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25141 if (near.selected()) {
25142 near.unselect(['tapunselect']);
25143 } else {
25144 near.select(['tapselect']);
25145 }
25146 } else {
25147 if (!multSelKeyDown) {
25148 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25149 near.select(['tapselect']);
25150 }
25151 }
25152 r.redrawHint('eles', true);
25153 }
25154 }
25155 if (r.hoverData.selecting) {
25156 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25157 r.redrawHint('select', true);
25158 if (box.length > 0) {
25159 r.redrawHint('eles', true);
25160 }
25161 cy.emit({
25162 type: 'boxend',
25163 originalEvent: e,
25164 position: {
25165 x: pos[0],
25166 y: pos[1]
25167 }
25168 });
25169 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25170 return ele.selectable() && !ele.selected();
25171 };
25172 if (cy.selectionType() === 'additive') {
25173 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25174 } else {
25175 if (!multSelKeyDown) {
25176 cy.$(isSelected).unmerge(box).unselect();
25177 }
25178 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25179 }
25180
25181 // always need redraw in case eles unselectable
25182 r.redraw();
25183 }
25184
25185 // Cancel drag pan
25186 if (r.hoverData.dragging) {
25187 r.hoverData.dragging = false;
25188 r.redrawHint('select', true);
25189 r.redrawHint('eles', true);
25190 r.redraw();
25191 }
25192 if (!select[4]) {
25193 r.redrawHint('drag', true);
25194 r.redrawHint('eles', true);
25195 var downWasGrabbed = down && down.grabbed();
25196 freeDraggedElements(draggedElements);
25197 if (downWasGrabbed) {
25198 down.emit('freeon');
25199 draggedElements.emit('free');
25200 if (r.dragData.didDrag) {
25201 down.emit('dragfreeon');
25202 draggedElements.emit('dragfree');
25203 }
25204 }
25205 }
25206 } // else not right mouse
25207
25208 select[4] = 0;
25209 r.hoverData.down = null;
25210 r.hoverData.cxtStarted = false;
25211 r.hoverData.draggingEles = false;
25212 r.hoverData.selecting = false;
25213 r.hoverData.isOverThresholdDrag = false;
25214 r.dragData.didDrag = false;
25215 r.hoverData.dragged = false;
25216 r.hoverData.dragDelta = [];
25217 r.hoverData.mdownPos = null;
25218 r.hoverData.mdownGPos = null;
25219 r.hoverData.which = null;
25220 }, false);
25221 var wheelHandler = function wheelHandler(e) {
25222 if (r.scrollingPage) {
25223 return;
25224 } // while scrolling, ignore wheel-to-zoom
25225
25226 var cy = r.cy;
25227 var zoom = cy.zoom();
25228 var pan = cy.pan();
25229 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25230 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25231 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25232 // if pan dragging or cxt dragging, wheel movements make no zoom
25233 e.preventDefault();
25234 return;
25235 }
25236 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25237 e.preventDefault();
25238 r.data.wheelZooming = true;
25239 clearTimeout(r.data.wheelTimeout);
25240 r.data.wheelTimeout = setTimeout(function () {
25241 r.data.wheelZooming = false;
25242 r.redrawHint('eles', true);
25243 r.redraw();
25244 }, 150);
25245 var diff;
25246 if (e.deltaY != null) {
25247 diff = e.deltaY / -250;
25248 } else if (e.wheelDeltaY != null) {
25249 diff = e.wheelDeltaY / 1000;
25250 } else {
25251 diff = e.wheelDelta / 1000;
25252 }
25253 diff = diff * r.wheelSensitivity;
25254 var needsWheelFix = e.deltaMode === 1;
25255 if (needsWheelFix) {
25256 // fixes slow wheel events on ff/linux and ff/windows
25257 diff *= 33;
25258 }
25259 var newZoom = cy.zoom() * Math.pow(10, diff);
25260 if (e.type === 'gesturechange') {
25261 newZoom = r.gestureStartZoom * e.scale;
25262 }
25263 cy.zoom({
25264 level: newZoom,
25265 renderedPosition: {
25266 x: rpos[0],
25267 y: rpos[1]
25268 }
25269 });
25270 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25271 }
25272 };
25273
25274 // Functions to help with whether mouse wheel should trigger zooming
25275 // --
25276 r.registerBinding(r.container, 'wheel', wheelHandler, true);
25277
25278 // disable nonstandard wheel events
25279 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25280 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25281 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25282
25283 r.registerBinding(containerWindow, 'scroll', function scrollHandler(e) {
25284 // eslint-disable-line no-unused-vars
25285 r.scrollingPage = true;
25286 clearTimeout(r.scrollingPageTimeout);
25287 r.scrollingPageTimeout = setTimeout(function () {
25288 r.scrollingPage = false;
25289 }, 250);
25290 }, true);
25291
25292 // desktop safari pinch to zoom start
25293 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25294 r.gestureStartZoom = r.cy.zoom();
25295 if (!r.hasTouchStarted) {
25296 // don't affect touch devices like iphone
25297 e.preventDefault();
25298 }
25299 }, true);
25300 r.registerBinding(r.container, 'gesturechange', function (e) {
25301 if (!r.hasTouchStarted) {
25302 // don't affect touch devices like iphone
25303 wheelHandler(e);
25304 }
25305 }, true);
25306
25307 // Functions to help with handling mouseout/mouseover on the Cytoscape container
25308 // Handle mouseout on Cytoscape container
25309 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25310 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25311 r.cy.emit({
25312 originalEvent: e,
25313 type: 'mouseout',
25314 position: {
25315 x: pos[0],
25316 y: pos[1]
25317 }
25318 });
25319 }, false);
25320 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25321 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25322 r.cy.emit({
25323 originalEvent: e,
25324 type: 'mouseover',
25325 position: {
25326 x: pos[0],
25327 y: pos[1]
25328 }
25329 });
25330 }, false);
25331 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25332 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25333 var center1, modelCenter1; // center point on start pinch to zoom
25334 var offsetLeft, offsetTop;
25335 var containerWidth, containerHeight;
25336 var twoFingersStartInside;
25337 var distance = function distance(x1, y1, x2, y2) {
25338 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25339 };
25340 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25341 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25342 };
25343 var touchstartHandler;
25344 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25345 r.hasTouchStarted = true;
25346 if (!eventInContainer(e)) {
25347 return;
25348 }
25349 blurActiveDomElement();
25350 r.touchData.capture = true;
25351 r.data.bgActivePosistion = undefined;
25352 var cy = r.cy;
25353 var now = r.touchData.now;
25354 var earlier = r.touchData.earlier;
25355 if (e.touches[0]) {
25356 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25357 now[0] = pos[0];
25358 now[1] = pos[1];
25359 }
25360 if (e.touches[1]) {
25361 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25362 now[2] = pos[0];
25363 now[3] = pos[1];
25364 }
25365 if (e.touches[2]) {
25366 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25367 now[4] = pos[0];
25368 now[5] = pos[1];
25369 }
25370
25371 // record starting points for pinch-to-zoom
25372 if (e.touches[1]) {
25373 r.touchData.singleTouchMoved = true;
25374 freeDraggedElements(r.dragData.touchDragEles);
25375 var offsets = r.findContainerClientCoords();
25376 offsetLeft = offsets[0];
25377 offsetTop = offsets[1];
25378 containerWidth = offsets[2];
25379 containerHeight = offsets[3];
25380 f1x1 = e.touches[0].clientX - offsetLeft;
25381 f1y1 = e.touches[0].clientY - offsetTop;
25382 f2x1 = e.touches[1].clientX - offsetLeft;
25383 f2y1 = e.touches[1].clientY - offsetTop;
25384 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25385 var pan = cy.pan();
25386 var zoom = cy.zoom();
25387 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25388 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25389 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25390 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom];
25391
25392 // consider context tap
25393 var cxtDistThreshold = 200;
25394 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25395 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25396 var near1 = r.findNearestElement(now[0], now[1], true, true);
25397 var near2 = r.findNearestElement(now[2], now[3], true, true);
25398 if (near1 && near1.isNode()) {
25399 near1.activate().emit({
25400 originalEvent: e,
25401 type: 'cxttapstart',
25402 position: {
25403 x: now[0],
25404 y: now[1]
25405 }
25406 });
25407 r.touchData.start = near1;
25408 } else if (near2 && near2.isNode()) {
25409 near2.activate().emit({
25410 originalEvent: e,
25411 type: 'cxttapstart',
25412 position: {
25413 x: now[0],
25414 y: now[1]
25415 }
25416 });
25417 r.touchData.start = near2;
25418 } else {
25419 cy.emit({
25420 originalEvent: e,
25421 type: 'cxttapstart',
25422 position: {
25423 x: now[0],
25424 y: now[1]
25425 }
25426 });
25427 }
25428 if (r.touchData.start) {
25429 r.touchData.start._private.grabbed = false;
25430 }
25431 r.touchData.cxt = true;
25432 r.touchData.cxtDragged = false;
25433 r.data.bgActivePosistion = undefined;
25434 r.redraw();
25435 return;
25436 }
25437 }
25438 if (e.touches[2]) {
25439 // ignore
25440
25441 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25442 if (cy.boxSelectionEnabled()) {
25443 e.preventDefault();
25444 }
25445 } else if (e.touches[1]) ; else if (e.touches[0]) {
25446 var nears = r.findNearestElements(now[0], now[1], true, true);
25447 var near = nears[0];
25448 if (near != null) {
25449 near.activate();
25450 r.touchData.start = near;
25451 r.touchData.starts = nears;
25452 if (r.nodeIsGrabbable(near)) {
25453 var draggedEles = r.dragData.touchDragEles = cy.collection();
25454 var selectedNodes = null;
25455 r.redrawHint('eles', true);
25456 r.redrawHint('drag', true);
25457 if (near.selected()) {
25458 // reset drag elements, since near will be added again
25459
25460 selectedNodes = cy.$(function (ele) {
25461 return ele.selected() && r.nodeIsGrabbable(ele);
25462 });
25463 addNodesToDrag(selectedNodes, {
25464 addToList: draggedEles
25465 });
25466 } else {
25467 addNodeToDrag(near, {
25468 addToList: draggedEles
25469 });
25470 }
25471 setGrabTarget(near);
25472 var makeEvent = function makeEvent(type) {
25473 return {
25474 originalEvent: e,
25475 type: type,
25476 position: {
25477 x: now[0],
25478 y: now[1]
25479 }
25480 };
25481 };
25482 near.emit(makeEvent('grabon'));
25483 if (selectedNodes) {
25484 selectedNodes.forEach(function (n) {
25485 n.emit(makeEvent('grab'));
25486 });
25487 } else {
25488 near.emit(makeEvent('grab'));
25489 }
25490 }
25491 }
25492 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25493 x: now[0],
25494 y: now[1]
25495 });
25496 if (near == null) {
25497 r.data.bgActivePosistion = {
25498 x: pos[0],
25499 y: pos[1]
25500 };
25501 r.redrawHint('select', true);
25502 r.redraw();
25503 }
25504
25505 // Tap, taphold
25506 // -----
25507
25508 r.touchData.singleTouchMoved = false;
25509 r.touchData.singleTouchStartTime = +new Date();
25510 clearTimeout(r.touchData.tapholdTimeout);
25511 r.touchData.tapholdTimeout = setTimeout(function () {
25512 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25513 && !r.touchData.selecting // box selection shouldn't allow taphold through
25514 ) {
25515 triggerEvents(r.touchData.start, ['taphold'], e, {
25516 x: now[0],
25517 y: now[1]
25518 });
25519 }
25520 }, r.tapholdDuration);
25521 }
25522 if (e.touches.length >= 1) {
25523 var sPos = r.touchData.startPosition = [null, null, null, null, null, null];
25524 for (var i = 0; i < now.length; i++) {
25525 sPos[i] = earlier[i] = now[i];
25526 }
25527 var touch0 = e.touches[0];
25528 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25529 }
25530 }, false);
25531 var touchmoveHandler;
25532 r.registerBinding(containerWindow, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25533 // eslint-disable-line no-undef
25534 var capture = r.touchData.capture;
25535 if (!capture && !eventInContainer(e)) {
25536 return;
25537 }
25538 var select = r.selection;
25539 var cy = r.cy;
25540 var now = r.touchData.now;
25541 var earlier = r.touchData.earlier;
25542 var zoom = cy.zoom();
25543 if (e.touches[0]) {
25544 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25545 now[0] = pos[0];
25546 now[1] = pos[1];
25547 }
25548 if (e.touches[1]) {
25549 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25550 now[2] = pos[0];
25551 now[3] = pos[1];
25552 }
25553 if (e.touches[2]) {
25554 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25555 now[4] = pos[0];
25556 now[5] = pos[1];
25557 }
25558 var startGPos = r.touchData.startGPosition;
25559 var isOverThresholdDrag;
25560 if (capture && e.touches[0] && startGPos) {
25561 var disp = [];
25562 for (var j = 0; j < now.length; j++) {
25563 disp[j] = now[j] - earlier[j];
25564 }
25565 var dx = e.touches[0].clientX - startGPos[0];
25566 var dx2 = dx * dx;
25567 var dy = e.touches[0].clientY - startGPos[1];
25568 var dy2 = dy * dy;
25569 var dist2 = dx2 + dy2;
25570 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25571 }
25572
25573 // context swipe cancelling
25574 if (capture && r.touchData.cxt) {
25575 e.preventDefault();
25576 var f1x2 = e.touches[0].clientX - offsetLeft,
25577 f1y2 = e.touches[0].clientY - offsetTop;
25578 var f2x2 = e.touches[1].clientX - offsetLeft,
25579 f2y2 = e.touches[1].clientY - offsetTop;
25580 // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25581 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25582 var factorSq = distance2Sq / distance1Sq;
25583 var distThreshold = 150;
25584 var distThresholdSq = distThreshold * distThreshold;
25585 var factorThreshold = 1.5;
25586 var factorThresholdSq = factorThreshold * factorThreshold;
25587
25588 // cancel ctx gestures if the distance b/t the fingers increases
25589 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25590 r.touchData.cxt = false;
25591 r.data.bgActivePosistion = undefined;
25592 r.redrawHint('select', true);
25593 var cxtEvt = {
25594 originalEvent: e,
25595 type: 'cxttapend',
25596 position: {
25597 x: now[0],
25598 y: now[1]
25599 }
25600 };
25601 if (r.touchData.start) {
25602 r.touchData.start.unactivate().emit(cxtEvt);
25603 r.touchData.start = null;
25604 } else {
25605 cy.emit(cxtEvt);
25606 }
25607 }
25608 }
25609
25610 // context swipe
25611 if (capture && r.touchData.cxt) {
25612 var cxtEvt = {
25613 originalEvent: e,
25614 type: 'cxtdrag',
25615 position: {
25616 x: now[0],
25617 y: now[1]
25618 }
25619 };
25620 r.data.bgActivePosistion = undefined;
25621 r.redrawHint('select', true);
25622 if (r.touchData.start) {
25623 r.touchData.start.emit(cxtEvt);
25624 } else {
25625 cy.emit(cxtEvt);
25626 }
25627 if (r.touchData.start) {
25628 r.touchData.start._private.grabbed = false;
25629 }
25630 r.touchData.cxtDragged = true;
25631 var near = r.findNearestElement(now[0], now[1], true, true);
25632 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25633 if (r.touchData.cxtOver) {
25634 r.touchData.cxtOver.emit({
25635 originalEvent: e,
25636 type: 'cxtdragout',
25637 position: {
25638 x: now[0],
25639 y: now[1]
25640 }
25641 });
25642 }
25643 r.touchData.cxtOver = near;
25644 if (near) {
25645 near.emit({
25646 originalEvent: e,
25647 type: 'cxtdragover',
25648 position: {
25649 x: now[0],
25650 y: now[1]
25651 }
25652 });
25653 }
25654 }
25655
25656 // box selection
25657 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25658 e.preventDefault();
25659 r.data.bgActivePosistion = undefined;
25660 this.lastThreeTouch = +new Date();
25661 if (!r.touchData.selecting) {
25662 cy.emit({
25663 originalEvent: e,
25664 type: 'boxstart',
25665 position: {
25666 x: now[0],
25667 y: now[1]
25668 }
25669 });
25670 }
25671 r.touchData.selecting = true;
25672 r.touchData.didSelect = true;
25673 select[4] = 1;
25674 if (!select || select.length === 0 || select[0] === undefined) {
25675 select[0] = (now[0] + now[2] + now[4]) / 3;
25676 select[1] = (now[1] + now[3] + now[5]) / 3;
25677 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25678 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25679 } else {
25680 select[2] = (now[0] + now[2] + now[4]) / 3;
25681 select[3] = (now[1] + now[3] + now[5]) / 3;
25682 }
25683 r.redrawHint('select', true);
25684 r.redraw();
25685
25686 // pinch to zoom
25687 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25688 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25689 // two fingers => pinch to zoom
25690 e.preventDefault();
25691 r.data.bgActivePosistion = undefined;
25692 r.redrawHint('select', true);
25693 var draggedEles = r.dragData.touchDragEles;
25694 if (draggedEles) {
25695 r.redrawHint('drag', true);
25696 for (var i = 0; i < draggedEles.length; i++) {
25697 var de_p = draggedEles[i]._private;
25698 de_p.grabbed = false;
25699 de_p.rscratch.inDragLayer = false;
25700 }
25701 }
25702 var _start = r.touchData.start;
25703
25704 // (x2, y2) for fingers 1 and 2
25705 var f1x2 = e.touches[0].clientX - offsetLeft,
25706 f1y2 = e.touches[0].clientY - offsetTop;
25707 var f2x2 = e.touches[1].clientX - offsetLeft,
25708 f2y2 = e.touches[1].clientY - offsetTop;
25709 var distance2 = distance(f1x2, f1y2, f2x2, f2y2);
25710 // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25711 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25712 var factor = distance2 / distance1;
25713 if (twoFingersStartInside) {
25714 // delta finger1
25715 var df1x = f1x2 - f1x1;
25716 var df1y = f1y2 - f1y1;
25717
25718 // delta finger 2
25719 var df2x = f2x2 - f2x1;
25720 var df2y = f2y2 - f2y1;
25721
25722 // translation is the normalised vector of the two fingers movement
25723 // i.e. so pinching cancels out and moving together pans
25724 var tx = (df1x + df2x) / 2;
25725 var ty = (df1y + df2y) / 2;
25726
25727 // now calculate the zoom
25728 var zoom1 = cy.zoom();
25729 var zoom2 = zoom1 * factor;
25730 var pan1 = cy.pan();
25731
25732 // the model center point converted to the current rendered pos
25733 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25734 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25735 var pan2 = {
25736 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25737 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25738 };
25739
25740 // remove dragged eles
25741 if (_start && _start.active()) {
25742 var draggedEles = r.dragData.touchDragEles;
25743 freeDraggedElements(draggedEles);
25744 r.redrawHint('drag', true);
25745 r.redrawHint('eles', true);
25746 _start.unactivate().emit('freeon');
25747 draggedEles.emit('free');
25748 if (r.dragData.didDrag) {
25749 _start.emit('dragfreeon');
25750 draggedEles.emit('dragfree');
25751 }
25752 }
25753 cy.viewport({
25754 zoom: zoom2,
25755 pan: pan2,
25756 cancelOnFailedZoom: true
25757 });
25758 cy.emit('pinchzoom');
25759 distance1 = distance2;
25760 f1x1 = f1x2;
25761 f1y1 = f1y2;
25762 f2x1 = f2x2;
25763 f2y1 = f2y2;
25764 r.pinching = true;
25765 }
25766
25767 // Re-project
25768 if (e.touches[0]) {
25769 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25770 now[0] = pos[0];
25771 now[1] = pos[1];
25772 }
25773 if (e.touches[1]) {
25774 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25775 now[2] = pos[0];
25776 now[3] = pos[1];
25777 }
25778 if (e.touches[2]) {
25779 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25780 now[4] = pos[0];
25781 now[5] = pos[1];
25782 }
25783 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25784 ) {
25785 var start = r.touchData.start;
25786 var last = r.touchData.last;
25787 var near;
25788 if (!r.hoverData.draggingEles && !r.swipePanning) {
25789 near = r.findNearestElement(now[0], now[1], true, true);
25790 }
25791 if (capture && start != null) {
25792 e.preventDefault();
25793 }
25794
25795 // dragging nodes
25796 if (capture && start != null && r.nodeIsDraggable(start)) {
25797 if (isOverThresholdDrag) {
25798 // then dragging can happen
25799 var draggedEles = r.dragData.touchDragEles;
25800 var justStartedDrag = !r.dragData.didDrag;
25801 if (justStartedDrag) {
25802 addNodesToDrag(draggedEles, {
25803 inDragLayer: true
25804 });
25805 }
25806 r.dragData.didDrag = true;
25807 var totalShift = {
25808 x: 0,
25809 y: 0
25810 };
25811 if (number$1(disp[0]) && number$1(disp[1])) {
25812 totalShift.x += disp[0];
25813 totalShift.y += disp[1];
25814 if (justStartedDrag) {
25815 r.redrawHint('eles', true);
25816 var dragDelta = r.touchData.dragDelta;
25817 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
25818 totalShift.x += dragDelta[0];
25819 totalShift.y += dragDelta[1];
25820 }
25821 }
25822 }
25823 r.hoverData.draggingEles = true;
25824 draggedEles.silentShift(totalShift).emit('position drag');
25825 r.redrawHint('drag', true);
25826 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25827 r.redrawHint('eles', true);
25828 }
25829 r.redraw();
25830 } else {
25831 // otherwise keep track of drag delta for later
25832 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25833 if (dragDelta.length === 0) {
25834 dragDelta.push(disp[0]);
25835 dragDelta.push(disp[1]);
25836 } else {
25837 dragDelta[0] += disp[0];
25838 dragDelta[1] += disp[1];
25839 }
25840 }
25841 }
25842
25843 // touchmove
25844 {
25845 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25846 x: now[0],
25847 y: now[1]
25848 });
25849 if ((!start || !start.grabbed()) && near != last) {
25850 if (last) {
25851 last.emit({
25852 originalEvent: e,
25853 type: 'tapdragout',
25854 position: {
25855 x: now[0],
25856 y: now[1]
25857 }
25858 });
25859 }
25860 if (near) {
25861 near.emit({
25862 originalEvent: e,
25863 type: 'tapdragover',
25864 position: {
25865 x: now[0],
25866 y: now[1]
25867 }
25868 });
25869 }
25870 }
25871 r.touchData.last = near;
25872 }
25873
25874 // check to cancel taphold
25875 if (capture) {
25876 for (var i = 0; i < now.length; i++) {
25877 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25878 r.touchData.singleTouchMoved = true;
25879 }
25880 }
25881 }
25882
25883 // panning
25884 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25885 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25886 if (allowPassthrough) {
25887 e.preventDefault();
25888 if (!r.data.bgActivePosistion) {
25889 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25890 }
25891 if (r.swipePanning) {
25892 cy.panBy({
25893 x: disp[0] * zoom,
25894 y: disp[1] * zoom
25895 });
25896 cy.emit('dragpan');
25897 } else if (isOverThresholdDrag) {
25898 r.swipePanning = true;
25899 cy.panBy({
25900 x: dx * zoom,
25901 y: dy * zoom
25902 });
25903 cy.emit('dragpan');
25904 if (start) {
25905 start.unactivate();
25906 r.redrawHint('select', true);
25907 r.touchData.start = null;
25908 }
25909 }
25910 }
25911
25912 // Re-project
25913 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25914 now[0] = pos[0];
25915 now[1] = pos[1];
25916 }
25917 }
25918 for (var j = 0; j < now.length; j++) {
25919 earlier[j] = now[j];
25920 }
25921
25922 // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25923 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25924 r.data.bgActivePosistion = undefined;
25925 r.redrawHint('select', true);
25926 r.redraw();
25927 }
25928 }, false);
25929 var touchcancelHandler;
25930 r.registerBinding(containerWindow, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25931 // eslint-disable-line no-unused-vars
25932 var start = r.touchData.start;
25933 r.touchData.capture = false;
25934 if (start) {
25935 start.unactivate();
25936 }
25937 });
25938 var touchendHandler, didDoubleTouch, touchTimeout, prevTouchTimeStamp;
25939 r.registerBinding(containerWindow, 'touchend', touchendHandler = function touchendHandler(e) {
25940 // eslint-disable-line no-unused-vars
25941 var start = r.touchData.start;
25942 var capture = r.touchData.capture;
25943 if (capture) {
25944 if (e.touches.length === 0) {
25945 r.touchData.capture = false;
25946 }
25947 e.preventDefault();
25948 } else {
25949 return;
25950 }
25951 var select = r.selection;
25952 r.swipePanning = false;
25953 r.hoverData.draggingEles = false;
25954 var cy = r.cy;
25955 var zoom = cy.zoom();
25956 var now = r.touchData.now;
25957 var earlier = r.touchData.earlier;
25958 if (e.touches[0]) {
25959 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25960 now[0] = pos[0];
25961 now[1] = pos[1];
25962 }
25963 if (e.touches[1]) {
25964 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25965 now[2] = pos[0];
25966 now[3] = pos[1];
25967 }
25968 if (e.touches[2]) {
25969 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25970 now[4] = pos[0];
25971 now[5] = pos[1];
25972 }
25973 if (start) {
25974 start.unactivate();
25975 }
25976 var ctxTapend;
25977 if (r.touchData.cxt) {
25978 ctxTapend = {
25979 originalEvent: e,
25980 type: 'cxttapend',
25981 position: {
25982 x: now[0],
25983 y: now[1]
25984 }
25985 };
25986 if (start) {
25987 start.emit(ctxTapend);
25988 } else {
25989 cy.emit(ctxTapend);
25990 }
25991 if (!r.touchData.cxtDragged) {
25992 var ctxTap = {
25993 originalEvent: e,
25994 type: 'cxttap',
25995 position: {
25996 x: now[0],
25997 y: now[1]
25998 }
25999 };
26000 if (start) {
26001 start.emit(ctxTap);
26002 } else {
26003 cy.emit(ctxTap);
26004 }
26005 }
26006 if (r.touchData.start) {
26007 r.touchData.start._private.grabbed = false;
26008 }
26009 r.touchData.cxt = false;
26010 r.touchData.start = null;
26011 r.redraw();
26012 return;
26013 }
26014
26015 // no more box selection if we don't have three fingers
26016 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26017 r.touchData.selecting = false;
26018 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26019 select[0] = undefined;
26020 select[1] = undefined;
26021 select[2] = undefined;
26022 select[3] = undefined;
26023 select[4] = 0;
26024 r.redrawHint('select', true);
26025 cy.emit({
26026 type: 'boxend',
26027 originalEvent: e,
26028 position: {
26029 x: now[0],
26030 y: now[1]
26031 }
26032 });
26033 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26034 return ele.selectable() && !ele.selected();
26035 };
26036 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26037 if (box.nonempty()) {
26038 r.redrawHint('eles', true);
26039 }
26040 r.redraw();
26041 }
26042 if (start != null) {
26043 start.unactivate();
26044 }
26045 if (e.touches[2]) {
26046 r.data.bgActivePosistion = undefined;
26047 r.redrawHint('select', true);
26048 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26049 r.data.bgActivePosistion = undefined;
26050 r.redrawHint('select', true);
26051 var draggedEles = r.dragData.touchDragEles;
26052 if (start != null) {
26053 var startWasGrabbed = start._private.grabbed;
26054 freeDraggedElements(draggedEles);
26055 r.redrawHint('drag', true);
26056 r.redrawHint('eles', true);
26057 if (startWasGrabbed) {
26058 start.emit('freeon');
26059 draggedEles.emit('free');
26060 if (r.dragData.didDrag) {
26061 start.emit('dragfreeon');
26062 draggedEles.emit('dragfree');
26063 }
26064 }
26065 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26066 x: now[0],
26067 y: now[1]
26068 });
26069 start.unactivate();
26070 r.touchData.start = null;
26071 } else {
26072 var near = r.findNearestElement(now[0], now[1], true, true);
26073 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26074 x: now[0],
26075 y: now[1]
26076 });
26077 }
26078 var dx = r.touchData.startPosition[0] - now[0];
26079 var dx2 = dx * dx;
26080 var dy = r.touchData.startPosition[1] - now[1];
26081 var dy2 = dy * dy;
26082 var dist2 = dx2 + dy2;
26083 var rdist2 = dist2 * zoom * zoom;
26084
26085 // Tap event, roughly same as mouse click event for touch
26086 if (!r.touchData.singleTouchMoved) {
26087 if (!start) {
26088 cy.$(':selected').unselect(['tapunselect']);
26089 }
26090 triggerEvents(start, ['tap', 'vclick'], e, {
26091 x: now[0],
26092 y: now[1]
26093 });
26094 didDoubleTouch = false;
26095 if (e.timeStamp - prevTouchTimeStamp <= cy.multiClickDebounceTime()) {
26096 touchTimeout && clearTimeout(touchTimeout);
26097 didDoubleTouch = true;
26098 prevTouchTimeStamp = null;
26099 triggerEvents(start, ['dbltap', 'vdblclick'], e, {
26100 x: now[0],
26101 y: now[1]
26102 });
26103 } else {
26104 touchTimeout = setTimeout(function () {
26105 if (didDoubleTouch) return;
26106 triggerEvents(start, ['onetap', 'voneclick'], e, {
26107 x: now[0],
26108 y: now[1]
26109 });
26110 }, cy.multiClickDebounceTime());
26111 prevTouchTimeStamp = e.timeStamp;
26112 }
26113 }
26114
26115 // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26116 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26117 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26118 ) {
26119 if (cy.selectionType() === 'single') {
26120 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26121 start.select(['tapselect']);
26122 } else {
26123 if (start.selected()) {
26124 start.unselect(['tapunselect']);
26125 } else {
26126 start.select(['tapselect']);
26127 }
26128 }
26129 r.redrawHint('eles', true);
26130 }
26131 r.touchData.singleTouchMoved = true;
26132 }
26133 for (var j = 0; j < now.length; j++) {
26134 earlier[j] = now[j];
26135 }
26136 r.dragData.didDrag = false; // reset for next touchstart
26137
26138 if (e.touches.length === 0) {
26139 r.touchData.dragDelta = [];
26140 r.touchData.startPosition = [null, null, null, null, null, null];
26141 r.touchData.startGPosition = null;
26142 r.touchData.didSelect = false;
26143 }
26144 if (e.touches.length < 2) {
26145 if (e.touches.length === 1) {
26146 // the old start global pos'n may not be the same finger that remains
26147 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26148 }
26149 r.pinching = false;
26150 r.redrawHint('eles', true);
26151 r.redraw();
26152 }
26153
26154 //r.redraw();
26155 }, false);
26156
26157 // fallback compatibility layer for ms pointer events
26158 if (typeof TouchEvent === 'undefined') {
26159 var pointers = [];
26160 var makeTouch = function makeTouch(e) {
26161 return {
26162 clientX: e.clientX,
26163 clientY: e.clientY,
26164 force: 1,
26165 identifier: e.pointerId,
26166 pageX: e.pageX,
26167 pageY: e.pageY,
26168 radiusX: e.width / 2,
26169 radiusY: e.height / 2,
26170 screenX: e.screenX,
26171 screenY: e.screenY,
26172 target: e.target
26173 };
26174 };
26175 var makePointer = function makePointer(e) {
26176 return {
26177 event: e,
26178 touch: makeTouch(e)
26179 };
26180 };
26181 var addPointer = function addPointer(e) {
26182 pointers.push(makePointer(e));
26183 };
26184 var removePointer = function removePointer(e) {
26185 for (var i = 0; i < pointers.length; i++) {
26186 var p = pointers[i];
26187 if (p.event.pointerId === e.pointerId) {
26188 pointers.splice(i, 1);
26189 return;
26190 }
26191 }
26192 };
26193 var updatePointer = function updatePointer(e) {
26194 var p = pointers.filter(function (p) {
26195 return p.event.pointerId === e.pointerId;
26196 })[0];
26197 p.event = e;
26198 p.touch = makeTouch(e);
26199 };
26200 var addTouchesToEvent = function addTouchesToEvent(e) {
26201 e.touches = pointers.map(function (p) {
26202 return p.touch;
26203 });
26204 };
26205 var pointerIsMouse = function pointerIsMouse(e) {
26206 return e.pointerType === 'mouse' || e.pointerType === 4;
26207 };
26208 r.registerBinding(r.container, 'pointerdown', function (e) {
26209 if (pointerIsMouse(e)) {
26210 return;
26211 } // mouse already handled
26212
26213 e.preventDefault();
26214 addPointer(e);
26215 addTouchesToEvent(e);
26216 touchstartHandler(e);
26217 });
26218 r.registerBinding(r.container, 'pointerup', function (e) {
26219 if (pointerIsMouse(e)) {
26220 return;
26221 } // mouse already handled
26222
26223 removePointer(e);
26224 addTouchesToEvent(e);
26225 touchendHandler(e);
26226 });
26227 r.registerBinding(r.container, 'pointercancel', function (e) {
26228 if (pointerIsMouse(e)) {
26229 return;
26230 } // mouse already handled
26231
26232 removePointer(e);
26233 addTouchesToEvent(e);
26234 touchcancelHandler(e);
26235 });
26236 r.registerBinding(r.container, 'pointermove', function (e) {
26237 if (pointerIsMouse(e)) {
26238 return;
26239 } // mouse already handled
26240
26241 e.preventDefault();
26242 updatePointer(e);
26243 addTouchesToEvent(e);
26244 touchmoveHandler(e);
26245 });
26246 }
26247};
26248
26249var BRp$2 = {};
26250BRp$2.generatePolygon = function (name, points) {
26251 return this.nodeShapes[name] = {
26252 renderer: this,
26253 name: name,
26254 points: points,
26255 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26256 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26257 },
26258 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26259 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26260 },
26261 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26262 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26263 }
26264 };
26265};
26266BRp$2.generateEllipse = function () {
26267 return this.nodeShapes['ellipse'] = {
26268 renderer: this,
26269 name: 'ellipse',
26270 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26271 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26272 },
26273 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26274 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26275 },
26276 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26277 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26278 }
26279 };
26280};
26281BRp$2.generateRoundPolygon = function (name, points) {
26282 return this.nodeShapes[name] = {
26283 renderer: this,
26284 name: name,
26285 points: points,
26286 getOrCreateCorners: function getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, field) {
26287 if (rs[field] !== undefined && rs[field + '-cx'] === centerX && rs[field + '-cy'] === centerY) {
26288 return rs[field];
26289 }
26290 rs[field] = new Array(points.length / 2);
26291 rs[field + '-cx'] = centerX;
26292 rs[field + '-cy'] = centerY;
26293 var halfW = width / 2;
26294 var halfH = height / 2;
26295 cornerRadius = cornerRadius === 'auto' ? getRoundPolygonRadius(width, height) : cornerRadius;
26296 var p = new Array(points.length / 2);
26297 for (var _i = 0; _i < points.length / 2; _i++) {
26298 p[_i] = {
26299 x: centerX + halfW * points[_i * 2],
26300 y: centerY + halfH * points[_i * 2 + 1]
26301 };
26302 }
26303 var i,
26304 p1,
26305 p2,
26306 p3,
26307 len = p.length;
26308 p1 = p[len - 1];
26309 // for each point
26310 for (i = 0; i < len; i++) {
26311 p2 = p[i % len];
26312 p3 = p[(i + 1) % len];
26313 rs[field][i] = getRoundCorner(p1, p2, p3, cornerRadius);
26314 p1 = p2;
26315 p2 = p3;
26316 }
26317 return rs[field];
26318 },
26319 draw: function draw(context, centerX, centerY, width, height, cornerRadius, rs) {
26320 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points, this.getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, 'drawCorners'));
26321 },
26322 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius, rs) {
26323 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height, padding, this.getOrCreateCorners(nodeX, nodeY, width, height, cornerRadius, rs, 'corners'));
26324 },
26325 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius, rs) {
26326 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height, this.getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, 'corners'));
26327 }
26328 };
26329};
26330BRp$2.generateRoundRectangle = function () {
26331 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26332 renderer: this,
26333 name: 'round-rectangle',
26334 points: generateUnitNgonPointsFitToSquare(4, 0),
26335 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26336 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, this.points, cornerRadius);
26337 },
26338 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26339 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding, cornerRadius);
26340 },
26341 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26342 var halfWidth = width / 2;
26343 var halfHeight = height / 2;
26344 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(width, height) : cornerRadius;
26345 cornerRadius = Math.min(halfWidth, halfHeight, cornerRadius);
26346 var diam = cornerRadius * 2;
26347
26348 // Check hBox
26349 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26350 return true;
26351 }
26352
26353 // Check vBox
26354 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26355 return true;
26356 }
26357
26358 // Check top left quarter circle
26359 if (checkInEllipse(x, y, diam, diam, centerX - halfWidth + cornerRadius, centerY - halfHeight + cornerRadius, padding)) {
26360 return true;
26361 }
26362
26363 // Check top right quarter circle
26364 if (checkInEllipse(x, y, diam, diam, centerX + halfWidth - cornerRadius, centerY - halfHeight + cornerRadius, padding)) {
26365 return true;
26366 }
26367
26368 // Check bottom right quarter circle
26369 if (checkInEllipse(x, y, diam, diam, centerX + halfWidth - cornerRadius, centerY + halfHeight - cornerRadius, padding)) {
26370 return true;
26371 }
26372
26373 // Check bottom left quarter circle
26374 if (checkInEllipse(x, y, diam, diam, centerX - halfWidth + cornerRadius, centerY + halfHeight - cornerRadius, padding)) {
26375 return true;
26376 }
26377 return false;
26378 }
26379 };
26380};
26381BRp$2.generateCutRectangle = function () {
26382 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26383 renderer: this,
26384 name: 'cut-rectangle',
26385 cornerLength: getCutRectangleCornerLength(),
26386 points: generateUnitNgonPointsFitToSquare(4, 0),
26387 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26388 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, null, cornerRadius);
26389 },
26390 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY, cornerRadius) {
26391 var cl = cornerRadius === 'auto' ? this.cornerLength : cornerRadius;
26392 var hh = height / 2;
26393 var hw = width / 2;
26394 var xBegin = centerX - hw;
26395 var xEnd = centerX + hw;
26396 var yBegin = centerY - hh;
26397 var yEnd = centerY + hh;
26398
26399 // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26400 return {
26401 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26402 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26403 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26404 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26405 };
26406 },
26407 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26408 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY, cornerRadius);
26409 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26410 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26411 },
26412 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26413 var cl = cornerRadius === 'auto' ? this.cornerLength : cornerRadius;
26414 // Check hBox
26415 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * cl, [0, -1], padding)) {
26416 return true;
26417 }
26418
26419 // Check vBox
26420 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * cl, height, [0, -1], padding)) {
26421 return true;
26422 }
26423 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26424 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26425 }
26426 };
26427};
26428BRp$2.generateBarrel = function () {
26429 return this.nodeShapes['barrel'] = {
26430 renderer: this,
26431 name: 'barrel',
26432 points: generateUnitNgonPointsFitToSquare(4, 0),
26433 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26434 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26435 },
26436 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26437 // use two fixed t values for the bezier curve approximation
26438
26439 var t0 = 0.15;
26440 var t1 = 0.5;
26441 var t2 = 0.85;
26442 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26443 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26444 // approximate curve pts based on the two t values
26445 var m0 = qbezierPtAt({
26446 x: pts[0],
26447 y: pts[1]
26448 }, {
26449 x: pts[2],
26450 y: pts[3]
26451 }, {
26452 x: pts[4],
26453 y: pts[5]
26454 }, t0);
26455 var m1 = qbezierPtAt({
26456 x: pts[0],
26457 y: pts[1]
26458 }, {
26459 x: pts[2],
26460 y: pts[3]
26461 }, {
26462 x: pts[4],
26463 y: pts[5]
26464 }, t1);
26465 var m2 = qbezierPtAt({
26466 x: pts[0],
26467 y: pts[1]
26468 }, {
26469 x: pts[2],
26470 y: pts[3]
26471 }, {
26472 x: pts[4],
26473 y: pts[5]
26474 }, t2);
26475 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26476 };
26477 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26478 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26479 },
26480 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26481 var hh = height / 2;
26482 var hw = width / 2;
26483 var xBegin = centerX - hw;
26484 var xEnd = centerX + hw;
26485 var yBegin = centerY - hh;
26486 var yEnd = centerY + hh;
26487 var curveConstants = getBarrelCurveConstants(width, height);
26488 var hOffset = curveConstants.heightOffset;
26489 var wOffset = curveConstants.widthOffset;
26490 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width;
26491
26492 // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26493 var pts = {
26494 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26495 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26496 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26497 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26498 };
26499 pts.topLeft.isTop = true;
26500 pts.topRight.isTop = true;
26501 pts.bottomLeft.isBottom = true;
26502 pts.bottomRight.isBottom = true;
26503 return pts;
26504 },
26505 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26506 var curveConstants = getBarrelCurveConstants(width, height);
26507 var hOffset = curveConstants.heightOffset;
26508 var wOffset = curveConstants.widthOffset;
26509
26510 // Check hBox
26511 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26512 return true;
26513 }
26514
26515 // Check vBox
26516 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26517 return true;
26518 }
26519 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26520 var getCurveT = function getCurveT(x, y, curvePts) {
26521 var x0 = curvePts[4];
26522 var x1 = curvePts[2];
26523 var x2 = curvePts[0];
26524 var y0 = curvePts[5];
26525 // var y1 = curvePts[ 3 ];
26526 var y2 = curvePts[1];
26527 var xMin = Math.min(x0, x2);
26528 var xMax = Math.max(x0, x2);
26529 var yMin = Math.min(y0, y2);
26530 var yMax = Math.max(y0, y2);
26531 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26532 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26533 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26534 var validRoots = roots.filter(function (r) {
26535 return 0 <= r && r <= 1;
26536 });
26537 if (validRoots.length > 0) {
26538 return validRoots[0];
26539 }
26540 }
26541 return null;
26542 };
26543 var curveRegions = Object.keys(barrelCurvePts);
26544 for (var i = 0; i < curveRegions.length; i++) {
26545 var corner = curveRegions[i];
26546 var cornerPts = barrelCurvePts[corner];
26547 var t = getCurveT(x, y, cornerPts);
26548 if (t == null) {
26549 continue;
26550 }
26551 var y0 = cornerPts[5];
26552 var y1 = cornerPts[3];
26553 var y2 = cornerPts[1];
26554 var bezY = qbezierAt(y0, y1, y2, t);
26555 if (cornerPts.isTop && bezY <= y) {
26556 return true;
26557 }
26558 if (cornerPts.isBottom && y <= bezY) {
26559 return true;
26560 }
26561 }
26562 return false;
26563 }
26564 };
26565};
26566BRp$2.generateBottomRoundrectangle = function () {
26567 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26568 renderer: this,
26569 name: 'bottom-round-rectangle',
26570 points: generateUnitNgonPointsFitToSquare(4, 0),
26571 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26572 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, this.points, cornerRadius);
26573 },
26574 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26575 var topStartX = nodeX - (width / 2 + padding);
26576 var topStartY = nodeY - (height / 2 + padding);
26577 var topEndY = topStartY;
26578 var topEndX = nodeX + (width / 2 + padding);
26579 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26580 if (topIntersections.length > 0) {
26581 return topIntersections;
26582 }
26583 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding, cornerRadius);
26584 },
26585 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26586 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(width, height) : cornerRadius;
26587 var diam = 2 * cornerRadius;
26588
26589 // Check hBox
26590 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26591 return true;
26592 }
26593
26594 // Check vBox
26595 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26596 return true;
26597 }
26598
26599 // check non-rounded top side
26600 var outerWidth = width / 2 + 2 * padding;
26601 var outerHeight = height / 2 + 2 * padding;
26602 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26603 if (pointInsidePolygonPoints(x, y, points)) {
26604 return true;
26605 }
26606
26607 // Check bottom right quarter circle
26608 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26609 return true;
26610 }
26611
26612 // Check bottom left quarter circle
26613 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26614 return true;
26615 }
26616 return false;
26617 }
26618 };
26619};
26620BRp$2.registerNodeShapes = function () {
26621 var nodeShapes = this.nodeShapes = {};
26622 var renderer = this;
26623 this.generateEllipse();
26624 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26625 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26626 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26627 nodeShapes['square'] = nodeShapes['rectangle'];
26628 this.generateRoundRectangle();
26629 this.generateCutRectangle();
26630 this.generateBarrel();
26631 this.generateBottomRoundrectangle();
26632 {
26633 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26634 this.generatePolygon('diamond', diamondPoints);
26635 this.generateRoundPolygon('round-diamond', diamondPoints);
26636 }
26637 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26638 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26639 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26640 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26641 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26642 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26643 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26644 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26645 var star5Points = new Array(20);
26646 {
26647 var outerPoints = generateUnitNgonPoints(5, 0);
26648 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5);
26649
26650 // Outer radius is 1; inner radius of star is smaller
26651 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26652 innerRadius *= 1.57;
26653 for (var i = 0; i < innerPoints.length / 2; i++) {
26654 innerPoints[i * 2] *= innerRadius;
26655 innerPoints[i * 2 + 1] *= innerRadius;
26656 }
26657 for (var i = 0; i < 20 / 4; i++) {
26658 star5Points[i * 4] = outerPoints[i * 2];
26659 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26660 star5Points[i * 4 + 2] = innerPoints[i * 2];
26661 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26662 }
26663 }
26664 star5Points = fitPolygonToSquare(star5Points);
26665 this.generatePolygon('star', star5Points);
26666 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26667 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26668 this.generatePolygon('right-rhomboid', [-0.333, -1, 1, -1, 0.333, 1, -1, 1]);
26669 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]);
26670 {
26671 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26672 this.generatePolygon('tag', tagPoints);
26673 this.generateRoundPolygon('round-tag', tagPoints);
26674 }
26675 nodeShapes.makePolygon = function (points) {
26676 // use caching on user-specified polygons so they are as fast as native shapes
26677
26678 var key = points.join('$');
26679 var name = 'polygon-' + key;
26680 var shape;
26681 if (shape = this[name]) {
26682 // got cached shape
26683 return shape;
26684 }
26685
26686 // create and cache new shape
26687 return renderer.generatePolygon(name, points);
26688 };
26689};
26690
26691var BRp$1 = {};
26692BRp$1.timeToRender = function () {
26693 return this.redrawTotalTime / this.redrawCount;
26694};
26695BRp$1.redraw = function (options) {
26696 options = options || staticEmptyObject();
26697 var r = this;
26698 if (r.averageRedrawTime === undefined) {
26699 r.averageRedrawTime = 0;
26700 }
26701 if (r.lastRedrawTime === undefined) {
26702 r.lastRedrawTime = 0;
26703 }
26704 if (r.lastDrawTime === undefined) {
26705 r.lastDrawTime = 0;
26706 }
26707 r.requestedFrame = true;
26708 r.renderOptions = options;
26709};
26710BRp$1.beforeRender = function (fn, priority) {
26711 // the renderer can't add tick callbacks when destroyed
26712 if (this.destroyed) {
26713 return;
26714 }
26715 if (priority == null) {
26716 error('Priority is not optional for beforeRender');
26717 }
26718 var cbs = this.beforeRenderCallbacks;
26719 cbs.push({
26720 fn: fn,
26721 priority: priority
26722 });
26723
26724 // higher priority callbacks executed first
26725 cbs.sort(function (a, b) {
26726 return b.priority - a.priority;
26727 });
26728};
26729var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26730 var cbs = r.beforeRenderCallbacks;
26731 for (var i = 0; i < cbs.length; i++) {
26732 cbs[i].fn(willDraw, startTime);
26733 }
26734};
26735BRp$1.startRenderLoop = function () {
26736 var r = this;
26737 var cy = r.cy;
26738 if (r.renderLoopStarted) {
26739 return;
26740 } else {
26741 r.renderLoopStarted = true;
26742 }
26743 var renderFn = function renderFn(requestTime) {
26744 if (r.destroyed) {
26745 return;
26746 }
26747 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26748 beforeRenderCallbacks(r, true, requestTime);
26749 var startTime = performanceNow();
26750 r.render(r.renderOptions);
26751 var endTime = r.lastDrawTime = performanceNow();
26752 if (r.averageRedrawTime === undefined) {
26753 r.averageRedrawTime = endTime - startTime;
26754 }
26755 if (r.redrawCount === undefined) {
26756 r.redrawCount = 0;
26757 }
26758 r.redrawCount++;
26759 if (r.redrawTotalTime === undefined) {
26760 r.redrawTotalTime = 0;
26761 }
26762 var duration = endTime - startTime;
26763 r.redrawTotalTime += duration;
26764 r.lastRedrawTime = duration;
26765
26766 // use a weighted average with a bias from the previous average so we don't spike so easily
26767 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26768 r.requestedFrame = false;
26769 } else {
26770 beforeRenderCallbacks(r, false, requestTime);
26771 }
26772 r.skipFrame = false;
26773 requestAnimationFrame(renderFn);
26774 };
26775 requestAnimationFrame(renderFn);
26776};
26777
26778var BaseRenderer = function BaseRenderer(options) {
26779 this.init(options);
26780};
26781var BR = BaseRenderer;
26782var BRp = BR.prototype;
26783BRp.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26784BRp.init = function (options) {
26785 var r = this;
26786 r.options = options;
26787 r.cy = options.cy;
26788 var ctr = r.container = options.cy.container();
26789 var containerWindow = r.cy.window();
26790
26791 // prepend a stylesheet in the head such that
26792 if (containerWindow) {
26793 var document = containerWindow.document;
26794 var head = document.head;
26795 var stylesheetId = '__________cytoscape_stylesheet';
26796 var className = '__________cytoscape_container';
26797 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26798 if (ctr.className.indexOf(className) < 0) {
26799 ctr.className = (ctr.className || '') + ' ' + className;
26800 }
26801 if (!stylesheetAlreadyExists) {
26802 var stylesheet = document.createElement('style');
26803 stylesheet.id = stylesheetId;
26804 stylesheet.textContent = '.' + className + ' { position: relative; }';
26805 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26806 }
26807
26808 var computedStyle = containerWindow.getComputedStyle(ctr);
26809 var position = computedStyle.getPropertyValue('position');
26810 if (position === 'static') {
26811 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26812 }
26813 }
26814 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26815
26816 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95];
26817
26818 //--Pointer-related data
26819 r.hoverData = {
26820 down: null,
26821 last: null,
26822 downTime: null,
26823 triggerMode: null,
26824 dragging: false,
26825 initialPan: [null, null],
26826 capture: false
26827 };
26828 r.dragData = {
26829 possibleDragElements: []
26830 };
26831 r.touchData = {
26832 start: null,
26833 capture: false,
26834 // These 3 fields related to tap, taphold events
26835 startPosition: [null, null, null, null, null, null],
26836 singleTouchStartTime: null,
26837 singleTouchMoved: true,
26838 now: [null, null, null, null, null, null],
26839 earlier: [null, null, null, null, null, null]
26840 };
26841 r.redraws = 0;
26842 r.showFps = options.showFps;
26843 r.debug = options.debug;
26844 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26845 r.textureOnViewport = options.textureOnViewport;
26846 r.wheelSensitivity = options.wheelSensitivity;
26847 r.motionBlurEnabled = options.motionBlur; // on by default
26848 r.forcedPixelRatio = number$1(options.pixelRatio) ? options.pixelRatio : null;
26849 r.motionBlur = options.motionBlur; // for initial kick off
26850 r.motionBlurOpacity = options.motionBlurOpacity;
26851 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26852 r.motionBlurPxRatio = 1;
26853 r.mbPxRBlurry = 1; //0.8;
26854 r.minMbLowQualFrames = 4;
26855 r.fullQualityMb = false;
26856 r.clearedForMotionBlur = [];
26857 r.desktopTapThreshold = options.desktopTapThreshold;
26858 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26859 r.touchTapThreshold = options.touchTapThreshold;
26860 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26861 r.tapholdDuration = 500;
26862 r.bindings = [];
26863 r.beforeRenderCallbacks = [];
26864 r.beforeRenderPriorities = {
26865 // higher priority execs before lower one
26866 animations: 400,
26867 eleCalcs: 300,
26868 eleTxrDeq: 200,
26869 lyrTxrDeq: 150,
26870 lyrTxrSkip: 100
26871 };
26872 r.registerNodeShapes();
26873 r.registerArrowShapes();
26874 r.registerCalculationListeners();
26875};
26876BRp.notify = function (eventName, eles) {
26877 var r = this;
26878 var cy = r.cy;
26879
26880 // the renderer can't be notified after it's destroyed
26881 if (this.destroyed) {
26882 return;
26883 }
26884 if (eventName === 'init') {
26885 r.load();
26886 return;
26887 }
26888 if (eventName === 'destroy') {
26889 r.destroy();
26890 return;
26891 }
26892 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26893 r.invalidateCachedZSortedEles();
26894 }
26895 if (eventName === 'viewport') {
26896 r.redrawHint('select', true);
26897 }
26898 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26899 r.invalidateContainerClientCoordsCache();
26900 r.matchCanvasSize(r.container);
26901 }
26902 r.redrawHint('eles', true);
26903 r.redrawHint('drag', true);
26904 this.startRenderLoop();
26905 this.redraw();
26906};
26907BRp.destroy = function () {
26908 var r = this;
26909 r.destroyed = true;
26910 r.cy.stopAnimationLoop();
26911 for (var i = 0; i < r.bindings.length; i++) {
26912 var binding = r.bindings[i];
26913 var b = binding;
26914 var tgt = b.target;
26915 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26916 }
26917 r.bindings = [];
26918 r.beforeRenderCallbacks = [];
26919 r.onUpdateEleCalcsFns = [];
26920 if (r.removeObserver) {
26921 r.removeObserver.disconnect();
26922 }
26923 if (r.styleObserver) {
26924 r.styleObserver.disconnect();
26925 }
26926 if (r.resizeObserver) {
26927 r.resizeObserver.disconnect();
26928 }
26929 if (r.labelCalcDiv) {
26930 try {
26931 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26932 } catch (e) {
26933 // ie10 issue #1014
26934 }
26935 }
26936};
26937BRp.isHeadless = function () {
26938 return false;
26939};
26940[BRp$f, BRp$5, BRp$4, BRp$3, BRp$2, BRp$1].forEach(function (props) {
26941 extend(BRp, props);
26942});
26943
26944var fullFpsTime = 1000 / 60; // assume 60 frames per second
26945
26946var defs = {
26947 setupDequeueing: function setupDequeueing(opts) {
26948 return function setupDequeueingImpl() {
26949 var self = this;
26950 var r = this.renderer;
26951 if (self.dequeueingSetup) {
26952 return;
26953 } else {
26954 self.dequeueingSetup = true;
26955 }
26956 var queueRedraw = debounce_1(function () {
26957 r.redrawHint('eles', true);
26958 r.redrawHint('drag', true);
26959 r.redraw();
26960 }, opts.deqRedrawThreshold);
26961 var dequeue = function dequeue(willDraw, frameStartTime) {
26962 var startTime = performanceNow();
26963 var avgRenderTime = r.averageRedrawTime;
26964 var renderTime = r.lastRedrawTime;
26965 var deqd = [];
26966 var extent = r.cy.extent();
26967 var pixelRatio = r.getPixelRatio();
26968
26969 // if we aren't in a tick that causes a draw, then the rendered style
26970 // queue won't automatically be flushed before dequeueing starts
26971 if (!willDraw) {
26972 r.flushRenderedStyleQueue();
26973 }
26974 while (true) {
26975 // eslint-disable-line no-constant-condition
26976 var now = performanceNow();
26977 var duration = now - startTime;
26978 var frameDuration = now - frameStartTime;
26979 if (renderTime < fullFpsTime) {
26980 // if we're rendering faster than the ideal fps, then do dequeueing
26981 // during all of the remaining frame time
26982
26983 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26984 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26985 break;
26986 }
26987 } else {
26988 if (willDraw) {
26989 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26990 break;
26991 }
26992 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
26993 break;
26994 }
26995 }
26996 var thisDeqd = opts.deq(self, pixelRatio, extent);
26997 if (thisDeqd.length > 0) {
26998 for (var i = 0; i < thisDeqd.length; i++) {
26999 deqd.push(thisDeqd[i]);
27000 }
27001 } else {
27002 break;
27003 }
27004 }
27005
27006 // callbacks on dequeue
27007 if (deqd.length > 0) {
27008 opts.onDeqd(self, deqd);
27009 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27010 queueRedraw();
27011 }
27012 }
27013 };
27014 var priority = opts.priority || noop$1;
27015 r.beforeRender(dequeue, priority(self));
27016 };
27017 }
27018};
27019
27020// Allows lookups for (ele, lvl) => cache.
27021// Uses keys so elements may share the same cache.
27022var ElementTextureCacheLookup = /*#__PURE__*/function () {
27023 function ElementTextureCacheLookup(getKey) {
27024 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27025 _classCallCheck(this, ElementTextureCacheLookup);
27026 this.idsByKey = new Map$2();
27027 this.keyForId = new Map$2();
27028 this.cachesByLvl = new Map$2();
27029 this.lvls = [];
27030 this.getKey = getKey;
27031 this.doesEleInvalidateKey = doesEleInvalidateKey;
27032 }
27033 _createClass(ElementTextureCacheLookup, [{
27034 key: "getIdsFor",
27035 value: function getIdsFor(key) {
27036 if (key == null) {
27037 error("Can not get id list for null key");
27038 }
27039 var idsByKey = this.idsByKey;
27040 var ids = this.idsByKey.get(key);
27041 if (!ids) {
27042 ids = new Set$1();
27043 idsByKey.set(key, ids);
27044 }
27045 return ids;
27046 }
27047 }, {
27048 key: "addIdForKey",
27049 value: function addIdForKey(key, id) {
27050 if (key != null) {
27051 this.getIdsFor(key).add(id);
27052 }
27053 }
27054 }, {
27055 key: "deleteIdForKey",
27056 value: function deleteIdForKey(key, id) {
27057 if (key != null) {
27058 this.getIdsFor(key)["delete"](id);
27059 }
27060 }
27061 }, {
27062 key: "getNumberOfIdsForKey",
27063 value: function getNumberOfIdsForKey(key) {
27064 if (key == null) {
27065 return 0;
27066 } else {
27067 return this.getIdsFor(key).size;
27068 }
27069 }
27070 }, {
27071 key: "updateKeyMappingFor",
27072 value: function updateKeyMappingFor(ele) {
27073 var id = ele.id();
27074 var prevKey = this.keyForId.get(id);
27075 var currKey = this.getKey(ele);
27076 this.deleteIdForKey(prevKey, id);
27077 this.addIdForKey(currKey, id);
27078 this.keyForId.set(id, currKey);
27079 }
27080 }, {
27081 key: "deleteKeyMappingFor",
27082 value: function deleteKeyMappingFor(ele) {
27083 var id = ele.id();
27084 var prevKey = this.keyForId.get(id);
27085 this.deleteIdForKey(prevKey, id);
27086 this.keyForId["delete"](id);
27087 }
27088 }, {
27089 key: "keyHasChangedFor",
27090 value: function keyHasChangedFor(ele) {
27091 var id = ele.id();
27092 var prevKey = this.keyForId.get(id);
27093 var newKey = this.getKey(ele);
27094 return prevKey !== newKey;
27095 }
27096 }, {
27097 key: "isInvalid",
27098 value: function isInvalid(ele) {
27099 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27100 }
27101 }, {
27102 key: "getCachesAt",
27103 value: function getCachesAt(lvl) {
27104 var cachesByLvl = this.cachesByLvl,
27105 lvls = this.lvls;
27106 var caches = cachesByLvl.get(lvl);
27107 if (!caches) {
27108 caches = new Map$2();
27109 cachesByLvl.set(lvl, caches);
27110 lvls.push(lvl);
27111 }
27112 return caches;
27113 }
27114 }, {
27115 key: "getCache",
27116 value: function getCache(key, lvl) {
27117 return this.getCachesAt(lvl).get(key);
27118 }
27119 }, {
27120 key: "get",
27121 value: function get(ele, lvl) {
27122 var key = this.getKey(ele);
27123 var cache = this.getCache(key, lvl);
27124
27125 // getting for an element may need to add to the id list b/c eles can share keys
27126 if (cache != null) {
27127 this.updateKeyMappingFor(ele);
27128 }
27129 return cache;
27130 }
27131 }, {
27132 key: "getForCachedKey",
27133 value: function getForCachedKey(ele, lvl) {
27134 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27135 var cache = this.getCache(key, lvl);
27136 return cache;
27137 }
27138 }, {
27139 key: "hasCache",
27140 value: function hasCache(key, lvl) {
27141 return this.getCachesAt(lvl).has(key);
27142 }
27143 }, {
27144 key: "has",
27145 value: function has(ele, lvl) {
27146 var key = this.getKey(ele);
27147 return this.hasCache(key, lvl);
27148 }
27149 }, {
27150 key: "setCache",
27151 value: function setCache(key, lvl, cache) {
27152 cache.key = key;
27153 this.getCachesAt(lvl).set(key, cache);
27154 }
27155 }, {
27156 key: "set",
27157 value: function set(ele, lvl, cache) {
27158 var key = this.getKey(ele);
27159 this.setCache(key, lvl, cache);
27160 this.updateKeyMappingFor(ele);
27161 }
27162 }, {
27163 key: "deleteCache",
27164 value: function deleteCache(key, lvl) {
27165 this.getCachesAt(lvl)["delete"](key);
27166 }
27167 }, {
27168 key: "delete",
27169 value: function _delete(ele, lvl) {
27170 var key = this.getKey(ele);
27171 this.deleteCache(key, lvl);
27172 }
27173 }, {
27174 key: "invalidateKey",
27175 value: function invalidateKey(key) {
27176 var _this = this;
27177 this.lvls.forEach(function (lvl) {
27178 return _this.deleteCache(key, lvl);
27179 });
27180 }
27181
27182 // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27183 }, {
27184 key: "invalidate",
27185 value: function invalidate(ele) {
27186 var id = ele.id();
27187 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27188
27189 this.deleteKeyMappingFor(ele);
27190 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27191 if (entireKeyInvalidated) {
27192 // clear mapping for current key
27193 this.invalidateKey(key);
27194 }
27195 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27196 }
27197 }]);
27198 return ElementTextureCacheLookup;
27199}();
27200
27201var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27202var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27203var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27204var maxLvl$1 = 3; // when larger than this scale just render directly (caching is not helpful)
27205var maxZoom$1 = 7.99; // beyond this zoom level, layered textures are not used
27206var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27207var defTxrWidth = 1024; // default/minimum texture width
27208var maxTxrW = 1024; // the maximum width of a texture
27209var maxTxrH = 1024; // the maximum height of a texture
27210var minUtility = 0.2; // if usage of texture is less than this, it is retired
27211var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27212var maxFullnessChecks = 10; // dequeued after this many checks
27213var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27214var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27215var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27216var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27217var deqRedrawThreshold$1 = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27218var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27219
27220var getTxrReasons = {
27221 dequeue: 'dequeue',
27222 downscale: 'downscale',
27223 highQuality: 'highQuality'
27224};
27225var initDefaults = defaults$g({
27226 getKey: null,
27227 doesEleInvalidateKey: falsify,
27228 drawElement: null,
27229 getBoundingBox: null,
27230 getRotationPoint: null,
27231 getRotationOffset: null,
27232 isVisible: trueify,
27233 allowEdgeTxrCaching: true,
27234 allowParentTxrCaching: true
27235});
27236var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27237 var self = this;
27238 self.renderer = renderer;
27239 self.onDequeues = [];
27240 var opts = initDefaults(initOptions);
27241 extend(self, opts);
27242 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27243 self.setupDequeueing();
27244};
27245var ETCp = ElementTextureCache.prototype;
27246ETCp.reasons = getTxrReasons;
27247
27248// the list of textures in which new subtextures for elements can be placed
27249ETCp.getTextureQueue = function (txrH) {
27250 var self = this;
27251 self.eleImgCaches = self.eleImgCaches || {};
27252 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27253};
27254
27255// the list of usused textures which can be recycled (in use in texture queue)
27256ETCp.getRetiredTextureQueue = function (txrH) {
27257 var self = this;
27258 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27259 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27260 return rtxtrQ;
27261};
27262
27263// queue of element draw requests at different scale levels
27264ETCp.getElementQueue = function () {
27265 var self = this;
27266 var q = self.eleCacheQueue = self.eleCacheQueue || new heap(function (a, b) {
27267 return b.reqs - a.reqs;
27268 });
27269 return q;
27270};
27271
27272// queue of element draw requests at different scale levels (element id lookup)
27273ETCp.getElementKeyToQueue = function () {
27274 var self = this;
27275 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27276 return k2q;
27277};
27278ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27279 var self = this;
27280 var r = this.renderer;
27281 var zoom = r.cy.zoom();
27282 var lookup = this.lookup;
27283 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27284 return null;
27285 }
27286 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27287 return null;
27288 }
27289 if (lvl == null) {
27290 lvl = Math.ceil(log2(zoom * pxRatio));
27291 }
27292 if (lvl < minLvl$1) {
27293 lvl = minLvl$1;
27294 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27295 return null;
27296 }
27297 var scale = Math.pow(2, lvl);
27298 var eleScaledH = bb.h * scale;
27299 var eleScaledW = bb.w * scale;
27300 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27301 if (!this.isVisible(ele, scaledLabelShown)) {
27302 return null;
27303 }
27304 var eleCache = lookup.get(ele, lvl);
27305
27306 // if this get was on an unused/invalidated cache, then restore the texture usage metric
27307 if (eleCache && eleCache.invalidated) {
27308 eleCache.invalidated = false;
27309 eleCache.texture.invalidatedWidth -= eleCache.width;
27310 }
27311 if (eleCache) {
27312 return eleCache;
27313 }
27314 var txrH; // which texture height this ele belongs to
27315
27316 if (eleScaledH <= minTxrH) {
27317 txrH = minTxrH;
27318 } else if (eleScaledH <= txrStepH) {
27319 txrH = txrStepH;
27320 } else {
27321 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27322 }
27323 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27324 return null; // caching large elements is not efficient
27325 }
27326
27327 var txrQ = self.getTextureQueue(txrH);
27328
27329 // first try the second last one in case it has space at the end
27330 var txr = txrQ[txrQ.length - 2];
27331 var addNewTxr = function addNewTxr() {
27332 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27333 };
27334
27335 // try the last one if there is no second last one
27336 if (!txr) {
27337 txr = txrQ[txrQ.length - 1];
27338 }
27339
27340 // if the last one doesn't exist, we need a first one
27341 if (!txr) {
27342 txr = addNewTxr();
27343 }
27344
27345 // if there's no room in the current texture, we need a new one
27346 if (txr.width - txr.usedWidth < eleScaledW) {
27347 txr = addNewTxr();
27348 }
27349 var scalableFrom = function scalableFrom(otherCache) {
27350 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27351 };
27352 var deqing = reason && reason === getTxrReasons.dequeue;
27353 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27354 var downscaleReq = reason && reason === getTxrReasons.downscale;
27355 var higherCache; // the nearest cache with a higher level
27356 for (var l = lvl + 1; l <= maxLvl$1; l++) {
27357 var c = lookup.get(ele, l);
27358 if (c) {
27359 higherCache = c;
27360 break;
27361 }
27362 }
27363 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27364 var downscale = function downscale() {
27365 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27366 };
27367
27368 // reset ele area in texture
27369 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27370 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27371 if (scalableFrom(oneUpCache)) {
27372 // then we can relatively cheaply rescale the existing image w/o rerendering
27373 downscale();
27374 } else if (scalableFrom(higherCache)) {
27375 // then use the higher cache for now and queue the next level down
27376 // to cheaply scale towards the smaller level
27377
27378 if (highQualityReq) {
27379 for (var _l = higherCache.level; _l > lvl; _l--) {
27380 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27381 }
27382 downscale();
27383 } else {
27384 self.queueElement(ele, higherCache.level - 1);
27385 return higherCache;
27386 }
27387 } else {
27388 var lowerCache; // the nearest cache with a lower level
27389 if (!deqing && !highQualityReq && !downscaleReq) {
27390 for (var _l2 = lvl - 1; _l2 >= minLvl$1; _l2--) {
27391 var _c = lookup.get(ele, _l2);
27392 if (_c) {
27393 lowerCache = _c;
27394 break;
27395 }
27396 }
27397 }
27398 if (scalableFrom(lowerCache)) {
27399 // then use the lower quality cache for now and queue the better one for later
27400
27401 self.queueElement(ele, lvl);
27402 return lowerCache;
27403 }
27404 txr.context.translate(txr.usedWidth, 0);
27405 txr.context.scale(scale, scale);
27406 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27407 txr.context.scale(1 / scale, 1 / scale);
27408 txr.context.translate(-txr.usedWidth, 0);
27409 }
27410 eleCache = {
27411 x: txr.usedWidth,
27412 texture: txr,
27413 level: lvl,
27414 scale: scale,
27415 width: eleScaledW,
27416 height: eleScaledH,
27417 scaledLabelShown: scaledLabelShown
27418 };
27419 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27420 txr.eleCaches.push(eleCache);
27421 lookup.set(ele, lvl, eleCache);
27422 self.checkTextureFullness(txr);
27423 return eleCache;
27424};
27425ETCp.invalidateElements = function (eles) {
27426 for (var i = 0; i < eles.length; i++) {
27427 this.invalidateElement(eles[i]);
27428 }
27429};
27430ETCp.invalidateElement = function (ele) {
27431 var self = this;
27432 var lookup = self.lookup;
27433 var caches = [];
27434 var invalid = lookup.isInvalid(ele);
27435 if (!invalid) {
27436 return; // override the invalidation request if the element key has not changed
27437 }
27438
27439 for (var lvl = minLvl$1; lvl <= maxLvl$1; lvl++) {
27440 var cache = lookup.getForCachedKey(ele, lvl);
27441 if (cache) {
27442 caches.push(cache);
27443 }
27444 }
27445 var noOtherElesUseCache = lookup.invalidate(ele);
27446 if (noOtherElesUseCache) {
27447 for (var i = 0; i < caches.length; i++) {
27448 var _cache = caches[i];
27449 var txr = _cache.texture;
27450
27451 // remove space from the texture it belongs to
27452 txr.invalidatedWidth += _cache.width;
27453
27454 // mark the cache as invalidated
27455 _cache.invalidated = true;
27456
27457 // retire the texture if its utility is low
27458 self.checkTextureUtility(txr);
27459 }
27460 }
27461
27462 // remove from queue since the old req was for the old state
27463 self.removeFromQueue(ele);
27464};
27465ETCp.checkTextureUtility = function (txr) {
27466 // invalidate all entries in the cache if the cache size is small
27467 if (txr.invalidatedWidth >= minUtility * txr.width) {
27468 this.retireTexture(txr);
27469 }
27470};
27471ETCp.checkTextureFullness = function (txr) {
27472 // if texture has been mostly filled and passed over several times, remove
27473 // it from the queue so we don't need to waste time looking at it to put new things
27474
27475 var self = this;
27476 var txrQ = self.getTextureQueue(txr.height);
27477 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27478 removeFromArray(txrQ, txr);
27479 } else {
27480 txr.fullnessChecks++;
27481 }
27482};
27483ETCp.retireTexture = function (txr) {
27484 var self = this;
27485 var txrH = txr.height;
27486 var txrQ = self.getTextureQueue(txrH);
27487 var lookup = this.lookup;
27488
27489 // retire the texture from the active / searchable queue:
27490
27491 removeFromArray(txrQ, txr);
27492 txr.retired = true;
27493
27494 // remove the refs from the eles to the caches:
27495
27496 var eleCaches = txr.eleCaches;
27497 for (var i = 0; i < eleCaches.length; i++) {
27498 var eleCache = eleCaches[i];
27499 lookup.deleteCache(eleCache.key, eleCache.level);
27500 }
27501 clearArray(eleCaches);
27502
27503 // add the texture to a retired queue so it can be recycled in future:
27504
27505 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27506 rtxtrQ.push(txr);
27507};
27508ETCp.addTexture = function (txrH, minW) {
27509 var self = this;
27510 var txrQ = self.getTextureQueue(txrH);
27511 var txr = {};
27512 txrQ.push(txr);
27513 txr.eleCaches = [];
27514 txr.height = txrH;
27515 txr.width = Math.max(defTxrWidth, minW);
27516 txr.usedWidth = 0;
27517 txr.invalidatedWidth = 0;
27518 txr.fullnessChecks = 0;
27519 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27520 txr.context = txr.canvas.getContext('2d');
27521 return txr;
27522};
27523ETCp.recycleTexture = function (txrH, minW) {
27524 var self = this;
27525 var txrQ = self.getTextureQueue(txrH);
27526 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27527 for (var i = 0; i < rtxtrQ.length; i++) {
27528 var txr = rtxtrQ[i];
27529 if (txr.width >= minW) {
27530 txr.retired = false;
27531 txr.usedWidth = 0;
27532 txr.invalidatedWidth = 0;
27533 txr.fullnessChecks = 0;
27534 clearArray(txr.eleCaches);
27535 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27536 txr.context.clearRect(0, 0, txr.width, txr.height);
27537 removeFromArray(rtxtrQ, txr);
27538 txrQ.push(txr);
27539 return txr;
27540 }
27541 }
27542};
27543ETCp.queueElement = function (ele, lvl) {
27544 var self = this;
27545 var q = self.getElementQueue();
27546 var k2q = self.getElementKeyToQueue();
27547 var key = this.getKey(ele);
27548 var existingReq = k2q[key];
27549 if (existingReq) {
27550 // use the max lvl b/c in between lvls are cheap to make
27551 existingReq.level = Math.max(existingReq.level, lvl);
27552 existingReq.eles.merge(ele);
27553 existingReq.reqs++;
27554 q.updateItem(existingReq);
27555 } else {
27556 var req = {
27557 eles: ele.spawn().merge(ele),
27558 level: lvl,
27559 reqs: 1,
27560 key: key
27561 };
27562 q.push(req);
27563 k2q[key] = req;
27564 }
27565};
27566ETCp.dequeue = function (pxRatio /*, extent*/) {
27567 var self = this;
27568 var q = self.getElementQueue();
27569 var k2q = self.getElementKeyToQueue();
27570 var dequeued = [];
27571 var lookup = self.lookup;
27572 for (var i = 0; i < maxDeqSize$1; i++) {
27573 if (q.size() > 0) {
27574 var req = q.pop();
27575 var key = req.key;
27576 var ele = req.eles[0]; // all eles have the same key
27577 var cacheExists = lookup.hasCache(ele, req.level);
27578
27579 // clear out the key to req lookup
27580 k2q[key] = null;
27581
27582 // dequeueing isn't necessary with an existing cache
27583 if (cacheExists) {
27584 continue;
27585 }
27586 dequeued.push(req);
27587 var bb = self.getBoundingBox(ele);
27588 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27589 } else {
27590 break;
27591 }
27592 }
27593 return dequeued;
27594};
27595ETCp.removeFromQueue = function (ele) {
27596 var self = this;
27597 var q = self.getElementQueue();
27598 var k2q = self.getElementKeyToQueue();
27599 var key = this.getKey(ele);
27600 var req = k2q[key];
27601 if (req != null) {
27602 if (req.eles.length === 1) {
27603 // remove if last ele in the req
27604 // bring to front of queue
27605 req.reqs = MAX_INT$1;
27606 q.updateItem(req);
27607 q.pop(); // remove from queue
27608
27609 k2q[key] = null; // remove from lookup map
27610 } else {
27611 // otherwise just remove ele from req
27612 req.eles.unmerge(ele);
27613 }
27614 }
27615};
27616ETCp.onDequeue = function (fn) {
27617 this.onDequeues.push(fn);
27618};
27619ETCp.offDequeue = function (fn) {
27620 removeFromArray(this.onDequeues, fn);
27621};
27622ETCp.setupDequeueing = defs.setupDequeueing({
27623 deqRedrawThreshold: deqRedrawThreshold$1,
27624 deqCost: deqCost$1,
27625 deqAvgCost: deqAvgCost$1,
27626 deqNoDrawCost: deqNoDrawCost$1,
27627 deqFastCost: deqFastCost$1,
27628 deq: function deq(self, pxRatio, extent) {
27629 return self.dequeue(pxRatio, extent);
27630 },
27631 onDeqd: function onDeqd(self, deqd) {
27632 for (var i = 0; i < self.onDequeues.length; i++) {
27633 var fn = self.onDequeues[i];
27634 fn(deqd);
27635 }
27636 },
27637 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27638 for (var i = 0; i < deqd.length; i++) {
27639 var eles = deqd[i].eles;
27640 for (var j = 0; j < eles.length; j++) {
27641 var bb = eles[j].boundingBox();
27642 if (boundingBoxesIntersect(bb, extent)) {
27643 return true;
27644 }
27645 }
27646 }
27647 return false;
27648 },
27649 priority: function priority(self) {
27650 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27651 }
27652});
27653
27654var defNumLayers = 1; // default number of layers to use
27655var minLvl = -4; // when scaling smaller than that we don't need to re-render
27656var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful)
27657var maxZoom = 3.99; // beyond this zoom level, layered textures are not used
27658var deqRedrawThreshold = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27659var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27660var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27661var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27662var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27663var deqFastCost = 0.9; // % of frame time to be used when >60fps
27664var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27665var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27666var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27667var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27668
27669// var log = function(){ console.log.apply( console, arguments ); };
27670
27671var LayeredTextureCache = function LayeredTextureCache(renderer) {
27672 var self = this;
27673 var r = self.renderer = renderer;
27674 var cy = r.cy;
27675 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27676
27677 self.firstGet = true;
27678 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27679 self.skipping = false;
27680 self.eleTxrDeqs = cy.collection();
27681 self.scheduleElementRefinement = debounce_1(function () {
27682 self.refineElementTextures(self.eleTxrDeqs);
27683 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27684 }, refineEleDebounceTime);
27685 r.beforeRender(function (willDraw, now) {
27686 if (now - self.lastInvalidationTime <= invalidThreshold) {
27687 self.skipping = true;
27688 } else {
27689 self.skipping = false;
27690 }
27691 }, r.beforeRenderPriorities.lyrTxrSkip);
27692 var qSort = function qSort(a, b) {
27693 return b.reqs - a.reqs;
27694 };
27695 self.layersQueue = new heap(qSort);
27696 self.setupDequeueing();
27697};
27698var LTCp = LayeredTextureCache.prototype;
27699var layerIdPool = 0;
27700var MAX_INT = Math.pow(2, 53) - 1;
27701LTCp.makeLayer = function (bb, lvl) {
27702 var scale = Math.pow(2, lvl);
27703 var w = Math.ceil(bb.w * scale);
27704 var h = Math.ceil(bb.h * scale);
27705 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27706 var layer = {
27707 id: layerIdPool = ++layerIdPool % MAX_INT,
27708 bb: bb,
27709 level: lvl,
27710 width: w,
27711 height: h,
27712 canvas: canvas,
27713 context: canvas.getContext('2d'),
27714 eles: [],
27715 elesQueue: [],
27716 reqs: 0
27717 };
27718
27719 // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27720
27721 var cxt = layer.context;
27722 var dx = -layer.bb.x1;
27723 var dy = -layer.bb.y1;
27724
27725 // do the transform on creation to save cycles (it's the same for all eles)
27726 cxt.scale(scale, scale);
27727 cxt.translate(dx, dy);
27728 return layer;
27729};
27730LTCp.getLayers = function (eles, pxRatio, lvl) {
27731 var self = this;
27732 var r = self.renderer;
27733 var cy = r.cy;
27734 var zoom = cy.zoom();
27735 var firstGet = self.firstGet;
27736 self.firstGet = false;
27737
27738 // log('--\nget layers with %s eles', eles.length);
27739 //log eles.map(function(ele){ return ele.id() }) );
27740
27741 if (lvl == null) {
27742 lvl = Math.ceil(log2(zoom * pxRatio));
27743 if (lvl < minLvl) {
27744 lvl = minLvl;
27745 } else if (zoom >= maxZoom || lvl > maxLvl) {
27746 return null;
27747 }
27748 }
27749 self.validateLayersElesOrdering(lvl, eles);
27750 var layersByLvl = self.layersByLevel;
27751 var scale = Math.pow(2, lvl);
27752 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27753 var bb;
27754 var lvlComplete = self.levelIsComplete(lvl, eles);
27755 var tmpLayers;
27756 var checkTempLevels = function checkTempLevels() {
27757 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27758 self.validateLayersElesOrdering(l, eles);
27759 if (self.levelIsComplete(l, eles)) {
27760 tmpLayers = layersByLvl[l];
27761 return true;
27762 }
27763 };
27764 var checkLvls = function checkLvls(dir) {
27765 if (tmpLayers) {
27766 return;
27767 }
27768 for (var l = lvl + dir; minLvl <= l && l <= maxLvl; l += dir) {
27769 if (canUseAsTmpLvl(l)) {
27770 break;
27771 }
27772 }
27773 };
27774 checkLvls(+1);
27775 checkLvls(-1);
27776
27777 // remove the invalid layers; they will be replaced as needed later in this function
27778 for (var i = layers.length - 1; i >= 0; i--) {
27779 var layer = layers[i];
27780 if (layer.invalid) {
27781 removeFromArray(layers, layer);
27782 }
27783 }
27784 };
27785 if (!lvlComplete) {
27786 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27787 // and later queue the current layerset so we can get the proper quality level soon
27788
27789 checkTempLevels();
27790 } else {
27791 // log('level complete, using existing layers\n--');
27792 return layers;
27793 }
27794 var getBb = function getBb() {
27795 if (!bb) {
27796 bb = makeBoundingBox();
27797 for (var i = 0; i < eles.length; i++) {
27798 updateBoundingBox(bb, eles[i].boundingBox());
27799 }
27800 }
27801 return bb;
27802 };
27803 var makeLayer = function makeLayer(opts) {
27804 opts = opts || {};
27805 var after = opts.after;
27806 getBb();
27807 var area = bb.w * scale * (bb.h * scale);
27808 if (area > maxLayerArea) {
27809 return null;
27810 }
27811 var layer = self.makeLayer(bb, lvl);
27812 if (after != null) {
27813 var index = layers.indexOf(after) + 1;
27814 layers.splice(index, 0, layer);
27815 } else if (opts.insert === undefined || opts.insert) {
27816 // no after specified => first layer made so put at start
27817 layers.unshift(layer);
27818 }
27819
27820 // if( tmpLayers ){
27821 //self.queueLayer( layer );
27822 // }
27823
27824 return layer;
27825 };
27826 if (self.skipping && !firstGet) {
27827 // log('skip layers');
27828 return null;
27829 }
27830
27831 // log('do layers');
27832
27833 var layer = null;
27834 var maxElesPerLayer = eles.length / defNumLayers;
27835 var allowLazyQueueing = !firstGet;
27836 for (var i = 0; i < eles.length; i++) {
27837 var ele = eles[i];
27838 var rs = ele._private.rscratch;
27839 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
27840
27841 // log('look at ele', ele.id());
27842
27843 var existingLayer = caches[lvl];
27844 if (existingLayer) {
27845 // reuse layer for later eles
27846 // log('reuse layer for', ele.id());
27847 layer = existingLayer;
27848 continue;
27849 }
27850 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27851 // log('make new layer for ele %s', ele.id());
27852
27853 layer = makeLayer({
27854 insert: true,
27855 after: layer
27856 });
27857
27858 // if now layer can be built then we can't use layers at this level
27859 if (!layer) {
27860 return null;
27861 }
27862
27863 // log('new layer with id %s', layer.id);
27864 }
27865
27866 if (tmpLayers || allowLazyQueueing) {
27867 // log('queue ele %s in layer %s', ele.id(), layer.id);
27868 self.queueLayer(layer, ele);
27869 } else {
27870 // log('draw ele %s in layer %s', ele.id(), layer.id);
27871 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27872 }
27873 layer.eles.push(ele);
27874 caches[lvl] = layer;
27875 }
27876
27877 // log('--');
27878
27879 if (tmpLayers) {
27880 // then we only queued the current layerset and can't draw it yet
27881 return tmpLayers;
27882 }
27883 if (allowLazyQueueing) {
27884 // log('lazy queue level', lvl);
27885 return null;
27886 }
27887 return layers;
27888};
27889
27890// a layer may want to use an ele cache of a higher level to avoid blurriness
27891// so the layer level might not equal the ele level
27892LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
27893 return lvl;
27894};
27895LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
27896 var self = this;
27897 var r = this.renderer;
27898 var context = layer.context;
27899 var bb = ele.boundingBox();
27900 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
27901 return;
27902 }
27903 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
27904 {
27905 r.setImgSmoothing(context, false);
27906 }
27907 {
27908 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
27909 }
27910 {
27911 r.setImgSmoothing(context, true);
27912 }
27913};
27914LTCp.levelIsComplete = function (lvl, eles) {
27915 var self = this;
27916 var layers = self.layersByLevel[lvl];
27917 if (!layers || layers.length === 0) {
27918 return false;
27919 }
27920 var numElesInLayers = 0;
27921 for (var i = 0; i < layers.length; i++) {
27922 var layer = layers[i];
27923
27924 // if there are any eles needed to be drawn yet, the level is not complete
27925 if (layer.reqs > 0) {
27926 return false;
27927 }
27928
27929 // if the layer is invalid, the level is not complete
27930 if (layer.invalid) {
27931 return false;
27932 }
27933 numElesInLayers += layer.eles.length;
27934 }
27935
27936 // we should have exactly the number of eles passed in to be complete
27937 if (numElesInLayers !== eles.length) {
27938 return false;
27939 }
27940 return true;
27941};
27942LTCp.validateLayersElesOrdering = function (lvl, eles) {
27943 var layers = this.layersByLevel[lvl];
27944 if (!layers) {
27945 return;
27946 }
27947
27948 // if in a layer the eles are not in the same order, then the layer is invalid
27949 // (i.e. there is an ele in between the eles in the layer)
27950
27951 for (var i = 0; i < layers.length; i++) {
27952 var layer = layers[i];
27953 var offset = -1;
27954
27955 // find the offset
27956 for (var j = 0; j < eles.length; j++) {
27957 if (layer.eles[0] === eles[j]) {
27958 offset = j;
27959 break;
27960 }
27961 }
27962 if (offset < 0) {
27963 // then the layer has nonexistent elements and is invalid
27964 this.invalidateLayer(layer);
27965 continue;
27966 }
27967
27968 // the eles in the layer must be in the same continuous order, else the layer is invalid
27969
27970 var o = offset;
27971 for (var j = 0; j < layer.eles.length; j++) {
27972 if (layer.eles[j] !== eles[o + j]) {
27973 // log('invalidate based on ordering', layer.id);
27974
27975 this.invalidateLayer(layer);
27976 break;
27977 }
27978 }
27979 }
27980};
27981LTCp.updateElementsInLayers = function (eles, update) {
27982 var self = this;
27983 var isEles = element(eles[0]);
27984
27985 // collect udpated elements (cascaded from the layers) and update each
27986 // layer itself along the way
27987 for (var i = 0; i < eles.length; i++) {
27988 var req = isEles ? null : eles[i];
27989 var ele = isEles ? eles[i] : eles[i].ele;
27990 var rs = ele._private.rscratch;
27991 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
27992 for (var l = minLvl; l <= maxLvl; l++) {
27993 var layer = caches[l];
27994 if (!layer) {
27995 continue;
27996 }
27997
27998 // if update is a request from the ele cache, then it affects only
27999 // the matching level
28000 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28001 continue;
28002 }
28003 update(layer, ele, req);
28004 }
28005 }
28006};
28007LTCp.haveLayers = function () {
28008 var self = this;
28009 var haveLayers = false;
28010 for (var l = minLvl; l <= maxLvl; l++) {
28011 var layers = self.layersByLevel[l];
28012 if (layers && layers.length > 0) {
28013 haveLayers = true;
28014 break;
28015 }
28016 }
28017 return haveLayers;
28018};
28019LTCp.invalidateElements = function (eles) {
28020 var self = this;
28021 if (eles.length === 0) {
28022 return;
28023 }
28024 self.lastInvalidationTime = performanceNow();
28025
28026 // log('update invalidate layer time from eles');
28027
28028 if (eles.length === 0 || !self.haveLayers()) {
28029 return;
28030 }
28031 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28032 self.invalidateLayer(layer);
28033 });
28034};
28035LTCp.invalidateLayer = function (layer) {
28036 // log('update invalidate layer time');
28037
28038 this.lastInvalidationTime = performanceNow();
28039 if (layer.invalid) {
28040 return;
28041 } // save cycles
28042
28043 var lvl = layer.level;
28044 var eles = layer.eles;
28045 var layers = this.layersByLevel[lvl];
28046
28047 // log('invalidate layer', layer.id );
28048
28049 removeFromArray(layers, layer);
28050 // layer.eles = [];
28051
28052 layer.elesQueue = [];
28053 layer.invalid = true;
28054 if (layer.replacement) {
28055 layer.replacement.invalid = true;
28056 }
28057 for (var i = 0; i < eles.length; i++) {
28058 var caches = eles[i]._private.rscratch.imgLayerCaches;
28059 if (caches) {
28060 caches[lvl] = null;
28061 }
28062 }
28063};
28064LTCp.refineElementTextures = function (eles) {
28065 var self = this;
28066
28067 // log('refine', eles.length);
28068
28069 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28070 var rLyr = layer.replacement;
28071 if (!rLyr) {
28072 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28073 rLyr.replaces = layer;
28074 rLyr.eles = layer.eles;
28075
28076 // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28077 }
28078
28079 if (!rLyr.reqs) {
28080 for (var i = 0; i < rLyr.eles.length; i++) {
28081 self.queueLayer(rLyr, rLyr.eles[i]);
28082 }
28083
28084 // log('queue replacement layer refinement', rLyr.id);
28085 }
28086 });
28087};
28088
28089LTCp.enqueueElementRefinement = function (ele) {
28090 this.eleTxrDeqs.merge(ele);
28091 this.scheduleElementRefinement();
28092};
28093LTCp.queueLayer = function (layer, ele) {
28094 var self = this;
28095 var q = self.layersQueue;
28096 var elesQ = layer.elesQueue;
28097 var hasId = elesQ.hasId = elesQ.hasId || {};
28098
28099 // if a layer is going to be replaced, queuing is a waste of time
28100 if (layer.replacement) {
28101 return;
28102 }
28103 if (ele) {
28104 if (hasId[ele.id()]) {
28105 return;
28106 }
28107 elesQ.push(ele);
28108 hasId[ele.id()] = true;
28109 }
28110 if (layer.reqs) {
28111 layer.reqs++;
28112 q.updateItem(layer);
28113 } else {
28114 layer.reqs = 1;
28115 q.push(layer);
28116 }
28117};
28118LTCp.dequeue = function (pxRatio) {
28119 var self = this;
28120 var q = self.layersQueue;
28121 var deqd = [];
28122 var eleDeqs = 0;
28123 while (eleDeqs < maxDeqSize) {
28124 if (q.size() === 0) {
28125 break;
28126 }
28127 var layer = q.peek();
28128
28129 // if a layer has been or will be replaced, then don't waste time with it
28130 if (layer.replacement) {
28131 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28132 q.pop();
28133 continue;
28134 }
28135
28136 // if this is a replacement layer that has been superceded, then forget it
28137 if (layer.replaces && layer !== layer.replaces.replacement) {
28138 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28139 q.pop();
28140 continue;
28141 }
28142 if (layer.invalid) {
28143 // log('replacement layer %s is invalid; dequeued', layer.id);
28144 q.pop();
28145 continue;
28146 }
28147 var ele = layer.elesQueue.shift();
28148 if (ele) {
28149 // log('dequeue layer %s', layer.id);
28150
28151 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28152 eleDeqs++;
28153 }
28154 if (deqd.length === 0) {
28155 // we need only one entry in deqd to queue redrawing etc
28156 deqd.push(true);
28157 }
28158
28159 // if the layer has all its eles done, then remove from the queue
28160 if (layer.elesQueue.length === 0) {
28161 q.pop();
28162 layer.reqs = 0;
28163
28164 // log('dequeue of layer %s complete', layer.id);
28165
28166 // when a replacement layer is dequeued, it replaces the old layer in the level
28167 if (layer.replaces) {
28168 self.applyLayerReplacement(layer);
28169 }
28170 self.requestRedraw();
28171 }
28172 }
28173 return deqd;
28174};
28175LTCp.applyLayerReplacement = function (layer) {
28176 var self = this;
28177 var layersInLevel = self.layersByLevel[layer.level];
28178 var replaced = layer.replaces;
28179 var index = layersInLevel.indexOf(replaced);
28180
28181 // if the replaced layer is not in the active list for the level, then replacing
28182 // refs would be a mistake (i.e. overwriting the true active layer)
28183 if (index < 0 || replaced.invalid) {
28184 // log('replacement layer would have no effect', layer.id);
28185 return;
28186 }
28187 layersInLevel[index] = layer; // replace level ref
28188
28189 // replace refs in eles
28190 for (var i = 0; i < layer.eles.length; i++) {
28191 var _p = layer.eles[i]._private;
28192 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28193 if (cache) {
28194 cache[layer.level] = layer;
28195 }
28196 }
28197
28198 // log('apply replacement layer %s over %s', layer.id, replaced.id);
28199
28200 self.requestRedraw();
28201};
28202LTCp.requestRedraw = debounce_1(function () {
28203 var r = this.renderer;
28204 r.redrawHint('eles', true);
28205 r.redrawHint('drag', true);
28206 r.redraw();
28207}, 100);
28208LTCp.setupDequeueing = defs.setupDequeueing({
28209 deqRedrawThreshold: deqRedrawThreshold,
28210 deqCost: deqCost,
28211 deqAvgCost: deqAvgCost,
28212 deqNoDrawCost: deqNoDrawCost,
28213 deqFastCost: deqFastCost,
28214 deq: function deq(self, pxRatio) {
28215 return self.dequeue(pxRatio);
28216 },
28217 onDeqd: noop$1,
28218 shouldRedraw: trueify,
28219 priority: function priority(self) {
28220 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28221 }
28222});
28223
28224var CRp$a = {};
28225var impl;
28226function polygon(context, points) {
28227 for (var i = 0; i < points.length; i++) {
28228 var pt = points[i];
28229 context.lineTo(pt.x, pt.y);
28230 }
28231}
28232function triangleBackcurve(context, points, controlPoint) {
28233 var firstPt;
28234 for (var i = 0; i < points.length; i++) {
28235 var pt = points[i];
28236 if (i === 0) {
28237 firstPt = pt;
28238 }
28239 context.lineTo(pt.x, pt.y);
28240 }
28241 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28242}
28243function triangleTee(context, trianglePoints, teePoints) {
28244 if (context.beginPath) {
28245 context.beginPath();
28246 }
28247 var triPts = trianglePoints;
28248 for (var i = 0; i < triPts.length; i++) {
28249 var pt = triPts[i];
28250 context.lineTo(pt.x, pt.y);
28251 }
28252 var teePts = teePoints;
28253 var firstTeePt = teePoints[0];
28254 context.moveTo(firstTeePt.x, firstTeePt.y);
28255 for (var i = 1; i < teePts.length; i++) {
28256 var pt = teePts[i];
28257 context.lineTo(pt.x, pt.y);
28258 }
28259 if (context.closePath) {
28260 context.closePath();
28261 }
28262}
28263function circleTriangle(context, trianglePoints, rx, ry, r) {
28264 if (context.beginPath) {
28265 context.beginPath();
28266 }
28267 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28268 var triPts = trianglePoints;
28269 var firstTrPt = triPts[0];
28270 context.moveTo(firstTrPt.x, firstTrPt.y);
28271 for (var i = 0; i < triPts.length; i++) {
28272 var pt = triPts[i];
28273 context.lineTo(pt.x, pt.y);
28274 }
28275 if (context.closePath) {
28276 context.closePath();
28277 }
28278}
28279function circle(context, rx, ry, r) {
28280 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28281}
28282CRp$a.arrowShapeImpl = function (name) {
28283 return (impl || (impl = {
28284 'polygon': polygon,
28285 'triangle-backcurve': triangleBackcurve,
28286 'triangle-tee': triangleTee,
28287 'circle-triangle': circleTriangle,
28288 'triangle-cross': triangleTee,
28289 'circle': circle
28290 }))[name];
28291};
28292
28293var CRp$9 = {};
28294CRp$9.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28295 var r = this;
28296 if (ele.isNode()) {
28297 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28298 } else {
28299 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28300 }
28301};
28302CRp$9.drawElementOverlay = function (context, ele) {
28303 var r = this;
28304 if (ele.isNode()) {
28305 r.drawNodeOverlay(context, ele);
28306 } else {
28307 r.drawEdgeOverlay(context, ele);
28308 }
28309};
28310CRp$9.drawElementUnderlay = function (context, ele) {
28311 var r = this;
28312 if (ele.isNode()) {
28313 r.drawNodeUnderlay(context, ele);
28314 } else {
28315 r.drawEdgeUnderlay(context, ele);
28316 }
28317};
28318CRp$9.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28319 var r = this;
28320 var bb = eleTxrCache.getBoundingBox(ele);
28321 if (bb.w === 0 || bb.h === 0) {
28322 return;
28323 } // ignore zero size case
28324
28325 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28326 if (eleCache != null) {
28327 var opacity = getOpacity(r, ele);
28328 if (opacity === 0) {
28329 return;
28330 }
28331 var theta = getRotation(r, ele);
28332 var x1 = bb.x1,
28333 y1 = bb.y1,
28334 w = bb.w,
28335 h = bb.h;
28336 var x, y, sx, sy, smooth;
28337 if (theta !== 0) {
28338 var rotPt = eleTxrCache.getRotationPoint(ele);
28339 sx = rotPt.x;
28340 sy = rotPt.y;
28341 context.translate(sx, sy);
28342 context.rotate(theta);
28343 smooth = r.getImgSmoothing(context);
28344 if (!smooth) {
28345 r.setImgSmoothing(context, true);
28346 }
28347 var off = eleTxrCache.getRotationOffset(ele);
28348 x = off.x;
28349 y = off.y;
28350 } else {
28351 x = x1;
28352 y = y1;
28353 }
28354 var oldGlobalAlpha;
28355 if (opacity !== 1) {
28356 oldGlobalAlpha = context.globalAlpha;
28357 context.globalAlpha = oldGlobalAlpha * opacity;
28358 }
28359 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28360 if (opacity !== 1) {
28361 context.globalAlpha = oldGlobalAlpha;
28362 }
28363 if (theta !== 0) {
28364 context.rotate(-theta);
28365 context.translate(-sx, -sy);
28366 if (!smooth) {
28367 r.setImgSmoothing(context, false);
28368 }
28369 }
28370 } else {
28371 eleTxrCache.drawElement(context, ele); // direct draw fallback
28372 }
28373};
28374
28375var getZeroRotation = function getZeroRotation() {
28376 return 0;
28377};
28378var getLabelRotation = function getLabelRotation(r, ele) {
28379 return r.getTextAngle(ele, null);
28380};
28381var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28382 return r.getTextAngle(ele, 'source');
28383};
28384var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28385 return r.getTextAngle(ele, 'target');
28386};
28387var getOpacity = function getOpacity(r, ele) {
28388 return ele.effectiveOpacity();
28389};
28390var getTextOpacity = function getTextOpacity(e, ele) {
28391 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28392};
28393CRp$9.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28394 var r = this;
28395 var _r$data = r.data,
28396 eleTxrCache = _r$data.eleTxrCache,
28397 lblTxrCache = _r$data.lblTxrCache,
28398 slbTxrCache = _r$data.slbTxrCache,
28399 tlbTxrCache = _r$data.tlbTxrCache;
28400 var bb = ele.boundingBox();
28401 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28402 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28403 return;
28404 }
28405 if (!extent || boundingBoxesIntersect(bb, extent)) {
28406 var isEdge = ele.isEdge();
28407 var badLine = ele.element()._private.rscratch.badLine;
28408 r.drawElementUnderlay(context, ele);
28409 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28410 if (!isEdge || !badLine) {
28411 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28412 }
28413 if (isEdge && !badLine) {
28414 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28415 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28416 }
28417 r.drawElementOverlay(context, ele);
28418 }
28419};
28420CRp$9.drawElements = function (context, eles) {
28421 var r = this;
28422 for (var i = 0; i < eles.length; i++) {
28423 var ele = eles[i];
28424 r.drawElement(context, ele);
28425 }
28426};
28427CRp$9.drawCachedElements = function (context, eles, pxRatio, extent) {
28428 var r = this;
28429 for (var i = 0; i < eles.length; i++) {
28430 var ele = eles[i];
28431 r.drawCachedElement(context, ele, pxRatio, extent);
28432 }
28433};
28434CRp$9.drawCachedNodes = function (context, eles, pxRatio, extent) {
28435 var r = this;
28436 for (var i = 0; i < eles.length; i++) {
28437 var ele = eles[i];
28438 if (!ele.isNode()) {
28439 continue;
28440 }
28441 r.drawCachedElement(context, ele, pxRatio, extent);
28442 }
28443};
28444CRp$9.drawLayeredElements = function (context, eles, pxRatio, extent) {
28445 var r = this;
28446 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28447 if (layers) {
28448 for (var i = 0; i < layers.length; i++) {
28449 var layer = layers[i];
28450 var bb = layer.bb;
28451 if (bb.w === 0 || bb.h === 0) {
28452 continue;
28453 }
28454 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28455 }
28456 } else {
28457 // fall back on plain caching if no layers
28458 r.drawCachedElements(context, eles, pxRatio, extent);
28459 }
28460};
28461
28462var CRp$8 = {};
28463CRp$8.drawEdge = function (context, edge, shiftToOriginWithBb) {
28464 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28465 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28466 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28467 var r = this;
28468 var rs = edge._private.rscratch;
28469 if (shouldDrawOpacity && !edge.visible()) {
28470 return;
28471 }
28472
28473 // if bezier ctrl pts can not be calculated, then die
28474 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28475 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28476 return;
28477 }
28478 var bb;
28479 if (shiftToOriginWithBb) {
28480 bb = shiftToOriginWithBb;
28481 context.translate(-bb.x1, -bb.y1);
28482 }
28483 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28484 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28485 var curveStyle = edge.pstyle('curve-style').value;
28486 var lineStyle = edge.pstyle('line-style').value;
28487 var edgeWidth = edge.pstyle('width').pfValue;
28488 var lineCap = edge.pstyle('line-cap').value;
28489 var lineOutlineWidth = edge.pstyle('line-outline-width').value;
28490 var lineOutlineColor = edge.pstyle('line-outline-color').value;
28491 var effectiveLineOpacity = opacity * lineOpacity;
28492 // separate arrow opacity would require arrow-opacity property
28493 var effectiveArrowOpacity = opacity * lineOpacity;
28494 var drawLine = function drawLine() {
28495 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28496 if (curveStyle === 'straight-triangle') {
28497 r.eleStrokeStyle(context, edge, strokeOpacity);
28498 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28499 } else {
28500 context.lineWidth = edgeWidth;
28501 context.lineCap = lineCap;
28502 r.eleStrokeStyle(context, edge, strokeOpacity);
28503 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28504 context.lineCap = 'butt'; // reset for other drawing functions
28505 }
28506 };
28507
28508 var drawLineOutline = function drawLineOutline() {
28509 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28510 context.lineWidth = edgeWidth + lineOutlineWidth;
28511 context.lineCap = lineCap;
28512 if (lineOutlineWidth > 0) {
28513 r.colorStrokeStyle(context, lineOutlineColor[0], lineOutlineColor[1], lineOutlineColor[2], strokeOpacity);
28514 } else {
28515 // do not draw any lineOutline
28516 context.lineCap = 'butt'; // reset for other drawing functions
28517 return;
28518 }
28519 if (curveStyle === 'straight-triangle') {
28520 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28521 } else {
28522 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28523 context.lineCap = 'butt'; // reset for other drawing functions
28524 }
28525 };
28526
28527 var drawOverlay = function drawOverlay() {
28528 if (!shouldDrawOverlay) {
28529 return;
28530 }
28531 r.drawEdgeOverlay(context, edge);
28532 };
28533 var drawUnderlay = function drawUnderlay() {
28534 if (!shouldDrawOverlay) {
28535 return;
28536 }
28537 r.drawEdgeUnderlay(context, edge);
28538 };
28539 var drawArrows = function drawArrows() {
28540 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28541 r.drawArrowheads(context, edge, arrowOpacity);
28542 };
28543 var drawText = function drawText() {
28544 r.drawElementText(context, edge, null, drawLabel);
28545 };
28546 context.lineJoin = 'round';
28547 var ghost = edge.pstyle('ghost').value === 'yes';
28548 if (ghost) {
28549 var gx = edge.pstyle('ghost-offset-x').pfValue;
28550 var gy = edge.pstyle('ghost-offset-y').pfValue;
28551 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28552 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28553 context.translate(gx, gy);
28554 drawLine(effectiveGhostOpacity);
28555 drawArrows(effectiveGhostOpacity);
28556 context.translate(-gx, -gy);
28557 } else {
28558 drawLineOutline();
28559 }
28560 drawUnderlay();
28561 drawLine();
28562 drawArrows();
28563 drawOverlay();
28564 drawText();
28565 if (shiftToOriginWithBb) {
28566 context.translate(bb.x1, bb.y1);
28567 }
28568};
28569var drawEdgeOverlayUnderlay = function drawEdgeOverlayUnderlay(overlayOrUnderlay) {
28570 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
28571 throw new Error('Invalid state');
28572 }
28573 return function (context, edge) {
28574 if (!edge.visible()) {
28575 return;
28576 }
28577 var opacity = edge.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
28578 if (opacity === 0) {
28579 return;
28580 }
28581 var r = this;
28582 var usePaths = r.usePaths();
28583 var rs = edge._private.rscratch;
28584 var padding = edge.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
28585 var width = 2 * padding;
28586 var color = edge.pstyle("".concat(overlayOrUnderlay, "-color")).value;
28587 context.lineWidth = width;
28588 if (rs.edgeType === 'self' && !usePaths) {
28589 context.lineCap = 'butt';
28590 } else {
28591 context.lineCap = 'round';
28592 }
28593 r.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28594 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28595 };
28596};
28597CRp$8.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
28598CRp$8.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
28599CRp$8.drawEdgePath = function (edge, context, pts, type) {
28600 var rs = edge._private.rscratch;
28601 var canvasCxt = context;
28602 var path;
28603 var pathCacheHit = false;
28604 var usePaths = this.usePaths();
28605 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28606 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28607 if (usePaths) {
28608 var pathCacheKey = pts.join('$');
28609 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28610 if (keyMatches) {
28611 path = context = rs.pathCache;
28612 pathCacheHit = true;
28613 } else {
28614 path = context = new Path2D();
28615 rs.pathCacheKey = pathCacheKey;
28616 rs.pathCache = path;
28617 }
28618 }
28619 if (canvasCxt.setLineDash) {
28620 // for very outofdate browsers
28621 switch (type) {
28622 case 'dotted':
28623 canvasCxt.setLineDash([1, 1]);
28624 break;
28625 case 'dashed':
28626 canvasCxt.setLineDash(lineDashPattern);
28627 canvasCxt.lineDashOffset = lineDashOffset;
28628 break;
28629 case 'solid':
28630 canvasCxt.setLineDash([]);
28631 break;
28632 }
28633 }
28634 if (!pathCacheHit && !rs.badLine) {
28635 if (context.beginPath) {
28636 context.beginPath();
28637 }
28638 context.moveTo(pts[0], pts[1]);
28639 switch (rs.edgeType) {
28640 case 'bezier':
28641 case 'self':
28642 case 'compound':
28643 case 'multibezier':
28644 for (var i = 2; i + 3 < pts.length; i += 4) {
28645 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28646 }
28647 break;
28648 case 'straight':
28649 case 'haystack':
28650 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28651 context.lineTo(pts[_i], pts[_i + 1]);
28652 }
28653 break;
28654 case 'segments':
28655 if (rs.isRound) {
28656 var _iterator = _createForOfIteratorHelper(rs.roundCorners),
28657 _step;
28658 try {
28659 for (_iterator.s(); !(_step = _iterator.n()).done;) {
28660 var corner = _step.value;
28661 drawPreparedRoundCorner(context, corner);
28662 }
28663 } catch (err) {
28664 _iterator.e(err);
28665 } finally {
28666 _iterator.f();
28667 }
28668 context.lineTo(pts[pts.length - 2], pts[pts.length - 1]);
28669 } else {
28670 for (var _i2 = 2; _i2 + 1 < pts.length; _i2 += 2) {
28671 context.lineTo(pts[_i2], pts[_i2 + 1]);
28672 }
28673 }
28674 break;
28675 }
28676 }
28677 context = canvasCxt;
28678 if (usePaths) {
28679 context.stroke(path);
28680 } else {
28681 context.stroke();
28682 }
28683
28684 // reset any line dashes
28685 if (context.setLineDash) {
28686 // for very outofdate browsers
28687 context.setLineDash([]);
28688 }
28689};
28690CRp$8.drawEdgeTrianglePath = function (edge, context, pts) {
28691 // use line stroke style for triangle fill style
28692 context.fillStyle = context.strokeStyle;
28693 var edgeWidth = edge.pstyle('width').pfValue;
28694 for (var i = 0; i + 1 < pts.length; i += 2) {
28695 var vector = [pts[i + 2] - pts[i], pts[i + 3] - pts[i + 1]];
28696 var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
28697 var normal = [vector[1] / length, -vector[0] / length];
28698 var triangleHead = [normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2];
28699 context.beginPath();
28700 context.moveTo(pts[i] - triangleHead[0], pts[i + 1] - triangleHead[1]);
28701 context.lineTo(pts[i] + triangleHead[0], pts[i + 1] + triangleHead[1]);
28702 context.lineTo(pts[i + 2], pts[i + 3]);
28703 context.closePath();
28704 context.fill();
28705 }
28706};
28707CRp$8.drawArrowheads = function (context, edge, opacity) {
28708 var rs = edge._private.rscratch;
28709 var isHaystack = rs.edgeType === 'haystack';
28710 if (!isHaystack) {
28711 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28712 }
28713 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28714 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28715 if (!isHaystack) {
28716 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28717 }
28718};
28719CRp$8.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28720 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28721 return;
28722 }
28723 var self = this;
28724 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28725 if (arrowShape === 'none') {
28726 return;
28727 }
28728 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28729 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28730 var edgeWidth = edge.pstyle('width').pfValue;
28731 var pArrowWidth = edge.pstyle(prefix + '-arrow-width');
28732 var arrowWidth = pArrowWidth.value === 'match-line' ? edgeWidth : pArrowWidth.pfValue;
28733 if (pArrowWidth.units === '%') arrowWidth *= edgeWidth;
28734 var edgeOpacity = edge.pstyle('opacity').value;
28735 if (opacity === undefined) {
28736 opacity = edgeOpacity;
28737 }
28738 var gco = context.globalCompositeOperation;
28739 if (opacity !== 1 || arrowFill === 'hollow') {
28740 // then extra clear is needed
28741 context.globalCompositeOperation = 'destination-out';
28742 self.colorFillStyle(context, 255, 255, 255, 1);
28743 self.colorStrokeStyle(context, 255, 255, 255, 1);
28744 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, arrowWidth, x, y, angle);
28745 context.globalCompositeOperation = gco;
28746 } // otherwise, the opaque arrow clears it for free :)
28747
28748 var color = edge.pstyle(prefix + '-arrow-color').value;
28749 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28750 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28751 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, arrowWidth, x, y, angle);
28752};
28753CRp$8.drawArrowShape = function (edge, context, fill, edgeWidth, shape, shapeWidth, x, y, angle) {
28754 var r = this;
28755 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28756 var pathCacheHit = false;
28757 var path;
28758 var canvasContext = context;
28759 var translation = {
28760 x: x,
28761 y: y
28762 };
28763 var scale = edge.pstyle('arrow-scale').value;
28764 var size = this.getArrowWidth(edgeWidth, scale);
28765 var shapeImpl = r.arrowShapes[shape];
28766 if (usePaths) {
28767 var cache = r.arrowPathCache = r.arrowPathCache || [];
28768 var key = hashString(shape);
28769 var cachedPath = cache[key];
28770 if (cachedPath != null) {
28771 path = context = cachedPath;
28772 pathCacheHit = true;
28773 } else {
28774 path = context = new Path2D();
28775 cache[key] = path;
28776 }
28777 }
28778 if (!pathCacheHit) {
28779 if (context.beginPath) {
28780 context.beginPath();
28781 }
28782 if (usePaths) {
28783 // store in the path cache with values easily manipulated later
28784 shapeImpl.draw(context, 1, 0, {
28785 x: 0,
28786 y: 0
28787 }, 1);
28788 } else {
28789 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28790 }
28791 if (context.closePath) {
28792 context.closePath();
28793 }
28794 }
28795 context = canvasContext;
28796 if (usePaths) {
28797 // set transform to arrow position/orientation
28798 context.translate(x, y);
28799 context.rotate(angle);
28800 context.scale(size, size);
28801 }
28802 if (fill === 'filled' || fill === 'both') {
28803 if (usePaths) {
28804 context.fill(path);
28805 } else {
28806 context.fill();
28807 }
28808 }
28809 if (fill === 'hollow' || fill === 'both') {
28810 context.lineWidth = shapeWidth / (usePaths ? size : 1);
28811 context.lineJoin = 'miter';
28812 if (usePaths) {
28813 context.stroke(path);
28814 } else {
28815 context.stroke();
28816 }
28817 }
28818 if (usePaths) {
28819 // reset transform by applying inverse
28820 context.scale(1 / size, 1 / size);
28821 context.rotate(-angle);
28822 context.translate(-x, -y);
28823 }
28824};
28825
28826var CRp$7 = {};
28827CRp$7.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28828 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28829 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
28830 return;
28831 }
28832 try {
28833 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
28834 } catch (e) {
28835 warn(e);
28836 }
28837};
28838CRp$7.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
28839 var r = this;
28840 var pos = node.position();
28841 var nodeX = pos.x;
28842 var nodeY = pos.y;
28843 var styleObj = node.cy().style();
28844 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
28845 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
28846 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
28847 var nodeW = node.width();
28848 var nodeH = node.height();
28849 var paddingX2 = node.padding() * 2;
28850 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28851 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28852 var rs = node._private.rscratch;
28853 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
28854 var shouldClip = clip === 'node';
28855 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
28856 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
28857 var cornerRadius = node.pstyle('corner-radius').value;
28858 if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
28859 var imgW = img.width || img.cachedW;
28860 var imgH = img.height || img.cachedH;
28861
28862 // workaround for broken browsers like ie
28863 if (null == imgW || null == imgH) {
28864 document.body.appendChild(img); // eslint-disable-line no-undef
28865
28866 imgW = img.cachedW = img.width || img.offsetWidth;
28867 imgH = img.cachedH = img.height || img.offsetHeight;
28868 document.body.removeChild(img); // eslint-disable-line no-undef
28869 }
28870
28871 var w = imgW;
28872 var h = imgH;
28873 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
28874 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
28875 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
28876 } else {
28877 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
28878 }
28879 }
28880 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
28881 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
28882 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
28883 } else {
28884 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
28885 }
28886 }
28887 if (w === 0 || h === 0) {
28888 return; // no point in drawing empty image (and chrome is broken in this case)
28889 }
28890
28891 if (fit === 'contain') {
28892 var scale = Math.min(nodeTW / w, nodeTH / h);
28893 w *= scale;
28894 h *= scale;
28895 } else if (fit === 'cover') {
28896 var scale = Math.max(nodeTW / w, nodeTH / h);
28897 w *= scale;
28898 h *= scale;
28899 }
28900 var x = nodeX - nodeTW / 2; // left
28901 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
28902 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
28903 if (posXUnits === '%') {
28904 x += (nodeTW - w) * posXPfVal;
28905 } else {
28906 x += posXPfVal;
28907 }
28908 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
28909 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
28910 if (offXUnits === '%') {
28911 x += (nodeTW - w) * offXPfVal;
28912 } else {
28913 x += offXPfVal;
28914 }
28915 var y = nodeY - nodeTH / 2; // top
28916 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
28917 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
28918 if (posYUnits === '%') {
28919 y += (nodeTH - h) * posYPfVal;
28920 } else {
28921 y += posYPfVal;
28922 }
28923 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
28924 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
28925 if (offYUnits === '%') {
28926 y += (nodeTH - h) * offYPfVal;
28927 } else {
28928 y += offYPfVal;
28929 }
28930 if (rs.pathCache) {
28931 x -= nodeX;
28932 y -= nodeY;
28933 nodeX = 0;
28934 nodeY = 0;
28935 }
28936 var gAlpha = context.globalAlpha;
28937 context.globalAlpha = imgOpacity;
28938 var smoothingEnabled = r.getImgSmoothing(context);
28939 var isSmoothingSwitched = false;
28940 if (smooth === 'no' && smoothingEnabled) {
28941 r.setImgSmoothing(context, false);
28942 isSmoothingSwitched = true;
28943 } else if (smooth === 'yes' && !smoothingEnabled) {
28944 r.setImgSmoothing(context, true);
28945 isSmoothingSwitched = true;
28946 }
28947 if (repeat === 'no-repeat') {
28948 if (shouldClip) {
28949 context.save();
28950 if (rs.pathCache) {
28951 context.clip(rs.pathCache);
28952 } else {
28953 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH, cornerRadius, rs);
28954 context.clip();
28955 }
28956 }
28957 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
28958 if (shouldClip) {
28959 context.restore();
28960 }
28961 } else {
28962 var pattern = context.createPattern(img, repeat);
28963 context.fillStyle = pattern;
28964 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH, cornerRadius, rs);
28965 context.translate(x, y);
28966 context.fill();
28967 context.translate(-x, -y);
28968 }
28969 context.globalAlpha = gAlpha;
28970 if (isSmoothingSwitched) {
28971 r.setImgSmoothing(context, smoothingEnabled);
28972 }
28973};
28974
28975var CRp$6 = {};
28976CRp$6.eleTextBiggerThanMin = function (ele, scale) {
28977 if (!scale) {
28978 var zoom = ele.cy().zoom();
28979 var pxRatio = this.getPixelRatio();
28980 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
28981
28982 scale = Math.pow(2, lvl);
28983 }
28984 var computedSize = ele.pstyle('font-size').pfValue * scale;
28985 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
28986 if (computedSize < minSize) {
28987 return false;
28988 }
28989 return true;
28990};
28991CRp$6.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
28992 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28993 var r = this;
28994 if (force == null) {
28995 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
28996 return;
28997 }
28998 } else if (force === false) {
28999 return;
29000 }
29001 if (ele.isNode()) {
29002 var label = ele.pstyle('label');
29003 if (!label || !label.value) {
29004 return;
29005 }
29006 var justification = r.getLabelJustification(ele);
29007 context.textAlign = justification;
29008 context.textBaseline = 'bottom';
29009 } else {
29010 var badLine = ele.element()._private.rscratch.badLine;
29011 var _label = ele.pstyle('label');
29012 var srcLabel = ele.pstyle('source-label');
29013 var tgtLabel = ele.pstyle('target-label');
29014 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29015 return;
29016 }
29017 context.textAlign = 'center';
29018 context.textBaseline = 'bottom';
29019 }
29020 var applyRotation = !shiftToOriginWithBb;
29021 var bb;
29022 if (shiftToOriginWithBb) {
29023 bb = shiftToOriginWithBb;
29024 context.translate(-bb.x1, -bb.y1);
29025 }
29026 if (prefix == null) {
29027 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29028 if (ele.isEdge()) {
29029 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29030 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29031 }
29032 } else {
29033 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29034 }
29035 if (shiftToOriginWithBb) {
29036 context.translate(bb.x1, bb.y1);
29037 }
29038};
29039CRp$6.getFontCache = function (context) {
29040 var cache;
29041 this.fontCaches = this.fontCaches || [];
29042 for (var i = 0; i < this.fontCaches.length; i++) {
29043 cache = this.fontCaches[i];
29044 if (cache.context === context) {
29045 return cache;
29046 }
29047 }
29048 cache = {
29049 context: context
29050 };
29051 this.fontCaches.push(cache);
29052 return cache;
29053};
29054
29055// set up canvas context with font
29056// returns transformed text string
29057CRp$6.setupTextStyle = function (context, ele) {
29058 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29059 // Font style
29060 var labelStyle = ele.pstyle('font-style').strValue;
29061 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29062 var labelFamily = ele.pstyle('font-family').strValue;
29063 var labelWeight = ele.pstyle('font-weight').strValue;
29064 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29065 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29066 var color = ele.pstyle('color').value;
29067 var outlineColor = ele.pstyle('text-outline-color').value;
29068 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29069 context.lineJoin = 'round'; // so text outlines aren't jagged
29070
29071 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29072 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29073};
29074
29075// TODO ensure re-used
29076function roundRect(ctx, x, y, width, height) {
29077 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29078 var stroke = arguments.length > 6 ? arguments[6] : undefined;
29079 ctx.beginPath();
29080 ctx.moveTo(x + radius, y);
29081 ctx.lineTo(x + width - radius, y);
29082 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29083 ctx.lineTo(x + width, y + height - radius);
29084 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29085 ctx.lineTo(x + radius, y + height);
29086 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29087 ctx.lineTo(x, y + radius);
29088 ctx.quadraticCurveTo(x, y, x + radius, y);
29089 ctx.closePath();
29090 if (stroke) ctx.stroke();else ctx.fill();
29091}
29092CRp$6.getTextAngle = function (ele, prefix) {
29093 var theta;
29094 var _p = ele._private;
29095 var rscratch = _p.rscratch;
29096 var pdash = prefix ? prefix + '-' : '';
29097 var rotation = ele.pstyle(pdash + 'text-rotation');
29098 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29099 if (rotation.strValue === 'autorotate') {
29100 theta = ele.isEdge() ? textAngle : 0;
29101 } else if (rotation.strValue === 'none') {
29102 theta = 0;
29103 } else {
29104 theta = rotation.pfValue;
29105 }
29106 return theta;
29107};
29108CRp$6.drawText = function (context, ele, prefix) {
29109 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29110 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29111 var _p = ele._private;
29112 var rscratch = _p.rscratch;
29113 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29114 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29115 return;
29116 }
29117
29118 // use 'main' as an alias for the main label (i.e. null prefix)
29119 if (prefix === 'main') {
29120 prefix = null;
29121 }
29122 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29123 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29124 var orgTextX, orgTextY; // used for rotation
29125 var text = this.getLabelText(ele, prefix);
29126 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29127 this.setupTextStyle(context, ele, useEleOpacity);
29128 var pdash = prefix ? prefix + '-' : '';
29129 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29130 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29131 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29132 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29133 var isEdge = ele.isEdge();
29134 var halign = ele.pstyle('text-halign').value;
29135 var valign = ele.pstyle('text-valign').value;
29136 if (isEdge) {
29137 halign = 'center';
29138 valign = 'center';
29139 }
29140 textX += marginX;
29141 textY += marginY;
29142 var theta;
29143 if (!applyRotation) {
29144 theta = 0;
29145 } else {
29146 theta = this.getTextAngle(ele, prefix);
29147 }
29148 if (theta !== 0) {
29149 orgTextX = textX;
29150 orgTextY = textY;
29151 context.translate(orgTextX, orgTextY);
29152 context.rotate(theta);
29153 textX = 0;
29154 textY = 0;
29155 }
29156 switch (valign) {
29157 case 'top':
29158 break;
29159 case 'center':
29160 textY += textH / 2;
29161 break;
29162 case 'bottom':
29163 textY += textH;
29164 break;
29165 }
29166 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29167 var borderOpacity = ele.pstyle('text-border-opacity').value;
29168 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29169 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29170 var styleShape = ele.pstyle('text-background-shape').strValue;
29171 var rounded = styleShape.indexOf('round') === 0;
29172 var roundRadius = 2;
29173 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29174 var bgX = textX - backgroundPadding;
29175 switch (halign) {
29176 case 'left':
29177 bgX -= textW;
29178 break;
29179 case 'center':
29180 bgX -= textW / 2;
29181 break;
29182 }
29183 var bgY = textY - textH - backgroundPadding;
29184 var bgW = textW + 2 * backgroundPadding;
29185 var bgH = textH + 2 * backgroundPadding;
29186 if (backgroundOpacity > 0) {
29187 var textFill = context.fillStyle;
29188 var textBackgroundColor = ele.pstyle('text-background-color').value;
29189 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29190 if (rounded) {
29191 roundRect(context, bgX, bgY, bgW, bgH, roundRadius);
29192 } else {
29193 context.fillRect(bgX, bgY, bgW, bgH);
29194 }
29195 context.fillStyle = textFill;
29196 }
29197 if (textBorderWidth > 0 && borderOpacity > 0) {
29198 var textStroke = context.strokeStyle;
29199 var textLineWidth = context.lineWidth;
29200 var textBorderColor = ele.pstyle('text-border-color').value;
29201 var textBorderStyle = ele.pstyle('text-border-style').value;
29202 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29203 context.lineWidth = textBorderWidth;
29204 if (context.setLineDash) {
29205 // for very outofdate browsers
29206 switch (textBorderStyle) {
29207 case 'dotted':
29208 context.setLineDash([1, 1]);
29209 break;
29210 case 'dashed':
29211 context.setLineDash([4, 2]);
29212 break;
29213 case 'double':
29214 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29215 context.setLineDash([]);
29216 break;
29217 case 'solid':
29218 context.setLineDash([]);
29219 break;
29220 }
29221 }
29222 if (rounded) {
29223 roundRect(context, bgX, bgY, bgW, bgH, roundRadius, 'stroke');
29224 } else {
29225 context.strokeRect(bgX, bgY, bgW, bgH);
29226 }
29227 if (textBorderStyle === 'double') {
29228 var whiteWidth = textBorderWidth / 2;
29229 if (rounded) {
29230 roundRect(context, bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2, roundRadius, 'stroke');
29231 } else {
29232 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29233 }
29234 }
29235 if (context.setLineDash) {
29236 // for very outofdate browsers
29237 context.setLineDash([]);
29238 }
29239 context.lineWidth = textLineWidth;
29240 context.strokeStyle = textStroke;
29241 }
29242 }
29243 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29244
29245 if (lineWidth > 0) {
29246 context.lineWidth = lineWidth;
29247 }
29248 if (ele.pstyle('text-wrap').value === 'wrap') {
29249 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29250 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29251 var halfTextW = textW / 2;
29252 var justification = this.getLabelJustification(ele);
29253 if (justification === 'auto') ; else if (halign === 'left') {
29254 // auto justification : right
29255 if (justification === 'left') {
29256 textX += -textW;
29257 } else if (justification === 'center') {
29258 textX += -halfTextW;
29259 } // else same as auto
29260 } else if (halign === 'center') {
29261 // auto justfication : center
29262 if (justification === 'left') {
29263 textX += -halfTextW;
29264 } else if (justification === 'right') {
29265 textX += halfTextW;
29266 } // else same as auto
29267 } else if (halign === 'right') {
29268 // auto justification : left
29269 if (justification === 'center') {
29270 textX += halfTextW;
29271 } else if (justification === 'right') {
29272 textX += textW;
29273 } // else same as auto
29274 }
29275
29276 switch (valign) {
29277 case 'top':
29278 textY -= (lines.length - 1) * lineHeight;
29279 break;
29280 case 'center':
29281 case 'bottom':
29282 textY -= (lines.length - 1) * lineHeight;
29283 break;
29284 }
29285 for (var l = 0; l < lines.length; l++) {
29286 if (lineWidth > 0) {
29287 context.strokeText(lines[l], textX, textY);
29288 }
29289 context.fillText(lines[l], textX, textY);
29290 textY += lineHeight;
29291 }
29292 } else {
29293 if (lineWidth > 0) {
29294 context.strokeText(text, textX, textY);
29295 }
29296 context.fillText(text, textX, textY);
29297 }
29298 if (theta !== 0) {
29299 context.rotate(-theta);
29300 context.translate(-orgTextX, -orgTextY);
29301 }
29302 }
29303};
29304
29305/* global Path2D */
29306var CRp$5 = {};
29307CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29308 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29309 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29310 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29311 var r = this;
29312 var nodeWidth, nodeHeight;
29313 var _p = node._private;
29314 var rs = _p.rscratch;
29315 var pos = node.position();
29316 if (!number$1(pos.x) || !number$1(pos.y)) {
29317 return; // can't draw node with undefined position
29318 }
29319
29320 if (shouldDrawOpacity && !node.visible()) {
29321 return;
29322 }
29323 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29324 var usePaths = r.usePaths();
29325 var path;
29326 var pathCacheHit = false;
29327 var padding = node.padding();
29328 nodeWidth = node.width() + 2 * padding;
29329 nodeHeight = node.height() + 2 * padding;
29330
29331 //
29332 // setup shift
29333
29334 var bb;
29335 if (shiftToOriginWithBb) {
29336 bb = shiftToOriginWithBb;
29337 context.translate(-bb.x1, -bb.y1);
29338 }
29339
29340 //
29341 // load bg image
29342
29343 var bgImgProp = node.pstyle('background-image');
29344 var urls = bgImgProp.value;
29345 var urlDefined = new Array(urls.length);
29346 var image = new Array(urls.length);
29347 var numImages = 0;
29348 for (var i = 0; i < urls.length; i++) {
29349 var url = urls[i];
29350 var defd = urlDefined[i] = url != null && url !== 'none';
29351 if (defd) {
29352 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29353 numImages++;
29354
29355 // get image, and if not loaded then ask to redraw when later loaded
29356 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29357 _p.backgroundTimestamp = Date.now();
29358 node.emitAndNotify('background');
29359 });
29360 }
29361 }
29362
29363 //
29364 // setup styles
29365
29366 var darkness = node.pstyle('background-blacken').value;
29367 var borderWidth = node.pstyle('border-width').pfValue;
29368 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29369 var borderColor = node.pstyle('border-color').value;
29370 var borderStyle = node.pstyle('border-style').value;
29371 var borderJoin = node.pstyle('border-join').value;
29372 var borderCap = node.pstyle('border-cap').value;
29373 var borderPosition = node.pstyle('border-position').value;
29374 var borderPattern = node.pstyle('border-dash-pattern').pfValue;
29375 var borderOffset = node.pstyle('border-dash-offset').pfValue;
29376 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29377 var outlineWidth = node.pstyle('outline-width').pfValue;
29378 var outlineColor = node.pstyle('outline-color').value;
29379 var outlineStyle = node.pstyle('outline-style').value;
29380 var outlineOpacity = node.pstyle('outline-opacity').value * eleOpacity;
29381 var outlineOffset = node.pstyle('outline-offset').value;
29382 var cornerRadius = node.pstyle('corner-radius').value;
29383 if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
29384 var setupShapeColor = function setupShapeColor() {
29385 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29386 r.eleFillStyle(context, node, bgOpy);
29387 };
29388 var setupBorderColor = function setupBorderColor() {
29389 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29390 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29391 };
29392 var setupOutlineColor = function setupOutlineColor() {
29393 var otlnOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : outlineOpacity;
29394 r.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], otlnOpy);
29395 };
29396
29397 //
29398 // setup shape
29399
29400 var getPath = function getPath(width, height, shape, points) {
29401 var pathCache = r.nodePathCache = r.nodePathCache || [];
29402 var key = hashStrings(shape === 'polygon' ? shape + ',' + points.join(',') : shape, '' + height, '' + width, '' + cornerRadius);
29403 var cachedPath = pathCache[key];
29404 var path;
29405 var cacheHit = false;
29406 if (cachedPath != null) {
29407 path = cachedPath;
29408 cacheHit = true;
29409 rs.pathCache = path;
29410 } else {
29411 path = new Path2D();
29412 pathCache[key] = rs.pathCache = path;
29413 }
29414 return {
29415 path: path,
29416 cacheHit: cacheHit
29417 };
29418 };
29419 var styleShape = node.pstyle('shape').strValue;
29420 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29421 if (usePaths) {
29422 context.translate(pos.x, pos.y);
29423 var shapePath = getPath(nodeWidth, nodeHeight, styleShape, shapePts);
29424 path = shapePath.path;
29425 pathCacheHit = shapePath.cacheHit;
29426 }
29427 var drawShape = function drawShape() {
29428 if (!pathCacheHit) {
29429 var npos = pos;
29430 if (usePaths) {
29431 npos = {
29432 x: 0,
29433 y: 0
29434 };
29435 }
29436 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight, cornerRadius, rs);
29437 }
29438 if (usePaths) {
29439 context.fill(path);
29440 } else {
29441 context.fill();
29442 }
29443 };
29444 var drawImages = function drawImages() {
29445 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29446 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29447 var prevBging = _p.backgrounding;
29448 var totalCompleted = 0;
29449 for (var _i = 0; _i < image.length; _i++) {
29450 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29451 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29452 totalCompleted++;
29453 continue;
29454 }
29455 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29456 totalCompleted++;
29457 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29458 }
29459 }
29460 _p.backgrounding = !(totalCompleted === numImages);
29461 if (prevBging !== _p.backgrounding) {
29462 // update style b/c :backgrounding state changed
29463 node.updateStyle(false);
29464 }
29465 };
29466 var drawPie = function drawPie() {
29467 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29468 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29469 if (r.hasPie(node)) {
29470 r.drawPie(context, node, pieOpacity);
29471
29472 // redraw/restore path if steps after pie need it
29473 if (redrawShape) {
29474 if (!usePaths) {
29475 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight, cornerRadius, rs);
29476 }
29477 }
29478 }
29479 };
29480 var darken = function darken() {
29481 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29482 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29483 var c = darkness > 0 ? 0 : 255;
29484 if (darkness !== 0) {
29485 r.colorFillStyle(context, c, c, c, opacity);
29486 if (usePaths) {
29487 context.fill(path);
29488 } else {
29489 context.fill();
29490 }
29491 }
29492 };
29493 var drawBorder = function drawBorder() {
29494 if (borderWidth > 0) {
29495 context.lineWidth = borderWidth;
29496 context.lineCap = borderCap;
29497 context.lineJoin = borderJoin;
29498 if (context.setLineDash) {
29499 // for very outofdate browsers
29500 switch (borderStyle) {
29501 case 'dotted':
29502 context.setLineDash([1, 1]);
29503 break;
29504 case 'dashed':
29505 context.setLineDash(borderPattern);
29506 context.lineDashOffset = borderOffset;
29507 break;
29508 case 'solid':
29509 case 'double':
29510 context.setLineDash([]);
29511 break;
29512 }
29513 }
29514 if (borderPosition !== 'center') {
29515 context.save();
29516 context.lineWidth *= 2;
29517 if (borderPosition === 'inside') {
29518 usePaths ? context.clip(path) : context.clip();
29519 } else {
29520 var region = new Path2D();
29521 region.rect(-nodeWidth / 2 - borderWidth, -nodeHeight / 2 - borderWidth, nodeWidth + 2 * borderWidth, nodeHeight + 2 * borderWidth);
29522 region.addPath(path);
29523 context.clip(region, 'evenodd');
29524 }
29525 usePaths ? context.stroke(path) : context.stroke();
29526 context.restore();
29527 } else {
29528 usePaths ? context.stroke(path) : context.stroke();
29529 }
29530 if (borderStyle === 'double') {
29531 context.lineWidth = borderWidth / 3;
29532 var gco = context.globalCompositeOperation;
29533 context.globalCompositeOperation = 'destination-out';
29534 if (usePaths) {
29535 context.stroke(path);
29536 } else {
29537 context.stroke();
29538 }
29539 context.globalCompositeOperation = gco;
29540 }
29541
29542 // reset in case we changed the border style
29543 if (context.setLineDash) {
29544 // for very outofdate browsers
29545 context.setLineDash([]);
29546 }
29547 }
29548 };
29549 var drawOutline = function drawOutline() {
29550 if (outlineWidth > 0) {
29551 context.lineWidth = outlineWidth;
29552 context.lineCap = 'butt';
29553 if (context.setLineDash) {
29554 // for very outofdate browsers
29555 switch (outlineStyle) {
29556 case 'dotted':
29557 context.setLineDash([1, 1]);
29558 break;
29559 case 'dashed':
29560 context.setLineDash([4, 2]);
29561 break;
29562 case 'solid':
29563 case 'double':
29564 context.setLineDash([]);
29565 break;
29566 }
29567 }
29568 var npos = pos;
29569 if (usePaths) {
29570 npos = {
29571 x: 0,
29572 y: 0
29573 };
29574 }
29575 var shape = r.getNodeShape(node);
29576 var bWidth = borderWidth;
29577 if (borderPosition === 'inside') bWidth = 0;
29578 if (borderPosition === 'outside') bWidth *= 2;
29579 var scaleX = (nodeWidth + bWidth + (outlineWidth + outlineOffset)) / nodeWidth;
29580 var scaleY = (nodeHeight + bWidth + (outlineWidth + outlineOffset)) / nodeHeight;
29581 var sWidth = nodeWidth * scaleX;
29582 var sHeight = nodeHeight * scaleY;
29583 var points = r.nodeShapes[shape].points;
29584 var _path;
29585 if (usePaths) {
29586 var outlinePath = getPath(sWidth, sHeight, shape, points);
29587 _path = outlinePath.path;
29588 }
29589
29590 // draw the outline path, either by using expanded points or by scaling
29591 // the dimensions, depending on shape
29592 if (shape === "ellipse") {
29593 r.drawEllipsePath(_path || context, npos.x, npos.y, sWidth, sHeight);
29594 } else if (['round-diamond', 'round-heptagon', 'round-hexagon', 'round-octagon', 'round-pentagon', 'round-polygon', 'round-triangle', 'round-tag'].includes(shape)) {
29595 var sMult = 0;
29596 var offsetX = 0;
29597 var offsetY = 0;
29598 if (shape === 'round-diamond') {
29599 sMult = (bWidth + outlineOffset + outlineWidth) * 1.4;
29600 } else if (shape === 'round-heptagon') {
29601 sMult = (bWidth + outlineOffset + outlineWidth) * 1.075;
29602 offsetY = -(bWidth / 2 + outlineOffset + outlineWidth) / 35;
29603 } else if (shape === 'round-hexagon') {
29604 sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
29605 } else if (shape === 'round-pentagon') {
29606 sMult = (bWidth + outlineOffset + outlineWidth) * 1.13;
29607 offsetY = -(bWidth / 2 + outlineOffset + outlineWidth) / 15;
29608 } else if (shape === 'round-tag') {
29609 sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
29610 offsetX = (bWidth / 2 + outlineWidth + outlineOffset) * .07;
29611 } else if (shape === 'round-triangle') {
29612 sMult = (bWidth + outlineOffset + outlineWidth) * (Math.PI / 2);
29613 offsetY = -(bWidth + outlineOffset / 2 + outlineWidth) / Math.PI;
29614 }
29615 if (sMult !== 0) {
29616 scaleX = (nodeWidth + sMult) / nodeWidth;
29617 sWidth = nodeWidth * scaleX;
29618 if (!['round-hexagon', 'round-tag'].includes(shape)) {
29619 scaleY = (nodeHeight + sMult) / nodeHeight;
29620 sHeight = nodeHeight * scaleY;
29621 }
29622 }
29623 cornerRadius = cornerRadius === 'auto' ? getRoundPolygonRadius(sWidth, sHeight) : cornerRadius;
29624 var halfW = sWidth / 2;
29625 var halfH = sHeight / 2;
29626 var radius = cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2;
29627 var p = new Array(points.length / 2);
29628 var corners = new Array(points.length / 2);
29629 for (var _i3 = 0; _i3 < points.length / 2; _i3++) {
29630 p[_i3] = {
29631 x: npos.x + offsetX + halfW * points[_i3 * 2],
29632 y: npos.y + offsetY + halfH * points[_i3 * 2 + 1]
29633 };
29634 }
29635 var _i2,
29636 p1,
29637 p2,
29638 p3,
29639 len = p.length;
29640 p1 = p[len - 1];
29641 // for each point
29642 for (_i2 = 0; _i2 < len; _i2++) {
29643 p2 = p[_i2 % len];
29644 p3 = p[(_i2 + 1) % len];
29645 corners[_i2] = getRoundCorner(p1, p2, p3, radius);
29646 p1 = p2;
29647 p2 = p3;
29648 }
29649 r.drawRoundPolygonPath(_path || context, npos.x + offsetX, npos.y + offsetY, nodeWidth * scaleX, nodeHeight * scaleY, points, corners);
29650 } else if (['roundrectangle', 'round-rectangle'].includes(shape)) {
29651 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(sWidth, sHeight) : cornerRadius;
29652 r.drawRoundRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2);
29653 } else if (['cutrectangle', 'cut-rectangle'].includes(shape)) {
29654 cornerRadius = cornerRadius === 'auto' ? getCutRectangleCornerLength() : cornerRadius;
29655 r.drawCutRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, null, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 4);
29656 } else if (['bottomroundrectangle', 'bottom-round-rectangle'].includes(shape)) {
29657 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(sWidth, sHeight) : cornerRadius;
29658 r.drawBottomRoundRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2);
29659 } else if (shape === "barrel") {
29660 r.drawBarrelPath(_path || context, npos.x, npos.y, sWidth, sHeight);
29661 } else if (shape.startsWith("polygon") || ['rhomboid', 'right-rhomboid', 'round-tag', 'tag', 'vee'].includes(shape)) {
29662 var pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
29663 points = joinLines(expandPolygon(points, pad));
29664 r.drawPolygonPath(_path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
29665 } else {
29666 var _pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
29667 points = joinLines(expandPolygon(points, -_pad));
29668 r.drawPolygonPath(_path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
29669 }
29670 if (usePaths) {
29671 context.stroke(_path);
29672 } else {
29673 context.stroke();
29674 }
29675 if (outlineStyle === 'double') {
29676 context.lineWidth = bWidth / 3;
29677 var gco = context.globalCompositeOperation;
29678 context.globalCompositeOperation = 'destination-out';
29679 if (usePaths) {
29680 context.stroke(_path);
29681 } else {
29682 context.stroke();
29683 }
29684 context.globalCompositeOperation = gco;
29685 }
29686
29687 // reset in case we changed the border style
29688 if (context.setLineDash) {
29689 // for very outofdate browsers
29690 context.setLineDash([]);
29691 }
29692 }
29693 };
29694 var drawOverlay = function drawOverlay() {
29695 if (shouldDrawOverlay) {
29696 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29697 }
29698 };
29699 var drawUnderlay = function drawUnderlay() {
29700 if (shouldDrawOverlay) {
29701 r.drawNodeUnderlay(context, node, pos, nodeWidth, nodeHeight);
29702 }
29703 };
29704 var drawText = function drawText() {
29705 r.drawElementText(context, node, null, drawLabel);
29706 };
29707 var ghost = node.pstyle('ghost').value === 'yes';
29708 if (ghost) {
29709 var gx = node.pstyle('ghost-offset-x').pfValue;
29710 var gy = node.pstyle('ghost-offset-y').pfValue;
29711 var ghostOpacity = node.pstyle('ghost-opacity').value;
29712 var effGhostOpacity = ghostOpacity * eleOpacity;
29713 context.translate(gx, gy);
29714 setupOutlineColor();
29715 drawOutline();
29716 setupShapeColor(ghostOpacity * bgOpacity);
29717 drawShape();
29718 drawImages(effGhostOpacity, true);
29719 setupBorderColor(ghostOpacity * borderOpacity);
29720 drawBorder();
29721 drawPie(darkness !== 0 || borderWidth !== 0);
29722 drawImages(effGhostOpacity, false);
29723 darken(effGhostOpacity);
29724 context.translate(-gx, -gy);
29725 }
29726 if (usePaths) {
29727 context.translate(-pos.x, -pos.y);
29728 }
29729 drawUnderlay();
29730 if (usePaths) {
29731 context.translate(pos.x, pos.y);
29732 }
29733 setupOutlineColor();
29734 drawOutline();
29735 setupShapeColor();
29736 drawShape();
29737 drawImages(eleOpacity, true);
29738 setupBorderColor();
29739 drawBorder();
29740 drawPie(darkness !== 0 || borderWidth !== 0);
29741 drawImages(eleOpacity, false);
29742 darken();
29743 if (usePaths) {
29744 context.translate(-pos.x, -pos.y);
29745 }
29746 drawText();
29747 drawOverlay();
29748
29749 //
29750 // clean up shift
29751
29752 if (shiftToOriginWithBb) {
29753 context.translate(bb.x1, bb.y1);
29754 }
29755};
29756var drawNodeOverlayUnderlay = function drawNodeOverlayUnderlay(overlayOrUnderlay) {
29757 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
29758 throw new Error('Invalid state');
29759 }
29760 return function (context, node, pos, nodeWidth, nodeHeight) {
29761 var r = this;
29762 if (!node.visible()) {
29763 return;
29764 }
29765 var padding = node.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
29766 var opacity = node.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
29767 var color = node.pstyle("".concat(overlayOrUnderlay, "-color")).value;
29768 var shape = node.pstyle("".concat(overlayOrUnderlay, "-shape")).value;
29769 var radius = node.pstyle("".concat(overlayOrUnderlay, "-corner-radius")).value;
29770 if (opacity > 0) {
29771 pos = pos || node.position();
29772 if (nodeWidth == null || nodeHeight == null) {
29773 var _padding = node.padding();
29774 nodeWidth = node.width() + 2 * _padding;
29775 nodeHeight = node.height() + 2 * _padding;
29776 }
29777 r.colorFillStyle(context, color[0], color[1], color[2], opacity);
29778 r.nodeShapes[shape].draw(context, pos.x, pos.y, nodeWidth + padding * 2, nodeHeight + padding * 2, radius);
29779 context.fill();
29780 }
29781 };
29782};
29783CRp$5.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
29784CRp$5.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay');
29785
29786// does the node have at least one pie piece?
29787CRp$5.hasPie = function (node) {
29788 node = node[0]; // ensure ele ref
29789
29790 return node._private.hasPie;
29791};
29792CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29793 node = node[0]; // ensure ele ref
29794 pos = pos || node.position();
29795 var cyStyle = node.cy().style();
29796 var pieSize = node.pstyle('pie-size');
29797 var x = pos.x;
29798 var y = pos.y;
29799 var nodeW = node.width();
29800 var nodeH = node.height();
29801 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29802 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29803 var usePaths = this.usePaths();
29804 if (usePaths) {
29805 x = 0;
29806 y = 0;
29807 }
29808 if (pieSize.units === '%') {
29809 radius = radius * pieSize.pfValue;
29810 } else if (pieSize.pfValue !== undefined) {
29811 radius = pieSize.pfValue / 2;
29812 }
29813 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29814 // 1..N
29815 var size = node.pstyle('pie-' + i + '-background-size').value;
29816 var color = node.pstyle('pie-' + i + '-background-color').value;
29817 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29818 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29819
29820 // percent can't push beyond 1
29821 if (percent + lastPercent > 1) {
29822 percent = 1 - lastPercent;
29823 }
29824 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29825 var angleDelta = 2 * Math.PI * percent;
29826 var angleEnd = angleStart + angleDelta;
29827
29828 // ignore if
29829 // - zero size
29830 // - we're already beyond the full circle
29831 // - adding the current slice would go beyond the full circle
29832 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29833 continue;
29834 }
29835 context.beginPath();
29836 context.moveTo(x, y);
29837 context.arc(x, y, radius, angleStart, angleEnd);
29838 context.closePath();
29839 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29840 context.fill();
29841 lastPercent += percent;
29842 }
29843};
29844
29845var CRp$4 = {};
29846var motionBlurDelay = 100;
29847
29848// var isFirefox = typeof InstallTrigger !== 'undefined';
29849
29850CRp$4.getPixelRatio = function () {
29851 var context = this.data.contexts[0];
29852 if (this.forcedPixelRatio != null) {
29853 return this.forcedPixelRatio;
29854 }
29855 var containerWindow = this.cy.window();
29856 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29857 return (containerWindow.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29858};
29859
29860CRp$4.paintCache = function (context) {
29861 var caches = this.paintCaches = this.paintCaches || [];
29862 var needToCreateCache = true;
29863 var cache;
29864 for (var i = 0; i < caches.length; i++) {
29865 cache = caches[i];
29866 if (cache.context === context) {
29867 needToCreateCache = false;
29868 break;
29869 }
29870 }
29871 if (needToCreateCache) {
29872 cache = {
29873 context: context
29874 };
29875 caches.push(cache);
29876 }
29877 return cache;
29878};
29879CRp$4.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29880 var gradientStyle;
29881 var usePaths = this.usePaths();
29882 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29883 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29884 if (fill === 'radial-gradient') {
29885 if (ele.isEdge()) {
29886 var start = ele.sourceEndpoint(),
29887 end = ele.targetEndpoint(),
29888 mid = ele.midpoint();
29889 var d1 = dist(start, mid);
29890 var d2 = dist(end, mid);
29891 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29892 } else {
29893 var pos = usePaths ? {
29894 x: 0,
29895 y: 0
29896 } : ele.position(),
29897 width = ele.paddedWidth(),
29898 height = ele.paddedHeight();
29899 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29900 }
29901 } else {
29902 if (ele.isEdge()) {
29903 var _start = ele.sourceEndpoint(),
29904 _end = ele.targetEndpoint();
29905 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29906 } else {
29907 var _pos = usePaths ? {
29908 x: 0,
29909 y: 0
29910 } : ele.position(),
29911 _width = ele.paddedWidth(),
29912 _height = ele.paddedHeight(),
29913 halfWidth = _width / 2,
29914 halfHeight = _height / 2;
29915 var direction = ele.pstyle('background-gradient-direction').value;
29916 switch (direction) {
29917 case 'to-bottom':
29918 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
29919 break;
29920 case 'to-top':
29921 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
29922 break;
29923 case 'to-left':
29924 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
29925 break;
29926 case 'to-right':
29927 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
29928 break;
29929 case 'to-bottom-right':
29930 case 'to-right-bottom':
29931 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
29932 break;
29933 case 'to-top-right':
29934 case 'to-right-top':
29935 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
29936 break;
29937 case 'to-bottom-left':
29938 case 'to-left-bottom':
29939 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
29940 break;
29941 case 'to-top-left':
29942 case 'to-left-top':
29943 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
29944 break;
29945 }
29946 }
29947 }
29948 if (!gradientStyle) return null; // invalid gradient style
29949
29950 var hasPositions = positions.length === colors.length;
29951 var length = colors.length;
29952 for (var i = 0; i < length; i++) {
29953 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
29954 }
29955 return gradientStyle;
29956};
29957CRp$4.gradientFillStyle = function (context, ele, fill, opacity) {
29958 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
29959 if (!gradientStyle) return null; // error
29960 context.fillStyle = gradientStyle;
29961};
29962CRp$4.colorFillStyle = function (context, r, g, b, a) {
29963 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29964 // turn off for now, seems context does its own caching
29965
29966 // var cache = this.paintCache(context);
29967
29968 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29969
29970 // if( cache.fillStyle !== fillStyle ){
29971 // context.fillStyle = cache.fillStyle = fillStyle;
29972 // }
29973};
29974
29975CRp$4.eleFillStyle = function (context, ele, opacity) {
29976 var backgroundFill = ele.pstyle('background-fill').value;
29977 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
29978 this.gradientFillStyle(context, ele, backgroundFill, opacity);
29979 } else {
29980 var backgroundColor = ele.pstyle('background-color').value;
29981 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
29982 }
29983};
29984CRp$4.gradientStrokeStyle = function (context, ele, fill, opacity) {
29985 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
29986 if (!gradientStyle) return null; // error
29987 context.strokeStyle = gradientStyle;
29988};
29989CRp$4.colorStrokeStyle = function (context, r, g, b, a) {
29990 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29991 // turn off for now, seems context does its own caching
29992
29993 // var cache = this.paintCache(context);
29994
29995 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29996
29997 // if( cache.strokeStyle !== strokeStyle ){
29998 // context.strokeStyle = cache.strokeStyle = strokeStyle;
29999 // }
30000};
30001
30002CRp$4.eleStrokeStyle = function (context, ele, opacity) {
30003 var lineFill = ele.pstyle('line-fill').value;
30004 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30005 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30006 } else {
30007 var lineColor = ele.pstyle('line-color').value;
30008 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30009 }
30010};
30011
30012// Resize canvas
30013CRp$4.matchCanvasSize = function (container) {
30014 var r = this;
30015 var data = r.data;
30016 var bb = r.findContainerClientCoords();
30017 var width = bb[2];
30018 var height = bb[3];
30019 var pixelRatio = r.getPixelRatio();
30020 var mbPxRatio = r.motionBlurPxRatio;
30021 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30022 pixelRatio = mbPxRatio;
30023 }
30024 var canvasWidth = width * pixelRatio;
30025 var canvasHeight = height * pixelRatio;
30026 var canvas;
30027 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30028 return; // save cycles if same
30029 }
30030
30031 r.fontCaches = null; // resizing resets the style
30032
30033 var canvasContainer = data.canvasContainer;
30034 canvasContainer.style.width = width + 'px';
30035 canvasContainer.style.height = height + 'px';
30036 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30037 canvas = data.canvases[i];
30038 canvas.width = canvasWidth;
30039 canvas.height = canvasHeight;
30040 canvas.style.width = width + 'px';
30041 canvas.style.height = height + 'px';
30042 }
30043 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30044 canvas = data.bufferCanvases[i];
30045 canvas.width = canvasWidth;
30046 canvas.height = canvasHeight;
30047 canvas.style.width = width + 'px';
30048 canvas.style.height = height + 'px';
30049 }
30050 r.textureMult = 1;
30051 if (pixelRatio <= 1) {
30052 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30053 r.textureMult = 2;
30054 canvas.width = canvasWidth * r.textureMult;
30055 canvas.height = canvasHeight * r.textureMult;
30056 }
30057 r.canvasWidth = canvasWidth;
30058 r.canvasHeight = canvasHeight;
30059};
30060CRp$4.renderTo = function (cxt, zoom, pan, pxRatio) {
30061 this.render({
30062 forcedContext: cxt,
30063 forcedZoom: zoom,
30064 forcedPan: pan,
30065 drawAllLayers: true,
30066 forcedPxRatio: pxRatio
30067 });
30068};
30069CRp$4.render = function (options) {
30070 options = options || staticEmptyObject();
30071 var forcedContext = options.forcedContext;
30072 var drawAllLayers = options.drawAllLayers;
30073 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30074 var forcedZoom = options.forcedZoom;
30075 var forcedPan = options.forcedPan;
30076 var r = this;
30077 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30078 var cy = r.cy;
30079 var data = r.data;
30080 var needDraw = data.canvasNeedsRedraw;
30081 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30082 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30083 var mbPxRatio = r.motionBlurPxRatio;
30084 var hasCompoundNodes = cy.hasCompoundNodes();
30085 var inNodeDragGesture = r.hoverData.draggingEles;
30086 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30087 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30088 var motionBlurFadeEffect = motionBlur;
30089 if (!forcedContext) {
30090 if (r.prevPxRatio !== pixelRatio) {
30091 r.invalidateContainerClientCoordsCache();
30092 r.matchCanvasSize(r.container);
30093 r.redrawHint('eles', true);
30094 r.redrawHint('drag', true);
30095 }
30096 r.prevPxRatio = pixelRatio;
30097 }
30098 if (!forcedContext && r.motionBlurTimeout) {
30099 clearTimeout(r.motionBlurTimeout);
30100 }
30101 if (motionBlur) {
30102 if (r.mbFrames == null) {
30103 r.mbFrames = 0;
30104 }
30105 r.mbFrames++;
30106 if (r.mbFrames < 3) {
30107 // need several frames before even high quality motionblur
30108 motionBlurFadeEffect = false;
30109 }
30110
30111 // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30112 if (r.mbFrames > r.minMbLowQualFrames) {
30113 //r.fullQualityMb = false;
30114 r.motionBlurPxRatio = r.mbPxRBlurry;
30115 }
30116 }
30117 if (r.clearingMotionBlur) {
30118 r.motionBlurPxRatio = 1;
30119 }
30120
30121 // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30122 // because a rogue async texture frame would clear needDraw
30123 if (r.textureDrawLastFrame && !textureDraw) {
30124 needDraw[r.NODE] = true;
30125 needDraw[r.SELECT_BOX] = true;
30126 }
30127 var style = cy.style();
30128 var zoom = cy.zoom();
30129 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30130 var pan = cy.pan();
30131 var effectivePan = {
30132 x: pan.x,
30133 y: pan.y
30134 };
30135 var vp = {
30136 zoom: zoom,
30137 pan: {
30138 x: pan.x,
30139 y: pan.y
30140 }
30141 };
30142 var prevVp = r.prevViewport;
30143 var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y;
30144
30145 // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed)
30146 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30147 r.motionBlurPxRatio = 1;
30148 }
30149 if (forcedPan) {
30150 effectivePan = forcedPan;
30151 }
30152
30153 // apply pixel ratio
30154
30155 effectiveZoom *= pixelRatio;
30156 effectivePan.x *= pixelRatio;
30157 effectivePan.y *= pixelRatio;
30158 var eles = r.getCachedZSortedEles();
30159 function mbclear(context, x, y, w, h) {
30160 var gco = context.globalCompositeOperation;
30161 context.globalCompositeOperation = 'destination-out';
30162 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30163 context.fillRect(x, y, w, h);
30164 context.globalCompositeOperation = gco;
30165 }
30166 function setContextTransform(context, clear) {
30167 var ePan, eZoom, w, h;
30168 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30169 ePan = {
30170 x: pan.x * mbPxRatio,
30171 y: pan.y * mbPxRatio
30172 };
30173 eZoom = zoom * mbPxRatio;
30174 w = r.canvasWidth * mbPxRatio;
30175 h = r.canvasHeight * mbPxRatio;
30176 } else {
30177 ePan = effectivePan;
30178 eZoom = effectiveZoom;
30179 w = r.canvasWidth;
30180 h = r.canvasHeight;
30181 }
30182 context.setTransform(1, 0, 0, 1, 0, 0);
30183 if (clear === 'motionBlur') {
30184 mbclear(context, 0, 0, w, h);
30185 } else if (!forcedContext && (clear === undefined || clear)) {
30186 context.clearRect(0, 0, w, h);
30187 }
30188 if (!drawAllLayers) {
30189 context.translate(ePan.x, ePan.y);
30190 context.scale(eZoom, eZoom);
30191 }
30192 if (forcedPan) {
30193 context.translate(forcedPan.x, forcedPan.y);
30194 }
30195 if (forcedZoom) {
30196 context.scale(forcedZoom, forcedZoom);
30197 }
30198 }
30199 if (!textureDraw) {
30200 r.textureDrawLastFrame = false;
30201 }
30202 if (textureDraw) {
30203 r.textureDrawLastFrame = true;
30204 if (!r.textureCache) {
30205 r.textureCache = {};
30206 r.textureCache.bb = cy.mutableElements().boundingBox();
30207 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30208 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30209 cxt.setTransform(1, 0, 0, 1, 0, 0);
30210 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30211 r.render({
30212 forcedContext: cxt,
30213 drawOnlyNodeLayer: true,
30214 forcedPxRatio: pixelRatio * r.textureMult
30215 });
30216 var vp = r.textureCache.viewport = {
30217 zoom: cy.zoom(),
30218 pan: cy.pan(),
30219 width: r.canvasWidth,
30220 height: r.canvasHeight
30221 };
30222 vp.mpan = {
30223 x: (0 - vp.pan.x) / vp.zoom,
30224 y: (0 - vp.pan.y) / vp.zoom
30225 };
30226 }
30227 needDraw[r.DRAG] = false;
30228 needDraw[r.NODE] = false;
30229 var context = data.contexts[r.NODE];
30230 var texture = r.textureCache.texture;
30231 var vp = r.textureCache.viewport;
30232 context.setTransform(1, 0, 0, 1, 0, 0);
30233 if (motionBlur) {
30234 mbclear(context, 0, 0, vp.width, vp.height);
30235 } else {
30236 context.clearRect(0, 0, vp.width, vp.height);
30237 }
30238 var outsideBgColor = style.core('outside-texture-bg-color').value;
30239 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30240 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30241 context.fillRect(0, 0, vp.width, vp.height);
30242 var zoom = cy.zoom();
30243 setContextTransform(context, false);
30244 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30245 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30246 } else if (r.textureOnViewport && !forcedContext) {
30247 // clear the cache since we don't need it
30248 r.textureCache = null;
30249 }
30250 var extent = cy.extent();
30251 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30252 var hideEdges = r.hideEdgesOnViewport && vpManip;
30253 var needMbClear = [];
30254 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30255 if (needMbClear[r.NODE]) {
30256 r.clearedForMotionBlur[r.NODE] = true;
30257 }
30258 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30259 if (needMbClear[r.DRAG]) {
30260 r.clearedForMotionBlur[r.DRAG] = true;
30261 }
30262 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30263 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30264 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30265 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30266 setContextTransform(context, clear);
30267 if (hideEdges) {
30268 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30269 } else {
30270 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30271 }
30272 if (r.debug) {
30273 r.drawDebugPoints(context, eles.nondrag);
30274 }
30275 if (!drawAllLayers && !motionBlur) {
30276 needDraw[r.NODE] = false;
30277 }
30278 }
30279 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30280 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30281 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30282 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30283 if (hideEdges) {
30284 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30285 } else {
30286 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30287 }
30288 if (r.debug) {
30289 r.drawDebugPoints(context, eles.drag);
30290 }
30291 if (!drawAllLayers && !motionBlur) {
30292 needDraw[r.DRAG] = false;
30293 }
30294 }
30295 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30296 var context = forcedContext || data.contexts[r.SELECT_BOX];
30297 setContextTransform(context);
30298 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30299 var zoom = r.cy.zoom();
30300 var borderWidth = style.core('selection-box-border-width').value / zoom;
30301 context.lineWidth = borderWidth;
30302 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 + ')';
30303 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30304 if (borderWidth > 0) {
30305 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 + ')';
30306 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30307 }
30308 }
30309 if (data.bgActivePosistion && !r.hoverData.selecting) {
30310 var zoom = r.cy.zoom();
30311 var pos = data.bgActivePosistion;
30312 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 + ')';
30313 context.beginPath();
30314 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30315 context.fill();
30316 }
30317 var timeToRender = r.lastRedrawTime;
30318 if (r.showFps && timeToRender) {
30319 timeToRender = Math.round(timeToRender);
30320 var fps = Math.round(1000 / timeToRender);
30321 context.setTransform(1, 0, 0, 1, 0, 0);
30322 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30323 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30324 context.lineWidth = 1;
30325 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30326 var maxFps = 60;
30327 context.strokeRect(0, 30, 250, 20);
30328 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30329 }
30330 if (!drawAllLayers) {
30331 needDraw[r.SELECT_BOX] = false;
30332 }
30333 }
30334
30335 // motionblur: blit rendered blurry frames
30336 if (motionBlur && mbPxRatio !== 1) {
30337 var cxtNode = data.contexts[r.NODE];
30338 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30339 var cxtDrag = data.contexts[r.DRAG];
30340 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30341 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30342 cxt.setTransform(1, 0, 0, 1, 0, 0);
30343 if (needClear || !motionBlurFadeEffect) {
30344 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30345 } else {
30346 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30347 }
30348 var pxr = mbPxRatio;
30349 cxt.drawImage(txt,
30350 // img
30351 0, 0,
30352 // sx, sy
30353 r.canvasWidth * pxr, r.canvasHeight * pxr,
30354 // sw, sh
30355 0, 0,
30356 // x, y
30357 r.canvasWidth, r.canvasHeight // w, h
30358 );
30359 };
30360
30361 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30362 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30363 needDraw[r.NODE] = false;
30364 }
30365 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30366 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30367 needDraw[r.DRAG] = false;
30368 }
30369 }
30370 r.prevViewport = vp;
30371 if (r.clearingMotionBlur) {
30372 r.clearingMotionBlur = false;
30373 r.motionBlurCleared = true;
30374 r.motionBlur = true;
30375 }
30376 if (motionBlur) {
30377 r.motionBlurTimeout = setTimeout(function () {
30378 r.motionBlurTimeout = null;
30379 r.clearedForMotionBlur[r.NODE] = false;
30380 r.clearedForMotionBlur[r.DRAG] = false;
30381 r.motionBlur = false;
30382 r.clearingMotionBlur = !textureDraw;
30383 r.mbFrames = 0;
30384 needDraw[r.NODE] = true;
30385 needDraw[r.DRAG] = true;
30386 r.redraw();
30387 }, motionBlurDelay);
30388 }
30389 if (!forcedContext) {
30390 cy.emit('render');
30391 }
30392};
30393
30394var CRp$3 = {};
30395
30396// @O Polygon drawing
30397CRp$3.drawPolygonPath = function (context, x, y, width, height, points) {
30398 var halfW = width / 2;
30399 var halfH = height / 2;
30400 if (context.beginPath) {
30401 context.beginPath();
30402 }
30403 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30404 for (var i = 1; i < points.length / 2; i++) {
30405 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30406 }
30407 context.closePath();
30408};
30409CRp$3.drawRoundPolygonPath = function (context, x, y, width, height, points, corners) {
30410 corners.forEach(function (corner) {
30411 return drawPreparedRoundCorner(context, corner);
30412 });
30413 context.closePath();
30414};
30415
30416// Round rectangle drawing
30417CRp$3.drawRoundRectanglePath = function (context, x, y, width, height, radius) {
30418 var halfWidth = width / 2;
30419 var halfHeight = height / 2;
30420 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : Math.min(radius, halfHeight, halfWidth);
30421 if (context.beginPath) {
30422 context.beginPath();
30423 }
30424
30425 // Start at top middle
30426 context.moveTo(x, y - halfHeight);
30427 // Arc from middle top to right side
30428 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius);
30429 // Arc from right side to bottom
30430 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30431 // Arc from bottom to left side
30432 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30433 // Arc from left side to topBorder
30434 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius);
30435 // Join line
30436 context.lineTo(x, y - halfHeight);
30437 context.closePath();
30438};
30439CRp$3.drawBottomRoundRectanglePath = function (context, x, y, width, height, radius) {
30440 var halfWidth = width / 2;
30441 var halfHeight = height / 2;
30442 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : radius;
30443 if (context.beginPath) {
30444 context.beginPath();
30445 }
30446
30447 // Start at top middle
30448 context.moveTo(x, y - halfHeight);
30449 context.lineTo(x + halfWidth, y - halfHeight);
30450 context.lineTo(x + halfWidth, y);
30451 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30452 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30453 context.lineTo(x - halfWidth, y - halfHeight);
30454 context.lineTo(x, y - halfHeight);
30455 context.closePath();
30456};
30457CRp$3.drawCutRectanglePath = function (context, x, y, width, height, points, corners) {
30458 var halfWidth = width / 2;
30459 var halfHeight = height / 2;
30460 var cornerLength = corners === 'auto' ? getCutRectangleCornerLength() : corners;
30461 if (context.beginPath) {
30462 context.beginPath();
30463 }
30464 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30465 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30466 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30467 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30468 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30469 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30470 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30471 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30472 context.closePath();
30473};
30474CRp$3.drawBarrelPath = function (context, x, y, width, height) {
30475 var halfWidth = width / 2;
30476 var halfHeight = height / 2;
30477 var xBegin = x - halfWidth;
30478 var xEnd = x + halfWidth;
30479 var yBegin = y - halfHeight;
30480 var yEnd = y + halfHeight;
30481 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30482 var wOffset = barrelCurveConstants.widthOffset;
30483 var hOffset = barrelCurveConstants.heightOffset;
30484 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30485 if (context.beginPath) {
30486 context.beginPath();
30487 }
30488 context.moveTo(xBegin, yBegin + hOffset);
30489 context.lineTo(xBegin, yEnd - hOffset);
30490 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30491 context.lineTo(xEnd - wOffset, yEnd);
30492 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30493 context.lineTo(xEnd, yBegin + hOffset);
30494 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30495 context.lineTo(xBegin + wOffset, yBegin);
30496 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30497 context.closePath();
30498};
30499var sin0 = Math.sin(0);
30500var cos0 = Math.cos(0);
30501var sin = {};
30502var cos = {};
30503var ellipseStepSize = Math.PI / 40;
30504for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30505 sin[i] = Math.sin(i);
30506 cos[i] = Math.cos(i);
30507}
30508CRp$3.drawEllipsePath = function (context, centerX, centerY, width, height) {
30509 if (context.beginPath) {
30510 context.beginPath();
30511 }
30512 if (context.ellipse) {
30513 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30514 } else {
30515 var xPos, yPos;
30516 var rw = width / 2;
30517 var rh = height / 2;
30518 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30519 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30520 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30521 if (i === 0) {
30522 context.moveTo(xPos, yPos);
30523 } else {
30524 context.lineTo(xPos, yPos);
30525 }
30526 }
30527 }
30528 context.closePath();
30529};
30530
30531/* global atob, ArrayBuffer, Uint8Array, Blob */
30532var CRp$2 = {};
30533CRp$2.createBuffer = function (w, h) {
30534 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30535 buffer.width = w;
30536 buffer.height = h;
30537 return [buffer, buffer.getContext('2d')];
30538};
30539CRp$2.bufferCanvasImage = function (options) {
30540 var cy = this.cy;
30541 var eles = cy.mutableElements();
30542 var bb = eles.boundingBox();
30543 var ctrRect = this.findContainerClientCoords();
30544 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30545 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30546 var specdMaxDims = number$1(options.maxWidth) || number$1(options.maxHeight);
30547 var pxRatio = this.getPixelRatio();
30548 var scale = 1;
30549 if (options.scale !== undefined) {
30550 width *= options.scale;
30551 height *= options.scale;
30552 scale = options.scale;
30553 } else if (specdMaxDims) {
30554 var maxScaleW = Infinity;
30555 var maxScaleH = Infinity;
30556 if (number$1(options.maxWidth)) {
30557 maxScaleW = scale * options.maxWidth / width;
30558 }
30559 if (number$1(options.maxHeight)) {
30560 maxScaleH = scale * options.maxHeight / height;
30561 }
30562 scale = Math.min(maxScaleW, maxScaleH);
30563 width *= scale;
30564 height *= scale;
30565 }
30566 if (!specdMaxDims) {
30567 width *= pxRatio;
30568 height *= pxRatio;
30569 scale *= pxRatio;
30570 }
30571 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30572
30573 buffCanvas.width = width;
30574 buffCanvas.height = height;
30575 buffCanvas.style.width = width + 'px';
30576 buffCanvas.style.height = height + 'px';
30577 var buffCxt = buffCanvas.getContext('2d');
30578
30579 // Rasterize the layers, but only if container has nonzero size
30580 if (width > 0 && height > 0) {
30581 buffCxt.clearRect(0, 0, width, height);
30582 buffCxt.globalCompositeOperation = 'source-over';
30583 var zsortedEles = this.getCachedZSortedEles();
30584 if (options.full) {
30585 // draw the full bounds of the graph
30586 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30587 buffCxt.scale(scale, scale);
30588 this.drawElements(buffCxt, zsortedEles);
30589 buffCxt.scale(1 / scale, 1 / scale);
30590 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30591 } else {
30592 // draw the current view
30593 var pan = cy.pan();
30594 var translation = {
30595 x: pan.x * scale,
30596 y: pan.y * scale
30597 };
30598 scale *= cy.zoom();
30599 buffCxt.translate(translation.x, translation.y);
30600 buffCxt.scale(scale, scale);
30601 this.drawElements(buffCxt, zsortedEles);
30602 buffCxt.scale(1 / scale, 1 / scale);
30603 buffCxt.translate(-translation.x, -translation.y);
30604 }
30605
30606 // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30607 if (options.bg) {
30608 buffCxt.globalCompositeOperation = 'destination-over';
30609 buffCxt.fillStyle = options.bg;
30610 buffCxt.rect(0, 0, width, height);
30611 buffCxt.fill();
30612 }
30613 }
30614 return buffCanvas;
30615};
30616function b64ToBlob(b64, mimeType) {
30617 var bytes = atob(b64);
30618 var buff = new ArrayBuffer(bytes.length);
30619 var buffUint8 = new Uint8Array(buff);
30620 for (var i = 0; i < bytes.length; i++) {
30621 buffUint8[i] = bytes.charCodeAt(i);
30622 }
30623 return new Blob([buff], {
30624 type: mimeType
30625 });
30626}
30627function b64UriToB64(b64uri) {
30628 var i = b64uri.indexOf(',');
30629 return b64uri.substr(i + 1);
30630}
30631function output(options, canvas, mimeType) {
30632 var getB64Uri = function getB64Uri() {
30633 return canvas.toDataURL(mimeType, options.quality);
30634 };
30635 switch (options.output) {
30636 case 'blob-promise':
30637 return new Promise$1(function (resolve, reject) {
30638 try {
30639 canvas.toBlob(function (blob) {
30640 if (blob != null) {
30641 resolve(blob);
30642 } else {
30643 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30644 }
30645 }, mimeType, options.quality);
30646 } catch (err) {
30647 reject(err);
30648 }
30649 });
30650 case 'blob':
30651 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30652 case 'base64':
30653 return b64UriToB64(getB64Uri());
30654 case 'base64uri':
30655 default:
30656 return getB64Uri();
30657 }
30658}
30659CRp$2.png = function (options) {
30660 return output(options, this.bufferCanvasImage(options), 'image/png');
30661};
30662CRp$2.jpg = function (options) {
30663 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30664};
30665
30666var CRp$1 = {};
30667CRp$1.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points, corners) {
30668 switch (name) {
30669 case 'ellipse':
30670 return this.drawEllipsePath(context, centerX, centerY, width, height);
30671 case 'polygon':
30672 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30673 case 'round-polygon':
30674 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points, corners);
30675 case 'roundrectangle':
30676 case 'round-rectangle':
30677 return this.drawRoundRectanglePath(context, centerX, centerY, width, height, corners);
30678 case 'cutrectangle':
30679 case 'cut-rectangle':
30680 return this.drawCutRectanglePath(context, centerX, centerY, width, height, points, corners);
30681 case 'bottomroundrectangle':
30682 case 'bottom-round-rectangle':
30683 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height, corners);
30684 case 'barrel':
30685 return this.drawBarrelPath(context, centerX, centerY, width, height);
30686 }
30687};
30688
30689var CR = CanvasRenderer;
30690var CRp = CanvasRenderer.prototype;
30691CRp.CANVAS_LAYERS = 3;
30692//
30693CRp.SELECT_BOX = 0;
30694CRp.DRAG = 1;
30695CRp.NODE = 2;
30696CRp.BUFFER_COUNT = 3;
30697//
30698CRp.TEXTURE_BUFFER = 0;
30699CRp.MOTIONBLUR_BUFFER_NODE = 1;
30700CRp.MOTIONBLUR_BUFFER_DRAG = 2;
30701function CanvasRenderer(options) {
30702 var r = this;
30703 var containerWindow = r.cy.window();
30704 var document = containerWindow.document;
30705 r.data = {
30706 canvases: new Array(CRp.CANVAS_LAYERS),
30707 contexts: new Array(CRp.CANVAS_LAYERS),
30708 canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS),
30709 bufferCanvases: new Array(CRp.BUFFER_COUNT),
30710 bufferContexts: new Array(CRp.CANVAS_LAYERS)
30711 };
30712 var tapHlOffAttr = '-webkit-tap-highlight-color';
30713 var tapHlOffStyle = 'rgba(0,0,0,0)';
30714 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30715 var containerStyle = r.data.canvasContainer.style;
30716 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30717 containerStyle.position = 'relative';
30718 containerStyle.zIndex = '0';
30719 containerStyle.overflow = 'hidden';
30720 var container = options.cy.container();
30721 container.appendChild(r.data.canvasContainer);
30722 container.style[tapHlOffAttr] = tapHlOffStyle;
30723 var styleMap = {
30724 '-webkit-user-select': 'none',
30725 '-moz-user-select': '-moz-none',
30726 'user-select': 'none',
30727 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30728 'outline-style': 'none'
30729 };
30730 if (ms()) {
30731 styleMap['-ms-touch-action'] = 'none';
30732 styleMap['touch-action'] = 'none';
30733 }
30734 for (var i = 0; i < CRp.CANVAS_LAYERS; i++) {
30735 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30736 r.data.contexts[i] = canvas.getContext('2d');
30737 Object.keys(styleMap).forEach(function (k) {
30738 canvas.style[k] = styleMap[k];
30739 });
30740 canvas.style.position = 'absolute';
30741 canvas.setAttribute('data-id', 'layer' + i);
30742 canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i);
30743 r.data.canvasContainer.appendChild(canvas);
30744 r.data.canvasNeedsRedraw[i] = false;
30745 }
30746 r.data.topCanvas = r.data.canvases[0];
30747 r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node');
30748 r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox');
30749 r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag');
30750 for (var i = 0; i < CRp.BUFFER_COUNT; i++) {
30751 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30752 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30753 r.data.bufferCanvases[i].style.position = 'absolute';
30754 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30755 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30756 r.data.bufferCanvases[i].style.visibility = 'hidden';
30757 //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30758 }
30759
30760 r.pathsEnabled = true;
30761 var emptyBb = makeBoundingBox();
30762 var getBoxCenter = function getBoxCenter(bb) {
30763 return {
30764 x: (bb.x1 + bb.x2) / 2,
30765 y: (bb.y1 + bb.y2) / 2
30766 };
30767 };
30768 var getCenterOffset = function getCenterOffset(bb) {
30769 return {
30770 x: -bb.w / 2,
30771 y: -bb.h / 2
30772 };
30773 };
30774 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
30775 var _p = ele[0]._private;
30776 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
30777 return !same;
30778 };
30779 var getStyleKey = function getStyleKey(ele) {
30780 return ele[0]._private.nodeKey;
30781 };
30782 var getLabelKey = function getLabelKey(ele) {
30783 return ele[0]._private.labelStyleKey;
30784 };
30785 var getSourceLabelKey = function getSourceLabelKey(ele) {
30786 return ele[0]._private.sourceLabelStyleKey;
30787 };
30788 var getTargetLabelKey = function getTargetLabelKey(ele) {
30789 return ele[0]._private.targetLabelStyleKey;
30790 };
30791 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
30792 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
30793 };
30794 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30795 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
30796 };
30797 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30798 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
30799 };
30800 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30801 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
30802 };
30803 var getElementBox = function getElementBox(ele) {
30804 ele.boundingBox();
30805 return ele[0]._private.bodyBounds;
30806 };
30807 var getLabelBox = function getLabelBox(ele) {
30808 ele.boundingBox();
30809 return ele[0]._private.labelBounds.main || emptyBb;
30810 };
30811 var getSourceLabelBox = function getSourceLabelBox(ele) {
30812 ele.boundingBox();
30813 return ele[0]._private.labelBounds.source || emptyBb;
30814 };
30815 var getTargetLabelBox = function getTargetLabelBox(ele) {
30816 ele.boundingBox();
30817 return ele[0]._private.labelBounds.target || emptyBb;
30818 };
30819 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
30820 return scaledLabelShown;
30821 };
30822 var getElementRotationPoint = function getElementRotationPoint(ele) {
30823 return getBoxCenter(getElementBox(ele));
30824 };
30825 var addTextMargin = function addTextMargin(prefix, pt, ele) {
30826 var pre = prefix ? prefix + '-' : '';
30827 return {
30828 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
30829 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
30830 };
30831 };
30832 var getRsPt = function getRsPt(ele, x, y) {
30833 var rs = ele[0]._private.rscratch;
30834 return {
30835 x: rs[x],
30836 y: rs[y]
30837 };
30838 };
30839 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
30840 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
30841 };
30842 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
30843 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
30844 };
30845 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
30846 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
30847 };
30848 var getElementRotationOffset = function getElementRotationOffset(ele) {
30849 return getCenterOffset(getElementBox(ele));
30850 };
30851 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
30852 return getCenterOffset(getSourceLabelBox(ele));
30853 };
30854 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
30855 return getCenterOffset(getTargetLabelBox(ele));
30856 };
30857 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
30858 var bb = getLabelBox(ele);
30859 var p = getCenterOffset(getLabelBox(ele));
30860 if (ele.isNode()) {
30861 switch (ele.pstyle('text-halign').value) {
30862 case 'left':
30863 p.x = -bb.w;
30864 break;
30865 case 'right':
30866 p.x = 0;
30867 break;
30868 }
30869 switch (ele.pstyle('text-valign').value) {
30870 case 'top':
30871 p.y = -bb.h;
30872 break;
30873 case 'bottom':
30874 p.y = 0;
30875 break;
30876 }
30877 }
30878 return p;
30879 };
30880 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
30881 getKey: getStyleKey,
30882 doesEleInvalidateKey: backgroundTimestampHasChanged,
30883 drawElement: drawElement,
30884 getBoundingBox: getElementBox,
30885 getRotationPoint: getElementRotationPoint,
30886 getRotationOffset: getElementRotationOffset,
30887 allowEdgeTxrCaching: false,
30888 allowParentTxrCaching: false
30889 });
30890 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
30891 getKey: getLabelKey,
30892 drawElement: drawLabel,
30893 getBoundingBox: getLabelBox,
30894 getRotationPoint: getLabelRotationPoint,
30895 getRotationOffset: getLabelRotationOffset,
30896 isVisible: isLabelVisibleAtScale
30897 });
30898 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
30899 getKey: getSourceLabelKey,
30900 drawElement: drawSourceLabel,
30901 getBoundingBox: getSourceLabelBox,
30902 getRotationPoint: getSourceLabelRotationPoint,
30903 getRotationOffset: getSourceLabelRotationOffset,
30904 isVisible: isLabelVisibleAtScale
30905 });
30906 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
30907 getKey: getTargetLabelKey,
30908 drawElement: drawTargetLabel,
30909 getBoundingBox: getTargetLabelBox,
30910 getRotationPoint: getTargetLabelRotationPoint,
30911 getRotationOffset: getTargetLabelRotationOffset,
30912 isVisible: isLabelVisibleAtScale
30913 });
30914 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
30915 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
30916 // each cache should check for sub-key diff to see that the update affects that cache particularly
30917 eleTxrCache.invalidateElements(eles);
30918 lblTxrCache.invalidateElements(eles);
30919 slbTxrCache.invalidateElements(eles);
30920 tlbTxrCache.invalidateElements(eles);
30921
30922 // any change invalidates the layers
30923 lyrTxrCache.invalidateElements(eles);
30924
30925 // update the old bg timestamp so diffs can be done in the ele txr caches
30926 for (var _i = 0; _i < eles.length; _i++) {
30927 var _p = eles[_i]._private;
30928 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
30929 }
30930 });
30931 var refineInLayers = function refineInLayers(reqs) {
30932 for (var i = 0; i < reqs.length; i++) {
30933 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
30934 }
30935 };
30936 eleTxrCache.onDequeue(refineInLayers);
30937 lblTxrCache.onDequeue(refineInLayers);
30938 slbTxrCache.onDequeue(refineInLayers);
30939 tlbTxrCache.onDequeue(refineInLayers);
30940}
30941CRp.redrawHint = function (group, bool) {
30942 var r = this;
30943 switch (group) {
30944 case 'eles':
30945 r.data.canvasNeedsRedraw[CRp.NODE] = bool;
30946 break;
30947 case 'drag':
30948 r.data.canvasNeedsRedraw[CRp.DRAG] = bool;
30949 break;
30950 case 'select':
30951 r.data.canvasNeedsRedraw[CRp.SELECT_BOX] = bool;
30952 break;
30953 }
30954};
30955
30956// whether to use Path2D caching for drawing
30957var pathsImpld = typeof Path2D !== 'undefined';
30958CRp.path2dEnabled = function (on) {
30959 if (on === undefined) {
30960 return this.pathsEnabled;
30961 }
30962 this.pathsEnabled = on ? true : false;
30963};
30964CRp.usePaths = function () {
30965 return pathsImpld && this.pathsEnabled;
30966};
30967CRp.setImgSmoothing = function (context, bool) {
30968 if (context.imageSmoothingEnabled != null) {
30969 context.imageSmoothingEnabled = bool;
30970 } else {
30971 context.webkitImageSmoothingEnabled = bool;
30972 context.mozImageSmoothingEnabled = bool;
30973 context.msImageSmoothingEnabled = bool;
30974 }
30975};
30976CRp.getImgSmoothing = function (context) {
30977 if (context.imageSmoothingEnabled != null) {
30978 return context.imageSmoothingEnabled;
30979 } else {
30980 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
30981 }
30982};
30983CRp.makeOffscreenCanvas = function (width, height) {
30984 var canvas;
30985 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ("undefined" )) {
30986 canvas = new OffscreenCanvas(width, height);
30987 } else {
30988 var containerWindow = this.cy.window();
30989 var document = containerWindow.document;
30990 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
30991 canvas.width = width;
30992 canvas.height = height;
30993 }
30994 return canvas;
30995};
30996[CRp$a, CRp$9, CRp$8, CRp$7, CRp$6, CRp$5, CRp$4, CRp$3, CRp$2, CRp$1].forEach(function (props) {
30997 extend(CRp, props);
30998});
30999
31000var renderer = [{
31001 name: 'null',
31002 impl: NullRenderer
31003}, {
31004 name: 'base',
31005 impl: BR
31006}, {
31007 name: 'canvas',
31008 impl: CR
31009}];
31010
31011var incExts = [{
31012 type: 'layout',
31013 extensions: layout
31014}, {
31015 type: 'renderer',
31016 extensions: renderer
31017}];
31018
31019// registered extensions to cytoscape, indexed by name
31020var extensions = {};
31021
31022// registered modules for extensions, indexed by name
31023var modules = {};
31024function setExtension(type, name, registrant) {
31025 var ext = registrant;
31026 var overrideErr = function overrideErr(field) {
31027 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31028 };
31029 if (type === 'core') {
31030 if (Core.prototype[name]) {
31031 return overrideErr(name);
31032 } else {
31033 Core.prototype[name] = registrant;
31034 }
31035 } else if (type === 'collection') {
31036 if (Collection.prototype[name]) {
31037 return overrideErr(name);
31038 } else {
31039 Collection.prototype[name] = registrant;
31040 }
31041 } else if (type === 'layout') {
31042 // fill in missing layout functions in the prototype
31043
31044 var Layout = function Layout(options) {
31045 this.options = options;
31046 registrant.call(this, options);
31047
31048 // make sure layout has _private for use w/ std apis like .on()
31049 if (!plainObject(this._private)) {
31050 this._private = {};
31051 }
31052 this._private.cy = options.cy;
31053 this._private.listeners = [];
31054 this.createEmitter();
31055 };
31056 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31057 var optLayoutFns = [];
31058 for (var i = 0; i < optLayoutFns.length; i++) {
31059 var fnName = optLayoutFns[i];
31060 layoutProto[fnName] = layoutProto[fnName] || function () {
31061 return this;
31062 };
31063 }
31064
31065 // either .start() or .run() is defined, so autogen the other
31066 if (layoutProto.start && !layoutProto.run) {
31067 layoutProto.run = function () {
31068 this.start();
31069 return this;
31070 };
31071 } else if (!layoutProto.start && layoutProto.run) {
31072 layoutProto.start = function () {
31073 this.run();
31074 return this;
31075 };
31076 }
31077 var regStop = registrant.prototype.stop;
31078 layoutProto.stop = function () {
31079 var opts = this.options;
31080 if (opts && opts.animate) {
31081 var anis = this.animations;
31082 if (anis) {
31083 for (var _i = 0; _i < anis.length; _i++) {
31084 anis[_i].stop();
31085 }
31086 }
31087 }
31088 if (regStop) {
31089 regStop.call(this);
31090 } else {
31091 this.emit('layoutstop');
31092 }
31093 return this;
31094 };
31095 if (!layoutProto.destroy) {
31096 layoutProto.destroy = function () {
31097 return this;
31098 };
31099 }
31100 layoutProto.cy = function () {
31101 return this._private.cy;
31102 };
31103 var getCy = function getCy(layout) {
31104 return layout._private.cy;
31105 };
31106 var emitterOpts = {
31107 addEventFields: function addEventFields(layout, evt) {
31108 evt.layout = layout;
31109 evt.cy = getCy(layout);
31110 evt.target = layout;
31111 },
31112 bubble: function bubble() {
31113 return true;
31114 },
31115 parent: function parent(layout) {
31116 return getCy(layout);
31117 }
31118 };
31119 extend(layoutProto, {
31120 createEmitter: function createEmitter() {
31121 this._private.emitter = new Emitter(emitterOpts, this);
31122 return this;
31123 },
31124 emitter: function emitter() {
31125 return this._private.emitter;
31126 },
31127 on: function on(evt, cb) {
31128 this.emitter().on(evt, cb);
31129 return this;
31130 },
31131 one: function one(evt, cb) {
31132 this.emitter().one(evt, cb);
31133 return this;
31134 },
31135 once: function once(evt, cb) {
31136 this.emitter().one(evt, cb);
31137 return this;
31138 },
31139 removeListener: function removeListener(evt, cb) {
31140 this.emitter().removeListener(evt, cb);
31141 return this;
31142 },
31143 removeAllListeners: function removeAllListeners() {
31144 this.emitter().removeAllListeners();
31145 return this;
31146 },
31147 emit: function emit(evt, params) {
31148 this.emitter().emit(evt, params);
31149 return this;
31150 }
31151 });
31152 define.eventAliasesOn(layoutProto);
31153 ext = Layout; // replace with our wrapped layout
31154 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31155 // user registered renderers inherit from base
31156
31157 var BaseRenderer = getExtension('renderer', 'base');
31158 var bProto = BaseRenderer.prototype;
31159 var RegistrantRenderer = registrant;
31160 var rProto = registrant.prototype;
31161 var Renderer = function Renderer() {
31162 BaseRenderer.apply(this, arguments);
31163 RegistrantRenderer.apply(this, arguments);
31164 };
31165 var proto = Renderer.prototype;
31166 for (var pName in bProto) {
31167 var pVal = bProto[pName];
31168 var existsInR = rProto[pName] != null;
31169 if (existsInR) {
31170 return overrideErr(pName);
31171 }
31172 proto[pName] = pVal; // take impl from base
31173 }
31174
31175 for (var _pName in rProto) {
31176 proto[_pName] = rProto[_pName]; // take impl from registrant
31177 }
31178
31179 bProto.clientFunctions.forEach(function (name) {
31180 proto[name] = proto[name] || function () {
31181 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31182 };
31183 });
31184 ext = Renderer;
31185 } else if (type === '__proto__' || type === 'constructor' || type === 'prototype') {
31186 // to avoid potential prototype pollution
31187 return error(type + ' is an illegal type to be registered, possibly lead to prototype pollutions');
31188 }
31189 return setMap({
31190 map: extensions,
31191 keys: [type, name],
31192 value: ext
31193 });
31194}
31195function getExtension(type, name) {
31196 return getMap({
31197 map: extensions,
31198 keys: [type, name]
31199 });
31200}
31201function setModule(type, name, moduleType, moduleName, registrant) {
31202 return setMap({
31203 map: modules,
31204 keys: [type, name, moduleType, moduleName],
31205 value: registrant
31206 });
31207}
31208function getModule(type, name, moduleType, moduleName) {
31209 return getMap({
31210 map: modules,
31211 keys: [type, name, moduleType, moduleName]
31212 });
31213}
31214var extension = function extension() {
31215 // e.g. extension('renderer', 'svg')
31216 if (arguments.length === 2) {
31217 return getExtension.apply(null, arguments);
31218 }
31219
31220 // e.g. extension('renderer', 'svg', { ... })
31221 else if (arguments.length === 3) {
31222 return setExtension.apply(null, arguments);
31223 }
31224
31225 // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31226 else if (arguments.length === 4) {
31227 return getModule.apply(null, arguments);
31228 }
31229
31230 // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31231 else if (arguments.length === 5) {
31232 return setModule.apply(null, arguments);
31233 } else {
31234 error('Invalid extension access syntax');
31235 }
31236};
31237
31238// allows a core instance to access extensions internally
31239Core.prototype.extension = extension;
31240
31241// included extensions
31242incExts.forEach(function (group) {
31243 group.extensions.forEach(function (ext) {
31244 setExtension(group.type, ext.name, ext.impl);
31245 });
31246});
31247
31248// a dummy stylesheet object that doesn't need a reference to the core
31249// (useful for init)
31250var Stylesheet = function Stylesheet() {
31251 if (!(this instanceof Stylesheet)) {
31252 return new Stylesheet();
31253 }
31254 this.length = 0;
31255};
31256var sheetfn = Stylesheet.prototype;
31257sheetfn.instanceString = function () {
31258 return 'stylesheet';
31259};
31260
31261// just store the selector to be parsed later
31262sheetfn.selector = function (selector) {
31263 var i = this.length++;
31264 this[i] = {
31265 selector: selector,
31266 properties: []
31267 };
31268 return this; // chaining
31269};
31270
31271// just store the property to be parsed later
31272sheetfn.css = function (name, value) {
31273 var i = this.length - 1;
31274 if (string(name)) {
31275 this[i].properties.push({
31276 name: name,
31277 value: value
31278 });
31279 } else if (plainObject(name)) {
31280 var map = name;
31281 var propNames = Object.keys(map);
31282 for (var j = 0; j < propNames.length; j++) {
31283 var key = propNames[j];
31284 var mapVal = map[key];
31285 if (mapVal == null) {
31286 continue;
31287 }
31288 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31289 if (prop == null) {
31290 continue;
31291 }
31292 var _name = prop.name;
31293 var _value = mapVal;
31294 this[i].properties.push({
31295 name: _name,
31296 value: _value
31297 });
31298 }
31299 }
31300 return this; // chaining
31301};
31302
31303sheetfn.style = sheetfn.css;
31304
31305// generate a real style object from the dummy stylesheet
31306sheetfn.generateStyle = function (cy) {
31307 var style = new Style(cy);
31308 return this.appendToStyle(style);
31309};
31310
31311// append a dummy stylesheet object on a real style object
31312sheetfn.appendToStyle = function (style) {
31313 for (var i = 0; i < this.length; i++) {
31314 var context = this[i];
31315 var selector = context.selector;
31316 var props = context.properties;
31317 style.selector(selector); // apply selector
31318
31319 for (var j = 0; j < props.length; j++) {
31320 var prop = props[j];
31321 style.css(prop.name, prop.value); // apply property
31322 }
31323 }
31324
31325 return style;
31326};
31327
31328var version = "3.30.2";
31329
31330var cytoscape = function cytoscape(options) {
31331 // if no options specified, use default
31332 if (options === undefined) {
31333 options = {};
31334 }
31335
31336 // create instance
31337 if (plainObject(options)) {
31338 return new Core(options);
31339 }
31340
31341 // allow for registration of extensions
31342 else if (string(options)) {
31343 return extension.apply(extension, arguments);
31344 }
31345};
31346
31347// e.g. cytoscape.use( require('cytoscape-foo'), bar )
31348cytoscape.use = function (ext) {
31349 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31350
31351 args.unshift(cytoscape); // cytoscape is first arg to ext
31352
31353 ext.apply(null, args);
31354 return this;
31355};
31356cytoscape.warnings = function (bool) {
31357 return warnings(bool);
31358};
31359
31360// replaced by build system
31361cytoscape.version = version;
31362
31363// expose public apis (mostly for extensions)
31364cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31365
31366module.exports = cytoscape;